Skip to content

Commit 62efa15

Browse files
authored
Merge pull request #195 from decipherhub/fix/parenthash-blockhash
fix: correct parentHash and blockHash in RPC responses
2 parents 449a5f7 + f2aa0c4 commit 62efa15

File tree

2 files changed

+91
-27
lines changed

2 files changed

+91
-27
lines changed

crates/node/src/execution_bridge.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,33 @@ impl ExecutionBridge {
252252
self.gas_limit
253253
}
254254

255+
/// Set the initial block hash for chain connectivity.
256+
///
257+
/// This must be called after the genesis block is created in storage to ensure
258+
/// that block 1's parent_hash correctly references the genesis block hash.
259+
/// If not called, block 1 would have parent_hash = 0x000...000 (the default).
260+
///
261+
/// # Arguments
262+
///
263+
/// * `genesis_hash` - The hash of the genesis block (block 0)
264+
pub fn set_genesis_block_hash(&self, genesis_hash: B256) {
265+
match self.last_block_hash.write() {
266+
Ok(mut guard) => {
267+
*guard = genesis_hash;
268+
debug!(
269+
genesis_hash = %genesis_hash,
270+
"Execution bridge initialized with genesis block hash"
271+
);
272+
}
273+
Err(e) => {
274+
warn!(
275+
error = %e,
276+
"Failed to set genesis block hash - lock poisoned, block 1 may have incorrect parent_hash"
277+
);
278+
}
279+
}
280+
}
281+
255282
/// Validate a transaction for mempool CheckTx
256283
///
257284
/// This is called by workers before accepting transactions into batches.
@@ -758,6 +785,26 @@ mod tests {
758785
);
759786
}
760787

