diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md index b0d6d121..632c79bd 100644 --- a/SUPPORTED_APIS.md +++ b/SUPPORTED_APIS.md @@ -47,9 +47,9 @@ The `status` options are: | [`ETH`](#eth-namespace) | [`eth_getFilterLogs`](#eth_getfilterlogs) | `SUPPORTED` | Returns an array of all logs matching filter with given id | | [`ETH`](#eth-namespace) | [`eth_getLogs`](#eth_getlogs) | `SUPPORTED` | Returns an array of all logs matching a given filter object | | `ETH` | `eth_getProof` | `NOT IMPLEMENTED` | Returns the details for the account at the specified address and block number, the account's Merkle proof, and the storage values for the specified storage keys with their Merkle-proofs | -| [`ETH`](#eth-namespace) | `eth_getStorageAt`(#`eth_getstorageat) | `SUPPORTED` | Returns the value from a storage position at a given address | -| `ETH` | `eth_getTransactionByBlockHashAndIndex` | `NOT IMPLEMENTED`
[GitHub Issue #46](https://github.com/matter-labs/era-test-node/issues/46) | Returns information about a transaction by block hash and transaction index position | -| `ETH` | `eth_getTransactionByBlockNumberAndIndex` | `NOT IMPLEMENTED`
[GitHub Issue #47](https://github.com/matter-labs/era-test-node/issues/47) | Returns information about a transaction by block number and transaction index position | +| [`ETH`](#eth-namespace) | [`eth_getStorageAt`](#eth_getstorageat) | `SUPPORTED` | Returns the value from a storage position at a given address | +| [`ETH`](#eth-namespace) | [`eth_getTransactionByBlockHashAndIndex`](#eth_gettransactionbyblockhashandindex) | `SUPPORTED` | Returns information about a transaction by block hash and transaction index position | +| [`ETH`](#eth-namespace) | [`eth_getTransactionByBlockNumberAndIndex`](#eth_gettransactionbyblocknumberandindex) | `SUPPORTED` | Returns information about a transaction by block number and transaction index position | | [`ETH`](#eth-namespace) | [`eth_getTransactionReceipt`](#eth_gettransactionreceipt) | `SUPPORTED` | Returns the receipt of a transaction by transaction hash | | `ETH` | `eth_getUncleByBlockHashAndIndex` | `NOT IMPLEMENTED` | Returns information about a uncle of a block by hash and uncle index position | | `ETH` | `eth_getUncleByBlockNumberAndIndex` | `NOT IMPLEMENTED` | Returns information about a uncle of a block by hash and uncle index position | @@ -784,7 +784,6 @@ curl --request POST \ }' ``` - ### `eth_getFilterLogs` [source](src/node.rs) @@ -1101,6 +1100,65 @@ curl --request POST \ }' ``` +### `eth_getTransactionByBlockHashAndIndex` + +[source](src/node.rs) + +Returns information about a transaction by block hash and transaction index position + +#### Arguments + ++ `block_hash: H256` ++ `index: U64` + +#### Status + +`SUPPORTED` + +#### Example + +```bash +curl --request POST \ + --url http://localhost:8011/ \ + --header 'content-type: application/json' \ + --data '{ + "jsonrpc": "2.0", + "id": "1", + "method": "eth_getTransactionByBlockHashAndIndex", + "params": ["0x0000000000000000000000000000000000000000000000000000000000000008", "0x1"] +}' +``` + +### `eth_getTransactionByBlockNumberAndIndex` + +[source](src/node.rs) + +Returns information about a transaction by block number and transaction index position + +#### Arguments + ++ `block_number: BlockNumber` ++ `index: U64` + +#### Status + +`SUPPORTED` + +#### Example + +```bash +curl --request POST \ + --url http://localhost:8011/ \ + --header 'content-type: application/json' \ + --data '{ + "jsonrpc": "2.0", + "id": "1", + "method": "eth_getTransactionByBlockNumberAndIndex", + "params": ["latest", "0x1"] +}' +``` + + ## `HARDHAT NAMESPACE` ### `hardhat_setBalance` diff --git a/src/fork.rs b/src/fork.rs index 727dc05d..3e7f7932 100644 --- a/src/fork.rs +++ b/src/fork.rs @@ -22,7 +22,9 @@ use zksync_types::{ use zksync_state::ReadStorage; use zksync_utils::{bytecode::hash_bytecode, h256_to_u256}; -use zksync_web3_decl::{jsonrpsee::http_client::HttpClient, namespaces::EthNamespaceClient}; +use zksync_web3_decl::{ + jsonrpsee::http_client::HttpClient, namespaces::EthNamespaceClient, types::Index, +}; use zksync_web3_decl::{jsonrpsee::http_client::HttpClientBuilder, namespaces::ZksNamespaceClient}; use crate::{cache::CacheConfig, node::TEST_NODE_NETWORK_ID}; @@ -231,6 +233,20 @@ pub trait ForkSource { &self, block_number: zksync_types::api::BlockNumber, ) -> eyre::Result>; + + /// Returns information about a transaction by block hash and transaction index position. + fn get_transaction_by_block_hash_and_index( + &self, + block_hash: H256, + index: Index, + ) -> eyre::Result>; + + /// Returns information about a transaction by block number and transaction index position. + fn get_transaction_by_block_number_and_index( + &self, + block_number: BlockNumber, + index: Index, + ) -> eyre::Result>; } /// Holds the information about the original chain. diff --git a/src/http_fork_source.rs b/src/http_fork_source.rs index fa0221ce..ac2b3cad 100644 --- a/src/http_fork_source.rs +++ b/src/http_fork_source.rs @@ -2,9 +2,11 @@ use std::sync::RwLock; use eyre::Context; use zksync_basic_types::{H256, U256}; +use zksync_types::api::Transaction; use zksync_web3_decl::{ jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, namespaces::{EthNamespaceClient, ZksNamespaceClient}, + types::Index, }; use crate::{ @@ -220,6 +222,36 @@ impl ForkSource for HttpForkSource { }) .wrap_err("fork http client failed") } + + /// Returns information about a transaction by block hash and transaction index position. + fn get_transaction_by_block_hash_and_index( + &self, + block_hash: H256, + index: Index, + ) -> eyre::Result> { + let client = self.create_client(); + block_on(async move { + client + .get_transaction_by_block_hash_and_index(block_hash, index) + .await + }) + .wrap_err("fork http client failed") + } + + /// Returns information about a transaction by block number and transaction index position. + fn get_transaction_by_block_number_and_index( + &self, + block_number: zksync_types::api::BlockNumber, + index: Index, + ) -> eyre::Result> { + let client = self.create_client(); + block_on(async move { + client + .get_transaction_by_block_number_and_index(block_number, index) + .await + }) + .wrap_err("fork http client failed") + } } #[cfg(test)] diff --git a/src/node.rs b/src/node.rs index 2e970b79..ae69757c 100644 --- a/src/node.rs +++ b/src/node.rs @@ -2601,20 +2601,137 @@ impl EthNamespaceT for }) } + /// Returns information about a transaction by block hash and transaction index position. + /// + /// # Arguments + /// + /// * `block_hash`: Hash of a block + /// * `index`: Integer of the transaction index position + /// + /// # Returns + /// + /// A `BoxFuture` containing a `jsonrpc_core::Result` that maybe resolves to a [zksync_types::api::Transaction], if found. fn get_transaction_by_block_hash_and_index( &self, - _block_hash: zksync_basic_types::H256, - _index: zksync_basic_types::web3::types::Index, + block_hash: zksync_basic_types::H256, + index: zksync_basic_types::web3::types::Index, ) -> jsonrpc_core::BoxFuture>> { - not_implemented("get_transaction_by_block_hash_and_index") + let inner = Arc::clone(&self.inner); + + Box::pin(async move { + let reader = match inner.read() { + Ok(r) => r, + Err(_) => { + return Err(into_jsrpc_error(Web3Error::InternalError)); + } + }; + + let maybe_tx = reader + .blocks + .get(&block_hash) + .and_then(|block| block.transactions.get(index.as_usize())) + .and_then(|tx| match tx { + TransactionVariant::Full(tx) => Some(tx.clone()), + TransactionVariant::Hash(tx_hash) => reader + .fork_storage + .inner + .read() + .expect("failed reading fork storage") + .fork + .as_ref() + .and_then(|fork| { + fork.fork_source + .get_transaction_by_hash(*tx_hash) + .ok() + .flatten() + }), + }) + .or_else(|| { + reader + .fork_storage + .inner + .read() + .expect("failed reading fork storage") + .fork + .as_ref() + .and_then(|fork| { + fork.fork_source + .get_transaction_by_block_hash_and_index(block_hash, index) + .ok() + }) + .flatten() + }); + + Ok(maybe_tx) + }) } + /// Returns information about a transaction by block number and transaction index position. + /// + /// # Arguments + /// + /// * `block_number`: A block number, or the string "earliest", "latest" or "pending". + /// * `index`: Integer of the transaction index position + /// + /// # Returns + /// + /// A `BoxFuture` containing a `jsonrpc_core::Result` that maybe resolves to a [zksync_types::api::Transaction], if found. fn get_transaction_by_block_number_and_index( &self, - _block_number: zksync_types::api::BlockNumber, - _index: zksync_basic_types::web3::types::Index, + block_number: zksync_types::api::BlockNumber, + index: zksync_basic_types::web3::types::Index, ) -> jsonrpc_core::BoxFuture>> { - not_implemented("get_transaction_by_block_number_and_index") + let inner = Arc::clone(&self.inner); + + Box::pin(async move { + let reader = match inner.read() { + Ok(r) => r, + Err(_) => { + return Err(into_jsrpc_error(Web3Error::InternalError)); + } + }; + + let real_block_number = + utils::to_real_block_number(block_number, U64::from(reader.current_miniblock)); + let maybe_tx = reader + .block_hashes + .get(&real_block_number.as_u64()) + .and_then(|block_hash| reader.blocks.get(block_hash)) + .and_then(|block| block.transactions.get(index.as_usize())) + .and_then(|tx| match tx { + TransactionVariant::Full(tx) => Some(tx.clone()), + TransactionVariant::Hash(tx_hash) => reader + .fork_storage + .inner + .read() + .expect("failed reading fork storage") + .fork + .as_ref() + .and_then(|fork| { + fork.fork_source + .get_transaction_by_hash(*tx_hash) + .ok() + .flatten() + }), + }) + .or_else(|| { + reader + .fork_storage + .inner + .read() + .expect("failed reading fork storage") + .fork + .as_ref() + .and_then(|fork| { + fork.fork_source + .get_transaction_by_block_number_and_index(block_number, index) + .ok() + }) + .flatten() + }); + + Ok(maybe_tx) + }) } fn protocol_version(&self) -> jsonrpc_core::BoxFuture> { @@ -2746,7 +2863,7 @@ mod tests { cache::CacheConfig, http_fork_source::HttpForkSource, node::InMemoryNode, - testing::{self, ForkBlockConfig, LogBuilder, MockServer}, + testing::{self, ForkBlockConfig, LogBuilder, MockServer, TransactionResponseBuilder}, }; use maplit::hashmap; use zksync_types::{ @@ -2864,7 +2981,7 @@ mod tests { #[tokio::test] async fn test_get_block_by_hash_for_produced_block() { let node = InMemoryNode::::default(); - let expected_block_hash = testing::apply_tx(&node, H256::repeat_byte(0x01)); + let (expected_block_hash, _) = testing::apply_tx(&node, H256::repeat_byte(0x01)); let actual_block = node .get_block_by_hash(expected_block_hash, false) @@ -3157,7 +3274,7 @@ mod tests { async fn test_get_block_transaction_count_by_hash_for_produced_block() { let node = InMemoryNode::::default(); - let expected_block_hash = testing::apply_tx(&node, H256::repeat_byte(0x01)); + let (expected_block_hash, _) = testing::apply_tx(&node, H256::repeat_byte(0x01)); let actual_transaction_count = node .get_block_transaction_count_by_hash(expected_block_hash) .await @@ -3364,7 +3481,7 @@ mod tests { async fn test_get_transaction_receipt_uses_produced_block_hash() { let node = InMemoryNode::::default(); let tx_hash = H256::repeat_byte(0x01); - let expected_block_hash = testing::apply_tx(&node, tx_hash); + let (expected_block_hash, _) = testing::apply_tx(&node, tx_hash); let actual_tx_receipt = node .get_transaction_receipt(tx_hash) @@ -3446,7 +3563,7 @@ mod tests { .new_block_filter() .await .expect("failed creating filter"); - let block_hash = testing::apply_tx(&node, H256::repeat_byte(0x1)); + let (block_hash, _) = testing::apply_tx(&node, H256::repeat_byte(0x1)); match node .get_filter_changes(filter_id) @@ -4232,4 +4349,319 @@ mod tests { storage.factory_dep_cache ); } + #[tokio::test] + async fn test_get_transaction_by_block_hash_and_index_returns_none_for_invalid_block_hash() { + let node = InMemoryNode::::default(); + let input_tx_hash = H256::repeat_byte(0x01); + let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash); + let invalid_block_hash = H256::repeat_byte(0xab); + assert_ne!(input_block_hash, invalid_block_hash); + + let result = node + .get_transaction_by_block_hash_and_index(invalid_block_hash, U64::from(0)) + .await + .expect("failed fetching transaction"); + + assert!(result.is_none()); + } + + #[tokio::test] + async fn test_get_transaction_by_block_hash_and_index_returns_none_for_invalid_index() { + let node = InMemoryNode::::default(); + let input_tx_hash = H256::repeat_byte(0x01); + let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash); + + let result = node + .get_transaction_by_block_hash_and_index(input_block_hash, U64::from(10)) + .await + .expect("failed fetching transaction"); + + assert!(result.is_none()); + } + + #[tokio::test] + async fn test_get_transaction_by_block_hash_and_index_returns_transaction_for_valid_input() { + let node = InMemoryNode::::default(); + let input_tx_hash = H256::repeat_byte(0x01); + let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash); + + let actual_tx = node + .get_transaction_by_block_hash_and_index(input_block_hash, U64::from(0)) + .await + .expect("failed fetching transaction") + .expect("no transaction"); + + assert_eq!(input_tx_hash, actual_tx.hash); + } + + #[tokio::test] + async fn test_get_transaction_by_block_hash_and_index_fetches_full_transaction_for_hash_from_fork( + ) { + let mock_server = MockServer::run_with_config(ForkBlockConfig { + number: 10, + transaction_count: 0, + hash: H256::repeat_byte(0xab), + }); + let input_block_hash = H256::repeat_byte(0x01); + let input_tx_hash = H256::repeat_byte(0x02); + mock_server.expect( + serde_json::json!({ + "jsonrpc": "2.0", + "id": 0, + "method": "eth_getTransactionByHash", + "params": [ + format!("{:#x}", input_tx_hash), + ], + }), + TransactionResponseBuilder::new() + .set_hash(input_tx_hash) + .set_block_hash(input_block_hash) + .set_block_number(U64::from(1)) + .build(), + ); + + let node = InMemoryNode::::new( + Some(ForkDetails::from_network(&mock_server.url(), None, CacheConfig::None).await), + crate::node::ShowCalls::None, + ShowStorageLogs::None, + ShowVMDetails::None, + ShowGasDetails::None, + false, + &system_contracts::Options::BuiltIn, + ); + + // store the block info with just the tx hash invariant + { + let mut writer = node.inner.write().unwrap(); + writer.blocks.insert( + input_block_hash, + Block { + transactions: vec![TransactionVariant::Hash(input_tx_hash)], + ..Default::default() + }, + ); + } + + let actual_tx = node + .get_transaction_by_block_hash_and_index(input_block_hash, U64::from(0)) + .await + .expect("failed fetching transaction") + .expect("no transaction"); + + assert_eq!(input_tx_hash, actual_tx.hash); + assert_eq!(Some(U64::from(1)), actual_tx.block_number); + } + + #[tokio::test] + async fn test_get_transaction_by_block_hash_and_index_fetches_from_fork_if_block_missing() { + let mock_server = MockServer::run_with_config(ForkBlockConfig { + number: 10, + transaction_count: 0, + hash: H256::repeat_byte(0xab), + }); + let input_block_hash = H256::repeat_byte(0x01); + let input_tx_hash = H256::repeat_byte(0x02); + mock_server.expect( + serde_json::json!({ + "jsonrpc": "2.0", + "id": 0, + "method": "eth_getTransactionByBlockHashAndIndex", + "params": [ + format!("{:#x}", input_block_hash), + "0x1" + ], + }), + TransactionResponseBuilder::new() + .set_hash(input_tx_hash) + .set_block_hash(input_block_hash) + .set_block_number(U64::from(100)) + .build(), + ); + + let node = InMemoryNode::::new( + Some(ForkDetails::from_network(&mock_server.url(), None, CacheConfig::None).await), + crate::node::ShowCalls::None, + ShowStorageLogs::None, + ShowVMDetails::None, + ShowGasDetails::None, + false, + &system_contracts::Options::BuiltIn, + ); + + let actual_tx = node + .get_transaction_by_block_hash_and_index(input_block_hash, U64::from(1)) + .await + .expect("failed fetching transaction") + .expect("no transaction"); + + assert_eq!(input_tx_hash, actual_tx.hash); + assert_eq!(Some(U64::from(100)), actual_tx.block_number); + } + + #[tokio::test] + async fn test_get_transaction_by_block_number_and_index_returns_none_for_invalid_block_number() + { + let node = InMemoryNode::::default(); + let input_tx_hash = H256::repeat_byte(0x01); + let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash); + let invalid_block_hash = H256::repeat_byte(0xab); + assert_ne!(input_block_hash, invalid_block_hash); + + let result = node + .get_transaction_by_block_number_and_index( + BlockNumber::Number(U64::from(100)), + U64::from(0), + ) + .await + .expect("failed fetching transaction"); + + assert!(result.is_none()); + } + + #[tokio::test] + async fn test_get_transaction_by_block_number_and_index_returns_none_for_invalid_index() { + let node = InMemoryNode::::default(); + let input_tx_hash = H256::repeat_byte(0x01); + testing::apply_tx(&node, input_tx_hash); + + let result = node + .get_transaction_by_block_number_and_index(BlockNumber::Latest, U64::from(10)) + .await + .expect("failed fetching transaction"); + + assert!(result.is_none()); + } + + #[tokio::test] + async fn test_get_transaction_by_block_number_and_index_returns_transaction_for_valid_input() { + let node = InMemoryNode::::default(); + let input_tx_hash = H256::repeat_byte(0x01); + let (_, input_block_number) = testing::apply_tx(&node, input_tx_hash); + + let actual_tx = node + .get_transaction_by_block_number_and_index( + BlockNumber::Number(input_block_number), + U64::from(0), + ) + .await + .expect("failed fetching transaction") + .expect("no transaction"); + + assert_eq!(input_tx_hash, actual_tx.hash); + } + + #[tokio::test] + async fn test_get_transaction_by_block_number_and_index_fetches_full_transaction_for_hash_from_fork( + ) { + let mock_server = MockServer::run_with_config(ForkBlockConfig { + number: 10, + transaction_count: 0, + hash: H256::repeat_byte(0xab), + }); + let input_block_hash = H256::repeat_byte(0x01); + let input_block_number = U64::from(100); + let input_tx_hash = H256::repeat_byte(0x02); + mock_server.expect( + serde_json::json!({ + "jsonrpc": "2.0", + "id": 0, + "method": "eth_getTransactionByHash", + "params": [ + format!("{:#x}", input_tx_hash), + ], + }), + TransactionResponseBuilder::new() + .set_hash(input_tx_hash) + .set_block_hash(input_block_hash) + .set_block_number(input_block_number) + .build(), + ); + + let node = InMemoryNode::::new( + Some(ForkDetails::from_network(&mock_server.url(), None, CacheConfig::None).await), + crate::node::ShowCalls::None, + ShowStorageLogs::None, + ShowVMDetails::None, + ShowGasDetails::None, + false, + &system_contracts::Options::BuiltIn, + ); + + // store the block info with just the tx hash invariant + { + let mut writer = node.inner.write().unwrap(); + writer + .block_hashes + .insert(input_block_number.as_u64(), input_block_hash); + writer.blocks.insert( + input_block_hash, + Block { + transactions: vec![TransactionVariant::Hash(input_tx_hash)], + ..Default::default() + }, + ); + } + + let actual_tx = node + .get_transaction_by_block_number_and_index( + BlockNumber::Number(input_block_number), + U64::from(0), + ) + .await + .expect("failed fetching transaction") + .expect("no transaction"); + + assert_eq!(input_tx_hash, actual_tx.hash); + assert_eq!(Some(U64::from(input_block_number)), actual_tx.block_number); + } + + #[tokio::test] + async fn test_get_transaction_by_block_number_and_index_fetches_from_fork_if_block_missing() { + let mock_server = MockServer::run_with_config(ForkBlockConfig { + number: 10, + transaction_count: 0, + hash: H256::repeat_byte(0xab), + }); + let input_block_hash = H256::repeat_byte(0x01); + let input_block_number = U64::from(100); + let input_tx_hash = H256::repeat_byte(0x02); + mock_server.expect( + serde_json::json!({ + "jsonrpc": "2.0", + "id": 0, + "method": "eth_getTransactionByBlockNumberAndIndex", + "params": [ + format!("{:#x}", input_block_number), + "0x1" + ], + }), + TransactionResponseBuilder::new() + .set_hash(input_tx_hash) + .set_block_hash(input_block_hash) + .set_block_number(U64::from(input_block_number)) + .build(), + ); + + let node = InMemoryNode::::new( + Some(ForkDetails::from_network(&mock_server.url(), None, CacheConfig::None).await), + crate::node::ShowCalls::None, + ShowStorageLogs::None, + ShowVMDetails::None, + ShowGasDetails::None, + false, + &system_contracts::Options::BuiltIn, + ); + + let actual_tx = node + .get_transaction_by_block_number_and_index( + BlockNumber::Number(input_block_number), + U64::from(1), + ) + .await + .expect("failed fetching transaction") + .expect("no transaction"); + + assert_eq!(input_tx_hash, actual_tx.hash); + assert_eq!(Some(input_block_number), actual_tx.block_number); + } } diff --git a/src/testing.rs b/src/testing.rs index 97a02fdd..b43359e3 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -230,6 +230,8 @@ impl BlockResponseBuilder { #[derive(Default, Debug, Clone)] pub struct TransactionResponseBuilder { hash: H256, + block_hash: H256, + block_number: U64, } impl TransactionResponseBuilder { @@ -238,19 +240,31 @@ impl TransactionResponseBuilder { Self::default() } - /// Sets the block hash + /// Sets the transaction hash pub fn set_hash(&mut self, hash: H256) -> &mut Self { self.hash = hash; self } + /// Sets the block hash + pub fn set_block_hash(&mut self, hash: H256) -> &mut Self { + self.block_hash = hash; + self + } + + /// Sets the block number + pub fn set_block_number(&mut self, number: U64) -> &mut Self { + self.block_number = number; + self + } + /// Builds the transaction json result pub fn build_result(&mut self) -> serde_json::Value { serde_json::json!({ "hash": format!("{:#x}", self.hash), "nonce": "0x0", - "blockHash": "0x51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f", - "blockNumber": "0x1", + "blockHash": format!("{:#x}", self.block_hash), + "blockNumber": format!("{:#x}", self.block_number), "transactionIndex": "0x0", "from": "0x29df43f75149d0552475a6f9b2ac96e28796ed0b", "to": "0x0000000000000000000000000000000000008006", @@ -344,7 +358,10 @@ impl RawTransactionsResponseBuilder { } /// Applies a transaction with a given hash to the node and returns the block hash. -pub fn apply_tx(node: &InMemoryNode, tx_hash: H256) -> H256 { +pub fn apply_tx( + node: &InMemoryNode, + tx_hash: H256, +) -> (H256, U64) { let next_miniblock = node .get_inner() .read() @@ -376,7 +393,7 @@ pub fn apply_tx(node: &InMemoryNode, tx_hash tx.set_input(vec![], tx_hash); node.apply_txs(vec![tx]).expect("failed applying tx"); - produced_block_hash + (produced_block_hash, U64::from(next_miniblock)) } /// Deploys a contract with the given bytecode. @@ -642,13 +659,14 @@ mod test { #[tokio::test] async fn test_apply_tx() { let node = InMemoryNode::::default(); - let actual_block_hash = apply_tx(&node, H256::repeat_byte(0x01)); + let (actual_block_hash, actual_block_number) = apply_tx(&node, H256::repeat_byte(0x01)); assert_eq!( H256::from_str("0xd97ba6a5ab0f2d7fbfc697251321cce20bff3da2b0ddaf12c80f80f0ab270b15") .unwrap(), actual_block_hash, ); + assert_eq!(U64::from(1), actual_block_number); assert!( node.get_inner() diff --git a/test_endpoints.http b/test_endpoints.http index 97463ec5..c08513d3 100644 --- a/test_endpoints.http +++ b/test_endpoints.http @@ -509,6 +509,28 @@ content-type: application/json POST http://localhost:8011 content-type: application/json +{ + "jsonrpc": "2.0", + "id": "2", + "method": "eth_getTransactionByBlockHashAndIndex", + "params": ["0x123456789abcdef123456789abcdef1234567890", "0x1"] +} + +### +POST http://localhost:8011 +content-type: application/json + +{ + "jsonrpc": "2.0", + "id": "2", + "method": "eth_getTransactionByBlockNumberAndIndex", + "params": ["latest", "0x1"] +} + +### +POST http://localhost:8011 +content-type: application/json + { "jsonrpc": "2.0", "id": "2",