Repository: matter-labs/era-test-node (rust)

Issue:
Correctly fill block details
When we create blocks for in-memory-node, we set [most fields to default](https://github.com/matter-labs/era-test-node/blob/main/src/node/in_memory.rs#L114).
SDKs may rely on these fields, e.g. `viem` may use `baseFeePerGas` to set `maxFeePerGas` for transactions.

We need to make sure that for each field we set a sane value that makes practical sense if anyone uses it.

Pull request description:
feat: populate block fields
# What :computer: 

Makes sure that all block fields are set to reasonable values

# Why :hand:

See #358 for motivation


Patch:
diff --git a/src/node/eth.rs b/src/node/eth.rs
index b7c87f7..c8ce50e 100644
--- a/src/node/eth.rs
+++ b/src/node/eth.rs
@@ -1471,6 +1471,8 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EthTestNod
 
 #[cfg(test)]
 mod tests {
+    use super::*;
+    use crate::node::NON_FORK_FIRST_BLOCK_TIMESTAMP;
     use crate::{
         config::{cache::CacheConfig, gas::DEFAULT_L2_GAS_PRICE},
         fork::ForkDetails,
@@ -1482,16 +1484,18 @@ mod tests {
         },
     };
     use maplit::hashmap;
+    use zksync_basic_types::vm::VmVersion;
     use zksync_basic_types::{web3, Nonce};
+    use zksync_multivm::utils::get_max_batch_gas_limit;
+    use zksync_types::l2::TransactionType;
     use zksync_types::{
+        api,
         api::{BlockHashObject, BlockNumber, BlockNumberObject, TransactionReceipt},
         utils::deployed_address_create,
-        K256PrivateKey,
+        Bloom, K256PrivateKey, EMPTY_UNCLES_HASH,
     };
     use zksync_web3_decl::types::{SyncState, ValueOrArray};
 
-    use super::*;
-
     #[tokio::test]
     async fn test_eth_syncing() {
         let node = InMemoryNode::<HttpForkSource>::default();
@@ -1639,7 +1643,13 @@ mod tests {
     #[tokio::test]
     async fn test_get_block_by_hash_for_produced_block() {
         let node = InMemoryNode::<HttpForkSource>::default();
-        let (expected_block_hash, _) = testing::apply_tx(&node, H256::repeat_byte(0x01));
+        let tx_hash = H256::repeat_byte(0x01);
+        let (expected_block_hash, _, _) = testing::apply_tx(&node, tx_hash);
+        let genesis_block = node
+            .get_block_by_number(BlockNumber::from(0), false)
+            .await
+            .expect("failed fetching block by number")
+            .expect("no block");
 
         let actual_block = node
             .get_block_by_hash(expected_block_hash, false)
@@ -1647,9 +1657,40 @@ mod tests {
             .expect("failed fetching block by hash")
             .expect("no block");
 
-        assert_eq!(expected_block_hash, actual_block.hash);
-        assert_eq!(U64::from(1), actual_block.number);
-        assert_eq!(Some(U64::from(1)), actual_block.l1_batch_number);
+        let expected_block: Block<TransactionVariant> = Block {
+            hash: expected_block_hash,
+            parent_hash: genesis_block.hash,
+            uncles_hash: EMPTY_UNCLES_HASH,
+            author: Default::default(),
+            state_root: Default::default(),
+            transactions_root: Default::default(),
+            receipts_root: Default::default(),
+            number: U64::from(1),
+            l1_batch_number: Some(U64::from(1)),
+            gas_used: actual_block.gas_used, // Checked separately, see below
+            gas_limit: U256::from(get_max_batch_gas_limit(VmVersion::latest())),
+            base_fee_per_gas: actual_block.base_fee_per_gas, // Checked separately, see below
+            extra_data: Default::default(),
+            logs_bloom: actual_block.logs_bloom, // Checked separately, see below
+            timestamp: U256::from(NON_FORK_FIRST_BLOCK_TIMESTAMP + 1),
+            l1_batch_timestamp: Some(U256::from(NON_FORK_FIRST_BLOCK_TIMESTAMP + 1)),
+            difficulty: Default::default(),
+            total_difficulty: Default::default(),
+            seal_fields: vec![],
+            uncles: vec![],
+            transactions: vec![TransactionVariant::Hash(tx_hash)],
+            size: Default::default(),
+            mix_hash: Default::default(),
+            nonce: Default::default(),
+        };
+
+        assert_eq!(expected_block, actual_block);
+
+        // It is hard to predict the values below without repeating the exact logic used to calculate
+        // them. We are resorting to some basic sanity checks instead.
+        assert!(actual_block.gas_used > U256::zero());
+        assert!(actual_block.base_fee_per_gas > U256::zero());
+        assert_ne!(actual_block.logs_bloom, Bloom::zero());
     }
 
     #[tokio::test]
@@ -1748,17 +1789,102 @@ mod tests {
     #[tokio::test]
     async fn test_get_block_by_number_for_produced_block() {
         let node = InMemoryNode::<HttpForkSource>::default();
-        testing::apply_tx(&node, H256::repeat_byte(0x01));
+        let tx_hash = H256::repeat_byte(0x01);
+        let (expected_block_hash, _, _) = testing::apply_tx(&node, tx_hash);
         let expected_block_number = 1;
+        let genesis_block = node
+            .get_block_by_number(BlockNumber::from(0), false)
+            .await
+            .expect("failed fetching block by number")
+            .expect("no block");
 
         let actual_block = node
             .get_block_by_number(BlockNumber::Number(U64::from(expected_block_number)), false)
             .await
-            .expect("failed fetching block by hash")
+            .expect("failed fetching block by number")
             .expect("no block");
 
-        assert_eq!(U64::from(expected_block_number), actual_block.number);
-        assert_eq!(1, actual_block.transactions.len());
+        let expected_block: Block<TransactionVariant> = Block {
+            hash: expected_block_hash,
+            parent_hash: genesis_block.hash,
+            uncles_hash: EMPTY_UNCLES_HASH,
+            author: Default::default(),
+            state_root: Default::default(),
+            transactions_root: Default::default(),
+            receipts_root: Default::default(),
+            number: U64::from(expected_block_number),
+            l1_batch_number: Some(U64::from(1)),
+            gas_used: actual_block.gas_used, // Checked separately, see below
+            gas_limit: U256::from(get_max_batch_gas_limit(VmVersion::latest())),
+            base_fee_per_gas: actual_block.base_fee_per_gas, // Checked separately, see below
+            extra_data: Default::default(),
+            logs_bloom: actual_block.logs_bloom, // Checked separately, see below
+            timestamp: U256::from(NON_FORK_FIRST_BLOCK_TIMESTAMP + 1),
+            l1_batch_timestamp: Some(U256::from(NON_FORK_FIRST_BLOCK_TIMESTAMP + 1)),
+            difficulty: Default::default(),
+            total_difficulty: Default::default(),
+            seal_fields: vec![],
+            uncles: vec![],
+            transactions: vec![TransactionVariant::Hash(tx_hash)],
+            size: Default::default(),
+            mix_hash: Default::default(),
+            nonce: Default::default(),
+        };
+
+        assert_eq!(expected_block, actual_block);
+
+        // It is hard to predict the values below without repeating the exact logic used to calculate
+        // them. We are resorting to some basic sanity checks instead.
+        assert!(actual_block.gas_used > U256::zero());
+        assert!(actual_block.base_fee_per_gas > U256::zero());
+        assert_ne!(actual_block.logs_bloom, Bloom::zero());
+    }
+
+    #[tokio::test]
+    async fn test_get_block_by_number_for_produced_block_full_txs() {
+        let node = InMemoryNode::<HttpForkSource>::default();
+        let tx_hash = H256::repeat_byte(0x01);
+        let (block_hash, _, tx) = testing::apply_tx(&node, tx_hash);
+        let expected_block_number = 1;
+
+        let mut actual_block = node
+            .get_block_by_number(BlockNumber::Number(U64::from(expected_block_number)), true)
+            .await
+            .expect("failed fetching block by number")
+            .expect("no block");
+
+        assert_eq!(actual_block.transactions.len(), 1);
+        let actual_tx = match actual_block.transactions.remove(0) {
+            TransactionVariant::Full(tx) => tx,
+            TransactionVariant::Hash(_) => unreachable!(),
+        };
+        let expected_tx = api::Transaction {
+            hash: tx_hash,
+            nonce: U256::from(0),
+            block_hash: Some(block_hash),
+            block_number: Some(U64::from(expected_block_number)),
+            transaction_index: Some(U64::from(0)),
+            from: Some(tx.initiator_account()),
+            to: tx.recipient_account(),
+            value: U256::from(1),
+            gas_price: Some(tx.common_data.fee.max_fee_per_gas),
+            gas: tx.common_data.fee.gas_limit,
+            input: Default::default(),
+            v: actual_tx.v, // Checked separately, see below
+            r: actual_tx.r, // Checked separately, see below
+            s: actual_tx.s, // Checked separately, see below
+            raw: None,
+            transaction_type: Some(U64::from(TransactionType::EIP712Transaction as u32)),
+            access_list: None,
+            max_fee_per_gas: Some(tx.common_data.fee.max_fee_per_gas),
+            max_priority_fee_per_gas: Some(tx.common_data.fee.max_priority_fee_per_gas),
+            chain_id: U256::from(260),
+            l1_batch_number: Some(U64::from(1)),
+            l1_batch_tx_index: Some(U64::from(0)),
+        };
+        assert_eq!(expected_tx, actual_tx);
+
+        // TODO: Verify that the TX is signed properly (use alloy to abstract from zksync-core code?)
     }
 
     #[tokio::test]
@@ -1939,7 +2065,7 @@ mod tests {
     async fn test_get_block_transaction_count_by_hash_for_produced_block() {
         let node = InMemoryNode::<HttpForkSource>::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
@@ -2150,7 +2276,7 @@ mod tests {
     async fn test_get_transaction_receipt_uses_produced_block_hash() {
         let node = InMemoryNode::<HttpForkSource>::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)
@@ -2232,7 +2358,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)
@@ -3048,7 +3174,7 @@ mod tests {
     async fn test_get_transaction_by_block_hash_and_index_returns_none_for_invalid_block_hash() {
         let node = InMemoryNode::<HttpForkSource>::default();
         let input_tx_hash = H256::repeat_byte(0x01);
-        let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash);
+        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);
 
@@ -3064,7 +3190,7 @@ mod tests {
     async fn test_get_transaction_by_block_hash_and_index_returns_none_for_invalid_index() {
         let node = InMemoryNode::<HttpForkSource>::default();
         let input_tx_hash = H256::repeat_byte(0x01);
-        let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash);
+        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))
@@ -3078,7 +3204,7 @@ mod tests {
     async fn test_get_transaction_by_block_hash_and_index_returns_transaction_for_valid_input() {
         let node = InMemoryNode::<HttpForkSource>::default();
         let input_tx_hash = H256::repeat_byte(0x01);
-        let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash);
+        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))
@@ -3201,7 +3327,7 @@ mod tests {
     {
         let node = InMemoryNode::<HttpForkSource>::default();
         let input_tx_hash = H256::repeat_byte(0x01);
-        let (input_block_hash, _) = testing::apply_tx(&node, input_tx_hash);
+        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);
 
@@ -3234,7 +3360,7 @@ mod tests {
     async fn test_get_transaction_by_block_number_and_index_returns_transaction_for_valid_input() {
         let node = InMemoryNode::<HttpForkSource>::default();
         let input_tx_hash = H256::repeat_byte(0x01);
-        let (_, input_block_number) = testing::apply_tx(&node, input_tx_hash);
+        let (_, input_block_number, _) = testing::apply_tx(&node, input_tx_hash);
 
         let actual_tx = node
             .get_transaction_by_block_number_and_index(
diff --git a/src/node/in_memory.rs b/src/node/in_memory.rs
index 933db58..f7e12cb 100644
--- a/src/node/in_memory.rs
+++ b/src/node/in_memory.rs
@@ -1,77 +1,75 @@
 //! In-memory node, that supports forking other networks.
-use crate::{
-    bootloader_debug::{BootloaderDebug, BootloaderDebugTracer},
-    config::{
-        cache::CacheConfig,
-        gas::{self, GasConfig},
-        node::{InMemoryNodeConfig, ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails},
-    },
-    console_log::ConsoleLogHandler,
-    constants::{LEGACY_RICH_WALLETS, RICH_WALLETS},
-    deps::{storage_view::StorageView, InMemoryStorage},
-    filters::EthFilters,
-    fork::{ForkDetails, ForkSource, ForkStorage},
-    formatter,
-    node::{
-        call_error_tracer::CallErrorTracer, fee_model::TestNodeFeeInputProvider,
-        storage_logs::print_storage_logs_details,
-    },
-    observability::Observability,
-    system_contracts::{self, SystemContracts},
-    utils::{bytecode_to_factory_dep, create_debug_output, into_jsrpc_error, to_human_size},
-};
-use anyhow::Context as _;
-use colored::Colorize;
-use indexmap::IndexMap;
-use once_cell::sync::OnceCell;
 use std::{
     collections::{HashMap, HashSet},
+    convert::TryInto,
     str::FromStr,
     sync::{Arc, RwLock},
 };
 
-use std::convert::TryInto;
+use anyhow::Context as _;
+use colored::Colorize;
+use indexmap::IndexMap;
+use once_cell::sync::OnceCell;
 use zksync_basic_types::{
-    web3::keccak256, web3::Bytes, AccountTreeId, Address, L1BatchNumber, L2BlockNumber, H160, H256,
-    U256, U64,
+    web3::{keccak256, Bytes, Index},
+    AccountTreeId, Address, L1BatchNumber, L2BlockNumber, H160, H256, H64, U256, U64,
 };
 use zksync_contracts::BaseSystemContracts;
-use zksync_multivm::interface::storage::{ReadStorage, StoragePtr, WriteStorage};
 use zksync_multivm::{
     interface::{
+        storage::{ReadStorage, StoragePtr, WriteStorage},
         Call, ExecutionResult, L1BatchEnv, L2Block, L2BlockEnv, SystemEnv, TxExecutionMode,
         VmExecutionMode, VmExecutionResultAndLogs, VmFactory, VmInterface, VmInterfaceExt,
     },
-    vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT,
-    VmVersion,
-};
-use zksync_multivm::{
     tracers::CallTracer,
     utils::{
         adjust_pubdata_price_for_tx, derive_base_fee_and_gas_per_pubdata, derive_overhead,
-        get_max_gas_per_pubdata_byte,
+        get_batch_base_fee, get_max_batch_gas_limit, get_max_gas_per_pubdata_byte,
     },
-    vm_latest::HistoryDisabled,
     vm_latest::{
-        constants::{BATCH_GAS_LIMIT, MAX_VM_PUBDATA_PER_BATCH},
+        constants::{BATCH_COMPUTATIONAL_GAS_LIMIT, BATCH_GAS_LIMIT, MAX_VM_PUBDATA_PER_BATCH},
         utils::l2_blocks::load_last_l2_block,
-        ToTracerPointer, Vm,
+        HistoryDisabled, ToTracerPointer, Vm,
     },
+    VmVersion,
 };
 use zksync_types::{
     api::{Block, DebugCall, Log, TransactionReceipt, TransactionVariant},
-    block::{unpack_block_info, L2BlockHasher},
+    block::{build_bloom, unpack_block_info, L2BlockHasher},
     fee::Fee,
     fee_model::{BatchFeeInput, PubdataIndependentBatchFeeModelInput},
     get_code_key, get_nonce_key,
     l2::{L2Tx, TransactionType},
     utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance},
-    PackedEthSignature, StorageKey, StorageValue, Transaction, ACCOUNT_CODE_STORAGE_ADDRESS,
-    MAX_L2_TX_GAS_LIMIT, SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_BLOCK_INFO_POSITION,
+    BloomInput, PackedEthSignature, StorageKey, StorageValue, Transaction,
+    ACCOUNT_CODE_STORAGE_ADDRESS, EMPTY_UNCLES_HASH, MAX_L2_TX_GAS_LIMIT, SYSTEM_CONTEXT_ADDRESS,
+    SYSTEM_CONTEXT_BLOCK_INFO_POSITION,
 };
 use zksync_utils::{bytecode::hash_bytecode, h256_to_account_address, h256_to_u256, u256_to_h256};
 use zksync_web3_decl::error::Web3Error;
 
+use crate::{
+    bootloader_debug::{BootloaderDebug, BootloaderDebugTracer},
+    config::{
+        cache::CacheConfig,
+        gas::{self, GasConfig},
+        node::{InMemoryNodeConfig, ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails},
+    },
+    console_log::ConsoleLogHandler,
+    constants::{LEGACY_RICH_WALLETS, RICH_WALLETS},
+    deps::{storage_view::StorageView, InMemoryStorage},
+    filters::EthFilters,
+    fork::{ForkDetails, ForkSource, ForkStorage},
+    formatter,
+    node::{
+        call_error_tracer::CallErrorTracer, fee_model::TestNodeFeeInputProvider,
+        storage_logs::print_storage_logs_details,
+    },
+    observability::Observability,
+    system_contracts::{self, SystemContracts},
+    utils::{bytecode_to_factory_dep, create_debug_output, into_jsrpc_error, to_human_size},
+};
+
 /// Max possible size of an ABI encoded tx (in bytes).
 pub const MAX_TX_SIZE: usize = 1_000_000;
 /// Timestamp of the first block (if not running in fork mode).
@@ -1461,8 +1459,11 @@ impl<S: ForkSource + std::fmt::Debug + Clone> InMemoryNode<S> {
         let hash = compute_hash(block_ctx.miniblock, l2_tx.hash());
 
         let mut transaction = zksync_types::api::Transaction::from(l2_tx);
-        transaction.block_hash = Some(inner.current_miniblock_hash);
-        transaction.block_number = Some(U64::from(inner.current_miniblock));
+        transaction.block_hash = Some(hash);
+        transaction.block_number = Some(U64::from(block_ctx.miniblock));
+        transaction.transaction_index = Some(Index::zero());
+        transaction.l1_batch_number = Some(U64::from(batch_env.number.0));
+        transaction.l1_batch_tx_index = Some(Index::zero());
 
         let parent_block_hash = inner
             .block_hashes
@@ -1470,16 +1471,40 @@ impl<S: ForkSource + std::fmt::Debug + Clone> InMemoryNode<S> {
             .cloned()
             .unwrap_or_default();
 
+        let iter = tx_result.logs.events.iter().flat_map(|event| {
+            event
+                .indexed_topics
+                .iter()
+                .map(|topic| BloomInput::Raw(topic.as_bytes()))
+                .chain([BloomInput::Raw(event.address.as_bytes())])
+        });
+        let logs_bloom = build_bloom(iter);
+
         let block = Block {
             hash,
             parent_hash: parent_block_hash,
+            uncles_hash: EMPTY_UNCLES_HASH, // Static for non-PoW chains, see EIP-3675
             number: U64::from(block_ctx.miniblock),
-            timestamp: U256::from(batch_env.timestamp),
             l1_batch_number: Some(U64::from(batch_env.number.0)),
+            base_fee_per_gas: U256::from(get_batch_base_fee(&batch_env, VmVersion::latest())),
+            timestamp: U256::from(batch_env.timestamp),
+            l1_batch_timestamp: Some(U256::from(batch_env.timestamp)),
             transactions: vec![TransactionVariant::Full(transaction)],
             gas_used: U256::from(tx_result.statistics.gas_used),
-            gas_limit: U256::from(BATCH_GAS_LIMIT),
-            ..Default::default()
+            gas_limit: U256::from(get_max_batch_gas_limit(VmVersion::latest())),
+            logs_bloom,
+            author: Address::default(), // Matches core's behavior, irrelevant for ZKsync
+            state_root: H256::default(), // Intentionally empty as blocks in ZKsync don't have state - batches do
+            transactions_root: H256::default(), // Intentionally empty as blocks in ZKsync don't have state - batches do
+            receipts_root: H256::default(), // Intentionally empty as blocks in ZKsync don't have state - batches do
+            extra_data: Bytes::default(),   // Matches core's behavior, not used in ZKsync
+            difficulty: U256::default(), // Empty for non-PoW chains, see EIP-3675, TODO: should be 2500000000000000 to match DIFFICULTY opcode
+            total_difficulty: U256::default(), // Empty for non-PoW chains, see EIP-3675
+            seal_fields: vec![],         // Matches core's behavior, TODO: remove
+            uncles: vec![],              // Empty for non-PoW chains, see EIP-3675
+            size: U256::default(), // Matches core's behavior, TODO: perhaps it should be computed
+            mix_hash: H256::default(), // Empty for non-PoW chains, see EIP-3675
+            nonce: H64::default(), // Empty for non-PoW chains, see EIP-3675
         };
 
         let mut bytecodes = HashMap::new();


Test patch:
diff --git a/src/testing.rs b/src/testing.rs
index 4ed4388..bd07883 100644
--- a/src/testing.rs
+++ b/src/testing.rs
@@ -452,7 +452,10 @@ impl TransactionBuilder {
             Default::default(),
         )
         .unwrap();
-        tx.set_input(vec![], self.tx_hash);
+        tx.set_input(
+            tx.common_data.input_data().unwrap_or_default().into(),
+            self.tx_hash,
+        );
         tx
     }
 }
@@ -461,7 +464,7 @@ impl TransactionBuilder {
 pub fn apply_tx<T: ForkSource + std::fmt::Debug + Clone>(
     node: &InMemoryNode<T>,
     tx_hash: H256,
-) -> (H256, U64) {
+) -> (H256, U64, L2Tx) {
     let next_miniblock = node
         .get_inner()
         .read()
@@ -472,9 +475,10 @@ pub fn apply_tx<T: ForkSource + std::fmt::Debug + Clone>(
     let tx = TransactionBuilder::new().set_hash(tx_hash).build();
 
     node.set_rich_account(tx.common_data.initiator_address);
-    node.apply_txs(vec![tx]).expect("failed applying tx");
+    node.apply_txs(vec![tx.clone()])
+        .expect("failed applying tx");
 
-    (produced_block_hash, U64::from(next_miniblock))
+    (produced_block_hash, U64::from(next_miniblock), tx)
 }
 
 /// Deploys a contract with the given bytecode.
@@ -964,7 +968,7 @@ mod test {
     #[tokio::test]
     async fn test_apply_tx() {
         let node = InMemoryNode::<HttpForkSource>::default();
-        let (actual_block_hash, actual_block_number) = 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")