From f479beb87e8959a09d88a281686f7717108fbd35 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 8 Mar 2019 09:26:03 +1100 Subject: [PATCH] Implement deposit merkle root verification. It is currently disabled, but it's there for later. --- eth2/state_processing/Cargo.toml | 2 + .../src/per_block_processing.rs | 8 ++- .../src/per_block_processing/errors.rs | 3 ++ .../per_block_processing/verify_deposit.rs | 54 +++++++++++++++++-- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/eth2/state_processing/Cargo.toml b/eth2/state_processing/Cargo.toml index b673996ae..c51ce8372 100644 --- a/eth2/state_processing/Cargo.toml +++ b/eth2/state_processing/Cargo.toml @@ -18,6 +18,8 @@ hashing = { path = "../utils/hashing" } int_to_bytes = { path = "../utils/int_to_bytes" } integer-sqrt = "0.1" log = "0.4" +merkle_proof = { path = "../utils/merkle_proof" } ssz = { path = "../utils/ssz" } +ssz_derive = { path = "../utils/ssz_derive" } types = { path = "../types" } rayon = "1.0" diff --git a/eth2/state_processing/src/per_block_processing.rs b/eth2/state_processing/src/per_block_processing.rs index 61b12bedb..1ab1eed71 100644 --- a/eth2/state_processing/src/per_block_processing.rs +++ b/eth2/state_processing/src/per_block_processing.rs @@ -20,6 +20,11 @@ mod verify_proposer_slashing; mod verify_slashable_attestation; mod verify_transfer; +// Set to `true` to check the merkle proof that a deposit is in the eth1 deposit root. +// +// Presently disabled to make testing easier. +const VERIFY_DEPOSIT_MERKLE_PROOFS: bool = false; + /// Updates the state for a new block, whilst validating that the block is valid. /// /// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise @@ -309,7 +314,8 @@ pub fn process_deposits( Invalid::MaxDepositsExceeded ); for (i, deposit) in deposits.iter().enumerate() { - verify_deposit(state, deposit, spec).map_err(|e| e.into_with_index(i))?; + verify_deposit(state, deposit, VERIFY_DEPOSIT_MERKLE_PROOFS, spec) + .map_err(|e| e.into_with_index(i))?; state .process_deposit( diff --git a/eth2/state_processing/src/per_block_processing/errors.rs b/eth2/state_processing/src/per_block_processing/errors.rs index 36b0d4942..b97d8bacc 100644 --- a/eth2/state_processing/src/per_block_processing/errors.rs +++ b/eth2/state_processing/src/per_block_processing/errors.rs @@ -292,6 +292,9 @@ pub enum DepositInvalid { /// /// (state_index, deposit_index) BadIndex(u64, u64), + /// The specified `branch` and `index` did not form a valid proof that the deposit is included + /// in the eth1 deposit root. + BadMerkleProof, } impl_into_with_index_without_beacon_error!(DepositValidationError, DepositInvalid); diff --git a/eth2/state_processing/src/per_block_processing/verify_deposit.rs b/eth2/state_processing/src/per_block_processing/verify_deposit.rs index 702c6b9e0..69dae1533 100644 --- a/eth2/state_processing/src/per_block_processing/verify_deposit.rs +++ b/eth2/state_processing/src/per_block_processing/verify_deposit.rs @@ -1,4 +1,8 @@ use super::errors::{DepositInvalid as Invalid, DepositValidationError as Error}; +use hashing::hash; +use merkle_proof::verify_merkle_proof; +use ssz::ssz_encode; +use ssz_derive::Encode; use types::*; /// Indicates if a `Deposit` is valid to be included in a block in the current epoch of the given @@ -12,16 +16,58 @@ use types::*; pub fn verify_deposit( state: &BeaconState, deposit: &Deposit, - _spec: &ChainSpec, + verify_merkle_branch: bool, + spec: &ChainSpec, ) -> Result<(), Error> { - // TODO: verify serialized deposit data. - verify!( deposit.index == state.deposit_index, Invalid::BadIndex(state.deposit_index, deposit.index) ); - // TODO: verify merkle branch. + if verify_merkle_branch { + verify!( + verify_deposit_merkle_proof(state, deposit, spec), + Invalid::BadMerkleProof + ); + } Ok(()) } + +/// Verify that a deposit is included in the state's eth1 deposit root. +/// +/// Spec v0.4.0 +fn verify_deposit_merkle_proof(state: &BeaconState, deposit: &Deposit, spec: &ChainSpec) -> bool { + let leaf = hash(&get_serialized_deposit_data(deposit)); + verify_merkle_proof( + Hash256::from_slice(&leaf), + &deposit.branch, + spec.deposit_contract_tree_depth as usize, + deposit.index as usize, + state.latest_eth1_data.deposit_root, + ) +} + +/// Helper struct for easily getting the serialized data generated by the deposit contract. +/// +/// Spec v0.4.0 +#[derive(Encode)] +struct SerializedDepositData { + amount: u64, + timestamp: u64, + input: DepositInput, +} + +/// Return the serialized data generated by the deposit contract that is used to generate the +/// merkle proof. +/// +/// Spec v0.4.0 +fn get_serialized_deposit_data(deposit: &Deposit) -> Vec { + let serialized_deposit_data = SerializedDepositData { + amount: deposit.deposit_data.amount, + timestamp: deposit.deposit_data.timestamp, + input: deposit.deposit_data.deposit_input.clone(), + }; + + ssz_encode(&serialized_deposit_data) +}