Skip to content

Commit

Permalink
pending block number transformed into tag (kkrt-labs#850)
Browse files Browse the repository at this point in the history
* pending block number transformed into tag

* refactor logic

* add test for new behaviour

* fix test db

* update test
  • Loading branch information
Eikix authored Mar 12, 2024
1 parent f1713ba commit e5c860e
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 29 deletions.
59 changes: 31 additions & 28 deletions src/eth_provider/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use eyre::Result;
use itertools::Itertools;
use mongodb::bson::doc;
use reth_primitives::constants::EMPTY_ROOT_HASH;
use reth_primitives::revm_primitives::FixedBytes;
use reth_primitives::Address;
use reth_primitives::BlockId;
use reth_primitives::Bytes;
Expand All @@ -19,7 +20,6 @@ use reth_rpc_types::Filter;
use reth_rpc_types::FilterChanges;
use reth_rpc_types::Index;
use reth_rpc_types::JsonStorageKey;
use reth_rpc_types::RpcBlockHash;
use reth_rpc_types::TransactionReceipt;
use reth_rpc_types::TransactionRequest;
use reth_rpc_types::ValueOrArray;
Expand Down Expand Up @@ -55,6 +55,7 @@ use crate::eth_provider::utils::format_hex;
use crate::into_via_try_wrapper;
use crate::into_via_wrapper;
use crate::models::block::EthBlockId;
use crate::models::block::EthBlockNumberOrTag;
use crate::models::errors::ConversionError;
use crate::models::felt::Felt252Wrapper;

Expand Down Expand Up @@ -154,12 +155,10 @@ where
None => U64::from(self.starknet_provider.block_number().await?), // in case the database is empty, use the starknet provider
Some(header) => {
let number = header.header.number.ok_or(EthProviderError::ValueNotFound("Block".to_string()))?;
let n = number.as_le_bytes_trimmed();
// Block number is U64
if n.len() > 8 {
return Err(ConversionError::ValueOutOfRange("Block number too large".to_string()).into());
}
U64::from_le_slice(n.as_ref())
let number: u64 = number
.try_into()
.map_err(|_| ConversionError::ValueOutOfRange("Block number too large".to_string()))?;
U64::from(number)
}
};
Ok(block_number)
Expand Down Expand Up @@ -716,17 +715,35 @@ where
}

