From c288978c3a1da0ec3133dfb6dd8a4d00bca4b421 Mon Sep 17 00:00:00 2001 From: Jun Kimura Date: Sat, 13 Jan 2024 23:58:23 +0900 Subject: [PATCH] improve merkle branch depth checks for execution payload Signed-off-by: Jun Kimura --- crates/consensus/src/beacon.rs | 2 +- crates/consensus/src/bellatrix.rs | 124 +++++++++--------- crates/consensus/src/capella.rs | 118 +++++++++-------- crates/consensus/src/execution.rs | 4 +- crates/consensus/src/merkle.rs | 4 +- crates/consensus/src/types.rs | 10 +- crates/light-client-cli/src/client.rs | 14 +- crates/light-client-cli/src/state.rs | 11 +- crates/light-client-verifier/src/consensus.rs | 76 +++++++---- crates/light-client-verifier/src/updates.rs | 9 +- .../src/updates/bellatrix.rs | 13 +- 11 files changed, 219 insertions(+), 166 deletions(-) diff --git a/crates/consensus/src/beacon.rs b/crates/consensus/src/beacon.rs index f868f39..1c80d76 100644 --- a/crates/consensus/src/beacon.rs +++ b/crates/consensus/src/beacon.rs @@ -22,7 +22,7 @@ pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType([7, 0, 0, 0]); pub const PUBLIC_KEY_BYTES_LEN: usize = 48; pub const SIGNATURE_BYTES_LEN: usize = 96; -pub const BLOCK_BODY_EXECUTION_PAYLOAD_INDEX: usize = 9; +pub const BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX: usize = 9; #[derive( Clone, Debug, PartialEq, Eq, Default, SimpleSerialize, serde::Serialize, serde::Deserialize, diff --git a/crates/consensus/src/bellatrix.rs b/crates/consensus/src/bellatrix.rs index 2a4f63a..5a7947b 100644 --- a/crates/consensus/src/bellatrix.rs +++ b/crates/consensus/src/bellatrix.rs @@ -1,7 +1,7 @@ use crate::{ beacon::{ Attestation, AttesterSlashing, BeaconBlockHeader, Deposit, Eth1Data, ProposerSlashing, - Root, SignedVoluntaryExit, Slot, ValidatorIndex, + Root, SignedVoluntaryExit, Slot, ValidatorIndex, BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX, }, bls::Signature, compute::hash_tree_root, @@ -9,14 +9,17 @@ use crate::{ execution::BlockNumber, internal_prelude::*, sync_protocol::{ - SyncAggregate, SyncCommittee, CURRENT_SYNC_COMMITTEE_DEPTH, FINALIZED_ROOT_DEPTH, - NEXT_SYNC_COMMITTEE_DEPTH, + SyncAggregate, SyncCommittee, CURRENT_SYNC_COMMITTEE_DEPTH, EXECUTION_PAYLOAD_DEPTH, + FINALIZED_ROOT_DEPTH, NEXT_SYNC_COMMITTEE_DEPTH, }, types::{Address, ByteList, ByteVector, Bytes32, H256, U256, U64}, }; use ssz_rs::{Deserialize, List, Merkleized, Sized}; use ssz_rs_derive::SimpleSerialize; +/// Execution payload tree depth +pub const EXECUTION_PAYLOAD_TREE_DEPTH: usize = 4; + /// Beacon Block /// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblock #[derive( @@ -287,7 +290,7 @@ pub fn gen_execution_payload_proof< MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, >, -) -> Result<(Root, Vec), Error> { +) -> Result<(Root, [H256; EXECUTION_PAYLOAD_DEPTH]), Error> { let tree = rs_merkle::MerkleTree::::from_leaves(&[ hash_tree_root(body.randao_reveal.clone()).unwrap().0, hash_tree_root(body.eth1_data.clone()).unwrap().0, @@ -306,23 +309,25 @@ pub fn gen_execution_payload_proof< Default::default(), Default::default(), ]); - Ok(( - H256(tree.root().unwrap()), - tree.proof(&[9]) + let mut branch = [Default::default(); EXECUTION_PAYLOAD_DEPTH]; + branch.copy_from_slice( + tree.proof(&[BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX]) .proof_hashes() .into_iter() .map(|h| H256::from_slice(h)) - .collect(), - )) + .collect::>() + .as_slice(), + ); + Ok((H256(tree.root().unwrap()), branch)) } -pub fn gen_execution_payload_fields_proof< +pub fn gen_execution_payload_field_proof< const BYTES_PER_LOGS_BLOOM: usize, const MAX_EXTRA_DATA_BYTES: usize, >( payload: &ExecutionPayloadHeader, - leaf_indices: &[usize], -) -> Result<(Root, Vec), Error> { + leaf_index: usize, +) -> Result<(Root, [H256; EXECUTION_PAYLOAD_TREE_DEPTH]), Error> { let tree = rs_merkle::MerkleTree::::from_leaves(&[ payload.parent_hash.0, hash_tree_root(payload.fee_recipient.clone()).unwrap().0, @@ -341,26 +346,27 @@ pub fn gen_execution_payload_fields_proof< Default::default(), Default::default(), ]); - Ok(( - H256(tree.root().unwrap()), - tree.proof(leaf_indices) + let mut branch = [Default::default(); EXECUTION_PAYLOAD_TREE_DEPTH]; + branch.copy_from_slice( + tree.proof(&[leaf_index]) .proof_hashes() .into_iter() .map(|h| H256::from_slice(h)) - .collect(), - )) + .collect::>() + .as_slice(), + ); + Ok((H256(tree.root().unwrap()), branch)) } #[cfg(test)] mod test { use super::{ - gen_execution_payload_fields_proof, gen_execution_payload_proof, BeaconBlockHeader, + gen_execution_payload_field_proof, gen_execution_payload_proof, BeaconBlockHeader, }; - use crate::bellatrix::LightClientUpdate; - use crate::errors::Error; + use crate::beacon::BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX; + use crate::bellatrix::{LightClientUpdate, EXECUTION_PAYLOAD_TREE_DEPTH}; use crate::merkle::is_valid_merkle_branch; - use crate::sync_protocol::SyncCommittee; - use crate::{beacon::Root, compute::hash_tree_root, types::H256}; + use crate::sync_protocol::{SyncCommittee, EXECUTION_PAYLOAD_DEPTH}; use crate::{ beacon::DOMAIN_SYNC_COMMITTEE, bls::fast_aggregate_verify, @@ -371,16 +377,15 @@ mod test { context::DefaultChainContext, preset, }; + use crate::{compute::hash_tree_root, types::H256}; pub use milagro_bls::PublicKey as BLSPublicKey; - use rs_merkle::algorithms::Sha256; - use rs_merkle::MerkleProof; use ssz_rs::Merkleized; use std::fs; #[test] fn beacon_block_serialization() { use crate::execution::{ - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, }; let mut header: BeaconBlockHeader = serde_json::from_str( &fs::read_to_string("./data/goerli_bellatrix_header_4825088.json").unwrap(), @@ -410,50 +415,49 @@ mod test { assert!(is_valid_merkle_branch( H256::from_slice(payload_root.as_bytes()), &payload_proof, - 9, + EXECUTION_PAYLOAD_DEPTH as u32, + BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX as u64, block_root ) .is_ok()); - let (root, proof) = gen_execution_payload_fields_proof( - &payload_header, - &[ - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, - ], - ) - .unwrap(); - assert_eq!(root.as_bytes(), payload_root.as_bytes()); + { + let (root, proof) = gen_execution_payload_field_proof( + &payload_header, + EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, + ) + .unwrap(); + assert_eq!(root.as_bytes(), payload_root.as_bytes()); - assert!(is_valid_multiproofs_branch( - root, - &proof, - &[ - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX - ], - &[ + assert!(is_valid_merkle_branch( hash_tree_root(payload_header.state_root).unwrap().0.into(), + &proof, + EXECUTION_PAYLOAD_TREE_DEPTH as u32, + EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX as u64, + root, + ) + .is_ok()); + } + { + let (root, proof) = gen_execution_payload_field_proof( + &payload_header, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, + ) + .unwrap(); + assert_eq!(root.as_bytes(), payload_root.as_bytes()); + + assert!(is_valid_merkle_branch( hash_tree_root(payload_header.block_number) .unwrap() .0 - .into() - ] - ) - .unwrap()); - } - - fn is_valid_multiproofs_branch( - root: Root, - proof: &[H256], - leaf_indices: &[usize], - leaf_hashes: &[H256], - ) -> Result { - let proof: Vec<[u8; 32]> = proof.iter().map(|h| h.0.clone()).collect(); - let proof = MerkleProof::::new(proof); - let leaf_hashes: Vec<[u8; 32]> = leaf_hashes.iter().map(|h| h.0.clone()).collect(); - // TODO execution payload specific - Ok(proof.verify(root.0, leaf_indices, &leaf_hashes, 16)) + .into(), + &proof, + EXECUTION_PAYLOAD_TREE_DEPTH as u32, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX as u64, + root, + ) + .is_ok()); + } } #[derive(Clone, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)] diff --git a/crates/consensus/src/capella.rs b/crates/consensus/src/capella.rs index 3c21c65..07ba9e1 100644 --- a/crates/consensus/src/capella.rs +++ b/crates/consensus/src/capella.rs @@ -2,7 +2,9 @@ use crate::{ beacon::{ Attestation, AttesterSlashing, BeaconBlockHeader, Deposit, Eth1Data, Gwei, ProposerSlashing, Root, SignedVoluntaryExit, Slot, ValidatorIndex, + BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX, }, + bellatrix::EXECUTION_PAYLOAD_TREE_DEPTH, bls::{PublicKey, Signature}, compute::hash_tree_root, errors::Error, @@ -356,7 +358,7 @@ pub fn gen_execution_payload_proof< MAX_BLS_TO_EXECUTION_CHANGES, SYNC_COMMITTEE_SIZE, >, -) -> Result<(Root, Vec), Error> { +) -> Result<(Root, [H256; EXECUTION_PAYLOAD_DEPTH]), Error> { let tree = rs_merkle::MerkleTree::::from_leaves(&[ hash_tree_root(body.randao_reveal.clone()).unwrap().0, hash_tree_root(body.eth1_data.clone()).unwrap().0, @@ -377,23 +379,25 @@ pub fn gen_execution_payload_proof< Default::default(), Default::default(), ]); - Ok(( - H256(tree.root().unwrap()), - tree.proof(&[9]) + let mut branch = [Default::default(); EXECUTION_PAYLOAD_DEPTH]; + branch.copy_from_slice( + tree.proof(&[BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX]) .proof_hashes() .into_iter() .map(|h| H256::from_slice(h)) - .collect(), - )) + .collect::>() + .as_slice(), + ); + Ok((H256(tree.root().unwrap()), branch)) } -pub fn gen_execution_payload_fields_proof< +pub fn gen_execution_payload_field_proof< const BYTES_PER_LOGS_BLOOM: usize, const MAX_EXTRA_DATA_BYTES: usize, >( payload: &ExecutionPayloadHeader, - leaf_indices: &[usize], -) -> Result<(Root, Vec), Error> { + leaf_index: usize, +) -> Result<(Root, [H256; EXECUTION_PAYLOAD_TREE_DEPTH]), Error> { let tree = rs_merkle::MerkleTree::::from_leaves(&[ payload.parent_hash.0, hash_tree_root(payload.fee_recipient.clone()).unwrap().0, @@ -412,34 +416,35 @@ pub fn gen_execution_payload_fields_proof< payload.withdrawals_root.0, Default::default(), ]); - Ok(( - H256(tree.root().unwrap()), - tree.proof(leaf_indices) + let mut branch = [Default::default(); EXECUTION_PAYLOAD_TREE_DEPTH]; + branch.copy_from_slice( + tree.proof(&[leaf_index]) .proof_hashes() .into_iter() .map(|h| H256::from_slice(h)) - .collect(), - )) + .collect::>() + .as_slice(), + ); + Ok((H256(tree.root().unwrap()), branch)) } #[cfg(test)] mod test { use super::{ - gen_execution_payload_fields_proof, gen_execution_payload_proof, BeaconBlockHeader, + gen_execution_payload_field_proof, gen_execution_payload_proof, BeaconBlockHeader, }; - use rs_merkle::algorithms::Sha256; - use rs_merkle::MerkleProof; + use crate::beacon::BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX; + use crate::bellatrix::EXECUTION_PAYLOAD_TREE_DEPTH; + use crate::merkle::is_valid_merkle_branch; + use crate::sync_protocol::EXECUTION_PAYLOAD_DEPTH; + use crate::{compute::hash_tree_root, types::H256}; use ssz_rs::Merkleized; use std::fs; - use crate::errors::Error; - use crate::merkle::is_valid_merkle_branch; - use crate::{beacon::Root, compute::hash_tree_root, types::H256}; - #[test] fn beacon_block_serialization() { use crate::execution::{ - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, }; let mut header: BeaconBlockHeader = serde_json::from_str( &fs::read_to_string("./data/goerli_capella_header_5209248.json").unwrap(), @@ -469,49 +474,48 @@ mod test { assert!(is_valid_merkle_branch( H256::from_slice(payload_root.as_bytes()), &payload_proof, - 9, + EXECUTION_PAYLOAD_DEPTH as u32, + BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX as u64, block_root ) .is_ok()); - let (root, proof) = gen_execution_payload_fields_proof( - &payload_header, - &[ - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, - ], - ) - .unwrap(); - assert_eq!(root.as_bytes(), payload_root.as_bytes()); + { + let (root, proof) = gen_execution_payload_field_proof( + &payload_header, + EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, + ) + .unwrap(); + assert_eq!(root.as_bytes(), payload_root.as_bytes()); - assert!(is_valid_multiproofs_branch( - root, - &proof, - &[ - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX - ], - &[ + assert!(is_valid_merkle_branch( hash_tree_root(payload_header.state_root).unwrap().0.into(), + &proof, + EXECUTION_PAYLOAD_TREE_DEPTH as u32, + EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX as u64, + root, + ) + .is_ok()); + } + { + let (root, proof) = gen_execution_payload_field_proof( + &payload_header, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, + ) + .unwrap(); + assert_eq!(root.as_bytes(), payload_root.as_bytes()); + + assert!(is_valid_merkle_branch( hash_tree_root(payload_header.block_number) .unwrap() .0 - .into() - ] - ) - .unwrap()); - } - - fn is_valid_multiproofs_branch( - root: Root, - proof: &[H256], - leaf_indices: &[usize], - leaf_hashes: &[H256], - ) -> Result { - let proof: Vec<[u8; 32]> = proof.iter().map(|h| h.0.clone()).collect(); - let proof = MerkleProof::::new(proof); - let leaf_hashes: Vec<[u8; 32]> = leaf_hashes.iter().map(|h| h.0.clone()).collect(); - // TODO execution payload specific - Ok(proof.verify(root.0, leaf_indices, &leaf_hashes, 16)) + .into(), + &proof, + EXECUTION_PAYLOAD_TREE_DEPTH as u32, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX as u64, + root, + ) + .is_ok()); + } } } diff --git a/crates/consensus/src/execution.rs b/crates/consensus/src/execution.rs index 9eda456..56096e8 100644 --- a/crates/consensus/src/execution.rs +++ b/crates/consensus/src/execution.rs @@ -1,6 +1,6 @@ use crate::types::U64; -pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: usize = 2; -pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: usize = 6; +pub const EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX: usize = 2; +pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX: usize = 6; pub type BlockNumber = U64; diff --git a/crates/consensus/src/merkle.rs b/crates/consensus/src/merkle.rs index 44f6fd6..074e8cf 100644 --- a/crates/consensus/src/merkle.rs +++ b/crates/consensus/src/merkle.rs @@ -6,9 +6,11 @@ use sha2::{Digest, Sha256}; pub fn is_valid_merkle_branch( leaf: H256, branch: &[H256], - index: u64, + depth: u32, + leaf_index: u64, root: Root, ) -> Result<(), MerkleError> { + let index = 2u64.pow(depth) + leaf_index; let mut value = leaf.clone(); for (i, b) in branch.iter().enumerate() { if let Some(v) = 2u64.checked_pow(i as u32) { diff --git a/crates/consensus/src/types.rs b/crates/consensus/src/types.rs index b63a340..062ffdf 100644 --- a/crates/consensus/src/types.rs +++ b/crates/consensus/src/types.rs @@ -7,7 +7,15 @@ use ssz_rs::{Deserialize, List}; use ssz_rs_derive::SimpleSerialize; #[derive( - Clone, Debug, PartialEq, Eq, Default, SimpleSerialize, serde::Serialize, serde::Deserialize, + Clone, + Copy, + Debug, + PartialEq, + Eq, + Default, + SimpleSerialize, + serde::Serialize, + serde::Deserialize, )] #[serde(transparent)] pub struct H256(#[serde(with = "serde_hex")] pub [u8; 32]); diff --git a/crates/light-client-cli/src/client.rs b/crates/light-client-cli/src/client.rs index 1fe7b54..6c89092 100644 --- a/crates/light-client-cli/src/client.rs +++ b/crates/light-client-cli/src/client.rs @@ -7,11 +7,13 @@ use crate::{ use core::time::Duration; use ethereum_consensus::{ beacon::{Root, Slot}, + bellatrix::EXECUTION_PAYLOAD_TREE_DEPTH, capella::{self, LightClientUpdate}, compute::compute_sync_committee_period_at_slot, context::ChainContext, execution::{ - BlockNumber, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + BlockNumber, EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, + EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, }, sync_protocol::SyncCommitteePeriod, types::{H256, U64}, @@ -42,6 +44,8 @@ pub struct LightClient< ctx: Context, chain: Chain, verifier: CurrentNextSyncProtocolVerifier< + SYNC_COMMITTEE_SIZE, + EXECUTION_PAYLOAD_TREE_DEPTH, LightClientStore, >, genesis_time: U64, @@ -221,13 +225,13 @@ impl< let execution_update = { let execution_payload_header = update.finalized_header.execution.clone(); - let (_, state_root_branch) = capella::gen_execution_payload_fields_proof( + let (_, state_root_branch) = capella::gen_execution_payload_field_proof( &execution_payload_header, - &[EXECUTION_PAYLOAD_STATE_ROOT_INDEX], + EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, )?; - let (_, block_number_branch) = capella::gen_execution_payload_fields_proof( + let (_, block_number_branch) = capella::gen_execution_payload_field_proof( &execution_payload_header, - &[EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX], + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, )?; ExecutionUpdateInfo { state_root: execution_payload_header.state_root, diff --git a/crates/light-client-cli/src/state.rs b/crates/light-client-cli/src/state.rs index e5f26a8..0a6ea5e 100644 --- a/crates/light-client-cli/src/state.rs +++ b/crates/light-client-cli/src/state.rs @@ -1,5 +1,6 @@ use ethereum_consensus::{ beacon::{BeaconBlockHeader, Slot}, + bellatrix::EXECUTION_PAYLOAD_TREE_DEPTH, capella::{ExecutionPayloadHeader, LightClientBootstrap}, sync_protocol::SyncCommittee, types::{H256, U64}, @@ -97,17 +98,17 @@ impl< #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ExecutionUpdateInfo { pub state_root: H256, - pub state_root_branch: Vec, + pub state_root_branch: [H256; EXECUTION_PAYLOAD_TREE_DEPTH], pub block_number: U64, - pub block_number_branch: Vec, + pub block_number_branch: [H256; EXECUTION_PAYLOAD_TREE_DEPTH], } -impl ExecutionUpdate for ExecutionUpdateInfo { +impl ExecutionUpdate for ExecutionUpdateInfo { fn state_root(&self) -> H256 { self.state_root.clone() } - fn state_root_branch(&self) -> Vec { + fn state_root_branch(&self) -> [H256; EXECUTION_PAYLOAD_TREE_DEPTH] { self.state_root_branch.clone() } @@ -115,7 +116,7 @@ impl ExecutionUpdate for ExecutionUpdateInfo { self.block_number } - fn block_number_branch(&self) -> Vec { + fn block_number_branch(&self) -> [H256; EXECUTION_PAYLOAD_TREE_DEPTH] { self.block_number_branch.clone() } } diff --git a/crates/light-client-verifier/src/consensus.rs b/crates/light-client-verifier/src/consensus.rs index d975702..b8be920 100644 --- a/crates/light-client-verifier/src/consensus.rs +++ b/crates/light-client-verifier/src/consensus.rs @@ -5,7 +5,9 @@ use crate::misbehaviour::Misbehaviour; use crate::state::{NextSyncCommitteeView, SyncCommitteeView}; use crate::updates::{ConsensusUpdate, ExecutionUpdate, LightClientBootstrap}; use core::marker::PhantomData; -use ethereum_consensus::beacon::{Root, BLOCK_BODY_EXECUTION_PAYLOAD_INDEX, DOMAIN_SYNC_COMMITTEE}; +use ethereum_consensus::beacon::{ + Root, BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX, DOMAIN_SYNC_COMMITTEE, +}; use ethereum_consensus::bls::{fast_aggregate_verify, BLSPublicKey, BLSSignature}; use ethereum_consensus::compute::{ compute_domain, compute_epoch_at_slot, compute_fork_version, compute_signing_root, @@ -13,17 +15,23 @@ use ethereum_consensus::compute::{ }; use ethereum_consensus::context::ChainContext; use ethereum_consensus::execution::{ - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX, }; use ethereum_consensus::merkle::is_valid_merkle_branch; use ethereum_consensus::sync_protocol::{ - SyncCommittee, CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX, FINALIZED_ROOT_SUBTREE_INDEX, - NEXT_SYNC_COMMITTEE_SUBTREE_INDEX, + SyncCommittee, CURRENT_SYNC_COMMITTEE_DEPTH, CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX, + EXECUTION_PAYLOAD_DEPTH, FINALIZED_ROOT_DEPTH, FINALIZED_ROOT_SUBTREE_INDEX, + NEXT_SYNC_COMMITTEE_DEPTH, NEXT_SYNC_COMMITTEE_SUBTREE_INDEX, }; use ethereum_consensus::types::H256; /// SyncProtocolVerifier is a verifier of [light client sync protocol](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md) -pub trait SyncProtocolVerifier { +pub trait SyncProtocolVerifier< + const SYNC_COMMITTEE_SIZE: usize, + const EXECUTION_PAYLOAD_TREE_DEPTH: usize, + ST, +> +{ /// validates a LightClientBootstrap fn validate_boostrap>( &self, @@ -39,6 +47,7 @@ pub trait SyncProtocolVerifier { is_valid_merkle_branch( hash_tree_root(bootstrap.current_sync_committee().clone())?, &bootstrap.current_sync_committee_branch(), + CURRENT_SYNC_COMMITTEE_DEPTH as u32, CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX, bootstrap.beacon_header().state_root.clone(), ) @@ -50,7 +59,7 @@ pub trait SyncProtocolVerifier { fn validate_updates< CC: ChainContext + ConsensusVerificationContext, CU: ConsensusUpdate, - EU: ExecutionUpdate, + EU: ExecutionUpdate, >( &self, ctx: &CC, @@ -86,7 +95,8 @@ pub trait SyncProtocolVerifier { is_valid_merkle_branch( update.finalized_execution_root(), &update.finalized_execution_branch(), - BLOCK_BODY_EXECUTION_PAYLOAD_INDEX as u64, + EXECUTION_PAYLOAD_DEPTH as u32, + BLOCK_BODY_EXECUTION_PAYLOAD_LEAF_INDEX as u64, update.finalized_beacon_header().body_root.clone(), ) .map_err(Error::InvalidFinalizedExecutionPayload)?; @@ -94,7 +104,7 @@ pub trait SyncProtocolVerifier { } /// validate an execution update with trusted/verified beacon block body - fn validate_execution_update( + fn validate_execution_update>( &self, trusted_execution_root: Root, update: &EU, @@ -102,7 +112,8 @@ pub trait SyncProtocolVerifier { is_valid_merkle_branch( hash_tree_root(update.state_root()).unwrap().0.into(), &update.state_root_branch(), - EXECUTION_PAYLOAD_STATE_ROOT_INDEX as u64, + EXECUTION_PAYLOAD_TREE_DEPTH as u32, + EXECUTION_PAYLOAD_STATE_ROOT_LEAF_INDEX as u64, trusted_execution_root.clone(), ) .map_err(Error::InvalidExecutionStateRootMerkleBranch)?; @@ -110,7 +121,8 @@ pub trait SyncProtocolVerifier { is_valid_merkle_branch( hash_tree_root(update.block_number()).unwrap().0.into(), &update.block_number_branch(), - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as u64, + EXECUTION_PAYLOAD_TREE_DEPTH as u32, + EXECUTION_PAYLOAD_BLOCK_NUMBER_LEAF_INDEX as u64, trusted_execution_root, ) .map_err(Error::InvalidExecutionBlockNumberMerkleBranch)?; @@ -154,10 +166,18 @@ pub trait SyncProtocolVerifier { } #[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct CurrentNextSyncProtocolVerifier(PhantomData); - -impl> - SyncProtocolVerifier for CurrentNextSyncProtocolVerifier +pub struct CurrentNextSyncProtocolVerifier< + const SYNC_COMMITTEE_SIZE: usize, + const EXECUTION_PAYLOAD_TREE_DEPTH: usize, + ST: SyncCommitteeView, +>(PhantomData); + +impl< + const SYNC_COMMITTEE_SIZE: usize, + const EXECUTION_PAYLOAD_TREE_DEPTH: usize, + ST: SyncCommitteeView, + > SyncProtocolVerifier + for CurrentNextSyncProtocolVerifier { fn ensure_relevant_update>( &self, @@ -245,11 +265,17 @@ impl - SyncProtocolVerifier> - for NextSyncProtocolVerifier +pub struct NextSyncProtocolVerifier< + const SYNC_COMMITTEE_SIZE: usize, + const EXECUTION_PAYLOAD_TREE_DEPTH: usize, +>; + +impl + SyncProtocolVerifier< + SYNC_COMMITTEE_SIZE, + EXECUTION_PAYLOAD_TREE_DEPTH, + NextSyncCommitteeView, + > for NextSyncProtocolVerifier { fn ensure_relevant_update>( &self, @@ -365,6 +391,7 @@ pub fn verify_merkle_branches_with_attested_header< is_valid_merkle_branch( finalized_root, &consensus_update.finalized_beacon_header_branch(), + FINALIZED_ROOT_DEPTH as u32, FINALIZED_ROOT_SUBTREE_INDEX, consensus_update.attested_beacon_header().state_root.clone(), ) @@ -373,10 +400,8 @@ pub fn verify_merkle_branches_with_attested_header< if let Some(update_next_sync_committee) = consensus_update.next_sync_committee() { is_valid_merkle_branch( hash_tree_root(update_next_sync_committee.clone())?, - consensus_update - .next_sync_committee_branch() - .unwrap() - .as_ref(), + &consensus_update.next_sync_committee_branch().unwrap(), + NEXT_SYNC_COMMITTEE_DEPTH as u32, NEXT_SYNC_COMMITTEE_SUBTREE_INDEX, consensus_update.attested_beacon_header().state_root.clone(), ) @@ -412,6 +437,7 @@ mod tests_bellatrix { }; use ethereum_consensus::{ beacon::Version, + bellatrix::EXECUTION_PAYLOAD_TREE_DEPTH, bls::aggreate_public_key, config::{minimal, Config}, fork::{ForkParameter, ForkParameters}, @@ -425,6 +451,8 @@ mod tests_bellatrix { #[test] fn test_bootstrap() { let verifier = CurrentNextSyncProtocolVerifier::< + { preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }, + EXECUTION_PAYLOAD_TREE_DEPTH, MockStore<{ preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }>, >::default(); let path = format!("{}/initial_state.json", TEST_DATA_DIR); @@ -462,6 +490,8 @@ mod tests_bellatrix { #[test] fn test_verification() { let verifier = CurrentNextSyncProtocolVerifier::< + { preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }, + EXECUTION_PAYLOAD_TREE_DEPTH, MockStore<{ preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }>, >::default(); diff --git a/crates/light-client-verifier/src/updates.rs b/crates/light-client-verifier/src/updates.rs index d563fc2..33ae44c 100644 --- a/crates/light-client-verifier/src/updates.rs +++ b/crates/light-client-verifier/src/updates.rs @@ -1,6 +1,5 @@ use crate::context::ConsensusVerificationContext; use crate::errors::Error; -use crate::internal_prelude::*; use ethereum_consensus::{ beacon::{BeaconBlockHeader, Slot}, sync_protocol::{ @@ -86,11 +85,13 @@ pub trait ConsensusUpdate: // TODO multiproof support /// ExecutionUpdate is an update info of the execution layer -pub trait ExecutionUpdate: core::fmt::Debug + Clone + PartialEq + Eq { +pub trait ExecutionUpdate: + core::fmt::Debug + Clone + PartialEq + Eq +{ fn state_root(&self) -> H256; - fn state_root_branch(&self) -> Vec; + fn state_root_branch(&self) -> [H256; EXECUTION_PAYLOAD_TREE_DEPTH]; fn block_number(&self) -> U64; - fn block_number_branch(&self) -> Vec; + fn block_number_branch(&self) -> [H256; EXECUTION_PAYLOAD_TREE_DEPTH]; fn validate_basic(&self) -> Result<(), Error> { Ok(()) diff --git a/crates/light-client-verifier/src/updates/bellatrix.rs b/crates/light-client-verifier/src/updates/bellatrix.rs index bcc4e89..3c6a009 100644 --- a/crates/light-client-verifier/src/updates/bellatrix.rs +++ b/crates/light-client-verifier/src/updates/bellatrix.rs @@ -1,8 +1,7 @@ use super::{ConsensusUpdate, ExecutionUpdate, LightClientBootstrap}; -use crate::internal_prelude::*; use ethereum_consensus::{ beacon::{BeaconBlockHeader, Slot}, - bellatrix::LightClientUpdate, + bellatrix::{LightClientUpdate, EXECUTION_PAYLOAD_TREE_DEPTH}, sync_protocol::{ SyncAggregate, SyncCommittee, CURRENT_SYNC_COMMITTEE_DEPTH, EXECUTION_PAYLOAD_DEPTH, FINALIZED_ROOT_DEPTH, NEXT_SYNC_COMMITTEE_DEPTH, @@ -78,17 +77,17 @@ impl ConsensusUpdate #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ExecutionUpdateInfo { pub state_root: H256, - pub state_root_branch: Vec, + pub state_root_branch: [H256; EXECUTION_PAYLOAD_TREE_DEPTH], pub block_number: U64, - pub block_number_branch: Vec, + pub block_number_branch: [H256; EXECUTION_PAYLOAD_TREE_DEPTH], } -impl ExecutionUpdate for ExecutionUpdateInfo { +impl ExecutionUpdate for ExecutionUpdateInfo { fn state_root(&self) -> H256 { self.state_root.clone() } - fn state_root_branch(&self) -> Vec { + fn state_root_branch(&self) -> [H256; EXECUTION_PAYLOAD_TREE_DEPTH] { self.state_root_branch.clone() } @@ -96,7 +95,7 @@ impl ExecutionUpdate for ExecutionUpdateInfo { self.block_number } - fn block_number_branch(&self) -> Vec { + fn block_number_branch(&self) -> [H256; EXECUTION_PAYLOAD_TREE_DEPTH] { self.block_number_branch.clone() } }