Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ path = "src/main.rs"
name = "snapshot-checker"
path = "src/bin/snapshot_checker.rs"



[dependencies]
reth = { git = "https://github.com/clydemeng/reth.git", branch = "parlia-6487f0b" }
reth-cli = { git = "https://github.com/clydemeng/reth.git", branch = "parlia-6487f0b" }
Expand Down
9 changes: 8 additions & 1 deletion scripts/start_testnet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,17 @@ fi
# tip_block=0xb230ec6bfd3348dff7ae9af62d8d2fb25a2ff3781c770b3fcf75a186e6ddc1bd # 25M

# tip_block=0x1253e0b2342239c7e042d87d75974e7824c5503cd2ec34bfc7f5a8b25a1c36b1 # 35M
# tip_block=0xb74e00072b7aa7720f547c4ec3075b1f7310f98a2d8b3323289ad66927010dfa # 47M


tip_block=0xb74e00072b7aa7720f547c4ec3075b1f7310f98a2d8b3323289ad66927010dfa # 45M




tip_block=0xb5ddf3dcb55cf5013110acd3c6c8eaffe5c996e7d3c9d4803e36e9efccdbce47 # 43150000


tip_block=0x1253e0b2342239c7e042d87d75974e7824c5503cd2ec34bfc7f5a8b25a1c36b1 # 35M
RUST_LOG=INFO ./target/release/reth-bsc node \
--chain=bsc-testnet \
--http --http.api="eth, net, txpool, web3, rpc" \
Expand Down
108 changes: 80 additions & 28 deletions src/consensus/parlia/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ where
let message = Message::from_digest(seal_hash.0);

// Parse signature: 64 bytes + 1 recovery byte
if signature.len() != 65 {
return Err(ConsensusError::Other(format!("Invalid signature length: expected 65, got {}", signature.len()).into()));
}

let sig_bytes = &signature[..64];
let recovery_id = signature[64];

Expand All @@ -175,54 +179,102 @@ where
let hash = keccak256(&public_key_bytes[1..]); // Skip 0x04 prefix
let address = Address::from_slice(&hash[12..]);



Ok(address)
}

