Fix bugs in deposit processing

This commit is contained in:
Paul Hauner 2019-05-22 18:54:26 +10:00
parent 14d879d75f
commit 892d891977
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
3 changed files with 33 additions and 43 deletions

View File

@ -10,7 +10,10 @@ pub use validate_attestation::{
validate_attestation, validate_attestation_time_independent_only, validate_attestation, validate_attestation_time_independent_only,
validate_attestation_without_signature, validate_attestation_without_signature,
}; };
pub use verify_deposit::{get_existing_validator_index, verify_deposit, verify_deposit_index}; pub use verify_deposit::{
get_existing_validator_index, verify_deposit_index, verify_deposit_merkle_proof,
verify_deposit_signature,
};
pub use verify_exit::{verify_exit, verify_exit_time_independent_only}; pub use verify_exit::{verify_exit, verify_exit_time_independent_only};
pub use verify_indexed_attestation::{ pub use verify_indexed_attestation::{
verify_indexed_attestation, verify_indexed_attestation_without_signature, verify_indexed_attestation, verify_indexed_attestation_without_signature,
@ -28,9 +31,6 @@ mod verify_indexed_attestation;
mod verify_proposer_slashing; mod verify_proposer_slashing;
mod verify_transfer; mod verify_transfer;
// Set to `true` to check the merkle proof that a deposit is in the eth1 deposit root.
const VERIFY_DEPOSIT_MERKLE_PROOFS: bool = true;
/// Updates the state for a new block, whilst validating that the block is valid. /// 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 /// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise
@ -371,14 +371,15 @@ pub fn process_deposits<T: EthSpec>(
.par_iter() .par_iter()
.enumerate() .enumerate()
.try_for_each(|(i, deposit)| { .try_for_each(|(i, deposit)| {
verify_deposit(state, deposit, VERIFY_DEPOSIT_MERKLE_PROOFS, spec) verify_deposit_merkle_proof(state, deposit, spec).map_err(|e| e.into_with_index(i))
.map_err(|e| e.into_with_index(i))
})?; })?;
// Check `state.deposit_index` and update the state in series. // Check `state.deposit_index` and update the state in series.
for (i, deposit) in deposits.iter().enumerate() { for (i, deposit) in deposits.iter().enumerate() {
verify_deposit_index(state, deposit).map_err(|e| e.into_with_index(i))?; verify_deposit_index(state, deposit).map_err(|e| e.into_with_index(i))?;
state.deposit_index += 1;
// Ensure the state's pubkey cache is fully up-to-date, it will be used to check to see if the // Ensure the state's pubkey cache is fully up-to-date, it will be used to check to see if the
// depositing validator already exists in the registry. // depositing validator already exists in the registry.
state.update_pubkey_cache()?; state.update_pubkey_cache()?;
@ -396,6 +397,12 @@ pub fn process_deposits<T: EthSpec>(
// Update the existing validator balance. // Update the existing validator balance.
safe_add_assign!(state.balances[index as usize], amount); safe_add_assign!(state.balances[index as usize], amount);
} else { } else {
// The signature should be checked for new validators. Return early for a bad
// signature.
if verify_deposit_signature(state, deposit, spec).is_err() {
return Ok(());
}
// Create a new validator. // Create a new validator.
let validator = Validator { let validator = Validator {
pubkey: deposit.data.pubkey.clone(), pubkey: deposit.data.pubkey.clone(),
@ -413,8 +420,6 @@ pub fn process_deposits<T: EthSpec>(
state.validator_registry.push(validator); state.validator_registry.push(validator);
state.balances.push(deposit.data.amount); state.balances.push(deposit.data.amount);
} }
state.deposit_index += 1;
} }
Ok(()) Ok(())

View File

@ -321,8 +321,8 @@ pub enum DepositValidationError {
pub enum DepositInvalid { pub enum DepositInvalid {
/// The deposit index does not match the state index. /// The deposit index does not match the state index.
BadIndex { state: u64, deposit: u64 }, BadIndex { state: u64, deposit: u64 },
/// The proof-of-possession does not match the given pubkey. /// The signature (proof-of-possession) does not match the given pubkey.
BadProofOfPossession, BadSignature,
/// The withdrawal credentials for the depositing validator did not match the withdrawal /// The withdrawal credentials for the depositing validator did not match the withdrawal
/// credentials of an existing validator with the same public key. /// credentials of an existing validator with the same public key.
BadWithdrawalCredentials, BadWithdrawalCredentials,

View File

@ -1,44 +1,23 @@
use super::errors::{DepositInvalid as Invalid, DepositValidationError as Error}; use super::errors::{DepositInvalid as Invalid, DepositValidationError as Error};
use hashing::hash;
use merkle_proof::verify_merkle_proof; use merkle_proof::verify_merkle_proof;
use ssz::ssz_encode;
use ssz_derive::Encode;
use tree_hash::{SignedRoot, TreeHash}; use tree_hash::{SignedRoot, TreeHash};
use types::*; use types::*;
/// Indicates if a `Deposit` is valid to be included in a block in the current epoch of the given /// Verify `Deposit.pubkey` signed `Deposit.signature`.
/// state.
///
/// Returns `Ok(())` if the `Deposit` is valid, otherwise indicates the reason for invalidity.
///
/// This function _does not_ check `state.deposit_index` so this function may be run in parallel.
/// See the `verify_deposit_index` function for this.
///
/// Note: this function is incomplete.
/// ///
/// Spec v0.6.1 /// Spec v0.6.1
pub fn verify_deposit<T: EthSpec>( pub fn verify_deposit_signature<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
deposit: &Deposit, deposit: &Deposit,
verify_merkle_branch: bool,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), Error> { ) -> Result<(), Error> {
if verify_merkle_branch {
verify!(
verify_deposit_merkle_proof(state, deposit, spec),
Invalid::BadMerkleProof
);
}
// NOTE: proof of possession should only be verified when the validator
// is not already part of the registry
verify!( verify!(
deposit.data.signature.verify( deposit.data.signature.verify(
&deposit.data.signed_root(), &deposit.data.signed_root(),
spec.get_domain(state.current_epoch(), Domain::Deposit, &state.fork), spec.get_domain(state.current_epoch(), Domain::Deposit, &state.fork),
&deposit.data.pubkey, &deposit.data.pubkey,
), ),
Invalid::BadProofOfPossession Invalid::BadSignature
); );
Ok(()) Ok(())
@ -91,17 +70,23 @@ pub fn get_existing_validator_index<T: EthSpec>(
/// Verify that a deposit is included in the state's eth1 deposit root. /// Verify that a deposit is included in the state's eth1 deposit root.
/// ///
/// Spec v0.6.1 /// Spec v0.6.1
fn verify_deposit_merkle_proof<T: EthSpec>( pub fn verify_deposit_merkle_proof<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,
deposit: &Deposit, deposit: &Deposit,
spec: &ChainSpec, spec: &ChainSpec,
) -> bool { ) -> Result<(), Error> {
let leaf = deposit.data.tree_hash_root(); let leaf = deposit.data.tree_hash_root();
verify_merkle_proof(
Hash256::from_slice(&leaf), verify!(
&deposit.proof[..], verify_merkle_proof(
spec.deposit_contract_tree_depth as usize, Hash256::from_slice(&leaf),
deposit.index as usize, &deposit.proof[..],
state.latest_eth1_data.deposit_root, spec.deposit_contract_tree_depth as usize,
) deposit.index as usize,
state.latest_eth1_data.deposit_root,
),
Invalid::BadMerkleProof
);
Ok(())
} }