788+
#[tokio::test]
789+
async fn test_set_genesis_block_hash() {
790+
let bridge = create_default_bridge().unwrap();
791+
792+
// Initially should be B256::ZERO
793+
let initial_hash = bridge.last_block_hash.read().map(|guard| *guard).unwrap();
794+
assert_eq!(initial_hash, B256::ZERO);
795+
796+
// Set genesis hash
797+
let genesis_hash = B256::from([0x42u8; 32]);
798+
bridge.set_genesis_block_hash(genesis_hash);
799+
800+
// Should now be updated
801+
let updated_hash = bridge.last_block_hash.read().map(|guard| *guard).unwrap();
802+
assert_eq!(
803+
updated_hash, genesis_hash,
804+
"set_genesis_block_hash should update last_block_hash"
805+
);
806+
}
807+
761808
#[tokio::test]
762809
async fn test_gas_limit_from_config() {
763810
let (bridge, _temp_dir) = create_default_bridge().unwrap();

crates/node/src/node.rs

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::config::NodeConfig;
1919
use crate::execution_bridge::{BlockExecutionResult, ExecutionBridge};
2020
use crate::network::{TcpPrimaryNetwork, TcpWorkerNetwork};
2121
use crate::supervisor::NodeSupervisor;
22-
use alloy_primitives::Address;
22+
use alloy_primitives::{Address, B256};
2323
use anyhow::{Context, Result};
2424
use cipherbft_consensus::{
2525
create_context, default_consensus_params, default_engine_config_single_part, spawn_host,
@@ -1133,30 +1133,41 @@ impl Node {
11331133

11341134
// Ensure genesis block (block 0) exists for Ethereum RPC compatibility
11351135
// Block explorers like Blockscout expect block 0 to exist
1136-
match storage.block_store().get_block_by_number(0).await {
1137-
Ok(Some(_)) => {
1138-
debug!("Genesis block (block 0) already exists in storage");
1139-
}
1140-
Ok(None) => {
1141-
// Create and store genesis block
1142-
let genesis_timestamp = std::time::SystemTime::now()
1143-
.duration_since(std::time::UNIX_EPOCH)
1144-
.unwrap_or_default()
1145-
.as_secs();
1146-
let genesis_block =
1147-
Self::create_genesis_block(genesis_timestamp, self.gas_limit);
1148-
if let Err(e) = storage.block_store().put_block(&genesis_block).await {
1149-
error!("Failed to store genesis block: {}", e);
1150-
} else {
1151-
info!(
1152-
"Created genesis block (block 0) with hash 0x{}",
1153-
hex::encode(&genesis_block.hash[..8])
1154-
);
1136+
let genesis_hash: Option<[u8; 32]> =
1137+
match storage.block_store().get_block_by_number(0).await {
1138+
Ok(Some(existing_block)) => {
1139+
debug!("Genesis block (block 0) already exists in storage");
1140+
Some(existing_block.hash)
11551141
}
1156-
}
1157-
Err(e) => {
1158-
warn!("Failed to check for genesis block: {}", e);
1159-
}
1142+
Ok(None) => {
1143+
// Create and store genesis block
1144+
let genesis_timestamp = std::time::SystemTime::now()
1145+
.duration_since(std::time::UNIX_EPOCH)
1146+
.unwrap_or_default()
1147+
.as_secs();
1148+
let genesis_block =
1149+
Self::create_genesis_block(genesis_timestamp, self.gas_limit);
1150+
let hash = genesis_block.hash;
1151+
if let Err(e) = storage.block_store().put_block(&genesis_block).await {
1152+
error!("Failed to store genesis block: {}", e);
1153+
None
1154+
} else {
1155+
info!(
1156+
"Created genesis block (block 0) with hash 0x{}",
1157+
hex::encode(&genesis_block.hash[..8])
1158+
);
1159+
Some(hash)
1160+
}
1161+
}
1162+
Err(e) => {
1163+
warn!("Failed to check for genesis block: {}", e);
1164+
None
1165+
}
1166+
};
1167+
1168+
// Synchronize genesis block hash with execution bridge for correct parent_hash in block 1
1169+
if let (Some(hash), Some(ref bridge)) = (genesis_hash, &self.execution_bridge) {
1170+
bridge.set_genesis_block_hash(B256::from(hash));
11601171
}
11611172

11621173
// Initialize latest_block from storage (important for restart scenarios)
@@ -1515,11 +1526,12 @@ impl Node {
15151526

15161527
// Store receipts for eth_getBlockReceipts queries
15171528
if !block_result.execution_result.receipts.is_empty() {
1529+
let block_hash_bytes = block_result.block_hash.0;
15181530
let storage_receipts: Vec<StorageReceipt> = block_result
15191531
.execution_result
15201532
.receipts
15211533
.iter()
1522-
.map(Self::execution_receipt_to_storage)
1534+
.map(|r| Self::execution_receipt_to_storage(r, block_hash_bytes))
15231535
.collect();
15241536
if let Err(e) = storage.receipt_store().put_receipts(&storage_receipts).await {
15251537
error!("Failed to store {} receipts for block {}: {}", storage_receipts.len(), height.0, e);
@@ -1781,7 +1793,12 @@ impl Node {
17811793
/// Convert an execution TransactionReceipt to a storage Receipt for MDBX persistence.
17821794
///
17831795
/// This bridges the execution layer receipt format to the storage layer format.
1784-
fn execution_receipt_to_storage(receipt: &ExecutionReceipt) -> StorageReceipt {
1796+
/// The block_hash parameter is passed explicitly because the execution receipt
1797+
/// is created before the block hash is computed, so it contains a placeholder value.
1798+
fn execution_receipt_to_storage(
1799+
receipt: &ExecutionReceipt,
1800+
block_hash: [u8; 32],
1801+
) -> StorageReceipt {
17851802
// Convert logs
17861803
let logs: Vec<StorageLog> = receipt
17871804
.logs
@@ -1795,7 +1812,7 @@ impl Node {
17951812
StorageReceipt {
17961813
transaction_hash: receipt.transaction_hash.0,
17971814
block_number: receipt.block_number,
1798-
block_hash: receipt.block_hash.0,
1815+
block_hash,
17991816
transaction_index: receipt.transaction_index as u32,
18001817
from: receipt.from.0 .0,
18011818
to: receipt.to.map(|a| a.0 .0),

0 commit comments

Comments
 (0)