/// Calculate seal hash for BSC headers (using SealContent struct like double_sign precompile)
/// Calculate seal hash for BSC headers (matching bsc-erigon's EncodeSigHeader exactly)
fn calculate_seal_hash(&self, header: &SealedHeader) -> alloy_primitives::B256 {
use alloy_primitives::keccak256;

// Use the same approach as the double_sign precompile
// Use the exact same approach as bsc-erigon's EncodeSigHeader
const EXTRA_SEAL: usize = 65;

let chain_id = self.chain_spec.chain().id();
let extra_data = &header.extra_data();

// Extract extra data without the seal
// Extract extra data without the seal (matching bsc-erigon line 1761)
let extra_without_seal = if extra_data.len() >= EXTRA_SEAL {
&extra_data[..extra_data.len() - EXTRA_SEAL]
} else {
extra_data
};

// Create SealContent exactly like double_sign precompile
// Encode directly as slice like bsc-erigon does (NOT using SealContent struct)
// This matches bsc-erigon's EncodeSigHeader function exactly

let seal_content = crate::evm::precompiles::double_sign::SealContent {
chain_id,
parent_hash: header.parent_hash().0,
uncle_hash: header.ommers_hash().0,
coinbase: header.beneficiary().0 .0,
root: header.state_root().0,
tx_hash: header.transactions_root().0,
receipt_hash: header.receipts_root().0,
bloom: header.logs_bloom().0 .0,
difficulty: header.difficulty().clone(),
number: header.number(),
gas_limit: header.gas_limit(),
gas_used: header.gas_used(),
time: header.timestamp(),
extra: alloy_primitives::Bytes::from(extra_without_seal.to_vec()),
mix_digest: header.mix_hash().unwrap_or_default().0,
nonce: header.nonce().unwrap_or_default().0,
};
// manual field-by-field encoding
// This matches reth-bsc-trail's encode_header_with_chain_id function exactly
use alloy_rlp::Encodable;
use alloy_primitives::{bytes::BytesMut, U256};

// Use automatic RLP encoding like double_sign precompile
let encoded = alloy_rlp::encode(seal_content);
let result = keccak256(&encoded);
let mut out = BytesMut::new();


// First encode the RLP list header (like reth-bsc-trail's rlp_header function)
let mut rlp_head = alloy_rlp::Header { list: true, payload_length: 0 };

// Calculate payload length for all fields
rlp_head.payload_length += U256::from(chain_id).length();
rlp_head.payload_length += header.parent_hash().length();
rlp_head.payload_length += header.ommers_hash().length();
rlp_head.payload_length += header.beneficiary().length();
rlp_head.payload_length += header.state_root().length();
rlp_head.payload_length += header.transactions_root().length();
rlp_head.payload_length += header.receipts_root().length();
rlp_head.payload_length += header.logs_bloom().length();
rlp_head.payload_length += header.difficulty().length();
rlp_head.payload_length += U256::from(header.number()).length();
rlp_head.payload_length += header.gas_limit().length();
rlp_head.payload_length += header.gas_used().length();
rlp_head.payload_length += header.timestamp().length();
rlp_head.payload_length += extra_without_seal.length();
rlp_head.payload_length += header.mix_hash().unwrap_or_default().length();
rlp_head.payload_length += header.nonce().unwrap_or_default().length();

// Add conditional field lengths for post-4844 blocks (exactly like reth-bsc-trail)
if header.parent_beacon_block_root().is_some() &&
header.parent_beacon_block_root().unwrap() == alloy_primitives::B256::ZERO
{
rlp_head.payload_length += U256::from(header.base_fee_per_gas().unwrap_or_default()).length();
rlp_head.payload_length += header.withdrawals_root().unwrap_or_default().length();
rlp_head.payload_length += header.blob_gas_used().unwrap_or_default().length();
rlp_head.payload_length += header.excess_blob_gas().unwrap_or_default().length();
rlp_head.payload_length += header.parent_beacon_block_root().unwrap().length();
}

// Encode the RLP list header first
rlp_head.encode(&mut out);

// Then encode each field individually (exactly like reth-bsc-trail)
Encodable::encode(&U256::from(chain_id), &mut out);
Encodable::encode(&header.parent_hash(), &mut out);
Encodable::encode(&header.ommers_hash(), &mut out);
Encodable::encode(&header.beneficiary(), &mut out);
Encodable::encode(&header.state_root(), &mut out);
Encodable::encode(&header.transactions_root(), &mut out);
Encodable::encode(&header.receipts_root(), &mut out);
Encodable::encode(&header.logs_bloom(), &mut out);
Encodable::encode(&header.difficulty(), &mut out);
Encodable::encode(&U256::from(header.number()), &mut out);
Encodable::encode(&header.gas_limit(), &mut out);
Encodable::encode(&header.gas_used(), &mut out);
Encodable::encode(&header.timestamp(), &mut out);
Encodable::encode(&extra_without_seal, &mut out);
Encodable::encode(&header.mix_hash().unwrap_or_default(), &mut out);
Encodable::encode(&header.nonce().unwrap_or_default(), &mut out);

// Add conditional fields for post-4844 blocks
if header.parent_beacon_block_root().is_some() &&
header.parent_beacon_block_root().unwrap() == alloy_primitives::B256::ZERO
{
Encodable::encode(&U256::from(header.base_fee_per_gas().unwrap_or_default()), &mut out);
Encodable::encode(&header.withdrawals_root().unwrap_or_default(), &mut out);
Encodable::encode(&header.blob_gas_used().unwrap_or_default(), &mut out);
Encodable::encode(&header.excess_blob_gas().unwrap_or_default(), &mut out);
Encodable::encode(&header.parent_beacon_block_root().unwrap(), &mut out);
}

let encoded = out.to_vec();
let result = keccak256(&encoded);

result
}
Expand Down