/// Convert the given block id into a Starknet block id
async fn to_starknet_block_id(
pub async fn to_starknet_block_id(
&self,
block_id: impl Into<Option<BlockId>>,
) -> EthProviderResult<starknet::core::types::BlockId> {
match block_id.into() {
Some(BlockId::Hash(hash)) => {
Ok(starknet::core::types::BlockId::Number(self.hash_into_block_number(hash).await?.to::<u64>()))
}
Some(id) => {
let eth_block_id = EthBlockId::new(id);
Ok(eth_block_id.try_into()?)
Some(BlockId::Hash(hash)) => Ok(EthBlockId::new(BlockId::Hash(hash)).try_into()?),
Some(BlockId::Number(number_or_tag)) => {
// There is a need to separate the BlockNumberOrTag case into three subcases
// because pending Starknet blocks don't have a number.
// 1. The block number corresponds to a Starknet pending block, then we return the pending tag
// 2. The block number corresponds to a Starknet sealed block, then we return the block number
// 3. The block number is not found, then we return an error
match number_or_tag {
BlockNumberOrTag::Number(number) => {
let header = self
.header(BlockHashOrNumber::Number(number))
.await?
.ok_or(EthProviderError::ValueNotFound("Block".to_string()))?;
// If the block hash is zero, then the block corresponds to a Starknet pending block
if header.header.hash.ok_or(EthProviderError::ValueNotFound("Block".to_string()))?
== FixedBytes::ZERO
{
Ok(starknet::core::types::BlockId::Tag(starknet::core::types::BlockTag::Pending))
} else {
Ok(starknet::core::types::BlockId::Number(number))
}
}
_ => Ok(EthBlockNumberOrTag::from(number_or_tag).into()),
}
}
None => Ok(starknet::core::types::BlockId::Tag(starknet::core::types::BlockTag::Pending)),
}
Expand All @@ -743,18 +760,4 @@ where
| BlockNumberOrTag::Pending => self.block_number().await,
}
}

/// Convert the given block hash into a block number
async fn hash_into_block_number(&self, hash: RpcBlockHash) -> EthProviderResult<U64> {
let filter = into_filter("header.hash", hash.block_hash, 64);
let header: Option<StoredHeader> = self.database.get_one("headers", filter, None).await?;
let header = match header {
Some(header) => header,
None => return Err(EthProviderError::ValueNotFound("Block".to_string())),
};
let number = header.header.number.ok_or(EthProviderError::ValueNotFound("Block".to_string()))?;
let number: u64 =
number.try_into().map_err(|_| ConversionError::ValueOutOfRange("Block number too large".to_string()))?;
Ok(U64::from(number))
}
}
4 changes: 4 additions & 0 deletions src/test_utils/mongo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub async fn mock_database() -> Database {
"withdrawalsRoot": &empty_root_hash,
}},
doc! {"header": doc! {
"hash": format!("0x{:064x}", *BLOCK_HASH),
"parentHash": &hash_256_zero,
"sha3Uncles": &hash_256_zero,
"miner": &address_zero,
Expand All @@ -92,6 +93,7 @@ pub async fn mock_database() -> Database {
"withdrawalsRoot": &empty_root_hash,
}},
doc! {"header": doc! {
"hash": format!("0x{:064x}", *BLOCK_HASH),
"parentHash": &hash_256_zero,
"sha3Uncles": &hash_256_zero,
"miner": &address_zero,
Expand All @@ -110,6 +112,7 @@ pub async fn mock_database() -> Database {
"withdrawalsRoot": &empty_root_hash,
}},
doc! {"header": doc! {
"hash": format!("0x{:064x}", *BLOCK_HASH),
"parentHash": &hash_256_zero,
"sha3Uncles": &hash_256_zero,
"miner": &address_zero,
Expand All @@ -128,6 +131,7 @@ pub async fn mock_database() -> Database {
"withdrawalsRoot": &empty_root_hash,
}},
doc! {"header": doc! {
"hash": &hash_256_zero,
"parentHash": &hash_256_zero,
"sha3Uncles": &hash_256_zero,
"miner": &address_zero,
Expand Down
31 changes: 30 additions & 1 deletion tests/eth_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use reth_rpc_types::{JsonStorageKey, RpcBlockHash, TransactionRequest};
use rstest::*;

use reth_primitives::{Address, BlockNumberOrTag, Bytes, B256, U256, U64};
use starknet::core::types::BlockTag;
use starknet_crypto::FieldElement;

#[rstest]
#[awt]
Expand Down Expand Up @@ -354,8 +356,35 @@ async fn test_block_receipts(#[future] katana: Katana, _setup: ()) {
assert_eq!(receipt.block_number.unwrap(), U256::from(*BLOCK_NUMBER));

let receipts = eth_provider
.block_receipts(Some(reth_rpc_types::BlockId::Hash(RpcBlockHash::from(B256::default()))))
.block_receipts(Some(reth_rpc_types::BlockId::Hash(RpcBlockHash::from(B256::from(U256::from(0xc0fefe))))))
.await
.unwrap();
assert!(receipts.is_none());
}

#[rstest]
#[awt]
#[tokio::test(flavor = "multi_thread")]
async fn test_to_starknet_block_id(#[future] katana: Katana, _setup: ()) {
// Given
let eth_provider = katana.eth_provider();

// When
let block_id = reth_rpc_types::BlockId::Number(BlockNumberOrTag::Number(*BLOCK_NUMBER));
let pending_starknet_block_id = eth_provider.to_starknet_block_id(block_id).await.unwrap();

let some_block_hash = reth_rpc_types::BlockId::Hash(RpcBlockHash::from(*BLOCK_HASH));
let some_starknet_block_hash = eth_provider.to_starknet_block_id(some_block_hash).await.unwrap();

let some_block_number = reth_rpc_types::BlockId::Number(BlockNumberOrTag::Number(0));
let some_starknet_block_number = eth_provider.to_starknet_block_id(some_block_number).await.unwrap();

let unknown_block_number = reth_rpc_types::BlockId::Number(BlockNumberOrTag::Number(u64::MAX));
let unknown_starknet_block_number = eth_provider.to_starknet_block_id(unknown_block_number).await;

// Then
assert_eq!(pending_starknet_block_id, starknet::core::types::BlockId::Tag(BlockTag::Pending));
assert_eq!(some_starknet_block_hash, starknet::core::types::BlockId::Hash(FieldElement::from(0x1234_u64)));
assert_eq!(some_starknet_block_number, starknet::core::types::BlockId::Number(0_u64));
assert!(unknown_starknet_block_number.is_err());
}

0 comments on commit e5c860e

Please sign in to comment.