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",