diff --git a/eth2/state_processing/src/per_block_processing.rs b/eth2/state_processing/src/per_block_processing.rs index b9006d6f9..573513819 100644 --- a/eth2/state_processing/src/per_block_processing.rs +++ b/eth2/state_processing/src/per_block_processing.rs @@ -64,161 +64,22 @@ fn per_block_processing_signature_optional( // Verify that `block.slot == state.slot`. verify!(block.slot == state.slot, Invalid::StateSlotMismatch); - // Get the epoch for future ergonomics. - let epoch = block.slot.epoch(spec.slots_per_epoch); - // Ensure the current epoch cache is built. state.build_epoch_cache(RelativeEpoch::Current, spec)?; - // Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. - let block_proposer = - &state.validator_registry[state.get_beacon_proposer_index(block.slot, spec)?]; - - // Block signature if should_verify_block_signature { - verify!( - verify_block_signature(&block, &block_proposer, &state.fork, spec,), - Invalid::BadSignature - ); + verify_block_signature(&state, &block, &spec)?; } + process_randao(&mut state, &block, &spec)?; + process_eth1_data(&mut state, &block.eth1_data)?; + process_proposer_slashings(&mut state, &block.body.proposer_slashings[..], spec)?; + process_attester_slashings(&mut state, &block.body.attester_slashings[..], spec)?; + process_attestations(&mut state, &block.body.attestations[..], spec)?; + process_deposits(&mut state, &block.body.deposits[..], spec)?; + process_exits(&mut state, &block.body.voluntary_exits[..], spec)?; + process_transfers(&mut state, &block.body.transfers[..], spec)?; - // Randao - - // Verify that `bls_verify(pubkey=proposer.pubkey, - // message_hash=hash_tree_root(get_current_epoch(state)), signature=block.randao_reveal, - // domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. - verify!( - block.randao_reveal.verify( - &state.current_epoch(spec).hash_tree_root()[..], - spec.get_domain(epoch, Domain::Randao, &state.fork), - &block_proposer.pubkey - ), - Invalid::BadRandaoSignature - ); - - // Update the state's RANDAO mix with the one revealed in the block. - update_randao(&mut state, &block.randao_reveal, spec)?; - - // Eth1 Data - - // Either increment the eth1_data vote count, or add a new eth1_data. - let matching_eth1_vote_index = state - .eth1_data_votes - .iter() - .position(|vote| vote.eth1_data == block.eth1_data); - if let Some(index) = matching_eth1_vote_index { - state.eth1_data_votes[index].vote_count += 1; - } else { - state.eth1_data_votes.push(Eth1DataVote { - eth1_data: block.eth1_data.clone(), - vote_count: 1, - }); - } - - //Proposer slashings - - verify!( - block.body.proposer_slashings.len() as u64 <= spec.max_proposer_slashings, - Invalid::MaxProposerSlashingsExceeded - ); - for (i, proposer_slashing) in block.body.proposer_slashings.iter().enumerate() { - verify_proposer_slashing(proposer_slashing, &state, spec) - .map_err(|e| e.into_with_index(i))?; - state.slash_validator(proposer_slashing.proposer_index as usize, spec)?; - } - - // Attester Slashings - - verify!( - block.body.attester_slashings.len() as u64 <= spec.max_attester_slashings, - Invalid::MaxAttesterSlashingsExceed - ); - for (i, attester_slashing) in block.body.attester_slashings.iter().enumerate() { - let slashable_indices = verify_attester_slashing(&state, &attester_slashing, spec) - .map_err(|e| e.into_with_index(i))?; - for i in slashable_indices { - state.slash_validator(i as usize, spec)?; - } - } - - // Attestations - - verify!( - block.body.attestations.len() as u64 <= spec.max_attestations, - Invalid::MaxAttestationsExceeded - ); - for (i, attestation) in block.body.attestations.iter().enumerate() { - // Build the previous epoch cache only if required by an attestation. - if attestation.data.slot.epoch(spec.slots_per_epoch) == state.previous_epoch(spec) { - state.build_epoch_cache(RelativeEpoch::Previous, spec)?; - } - - validate_attestation(&mut state, attestation, spec).map_err(|e| e.into_with_index(i))?; - - let pending_attestation = PendingAttestation { - data: attestation.data.clone(), - aggregation_bitfield: attestation.aggregation_bitfield.clone(), - custody_bitfield: attestation.custody_bitfield.clone(), - inclusion_slot: state.slot, - }; - state.latest_attestations.push(pending_attestation); - } - - // Deposits - - verify!( - block.body.deposits.len() as u64 <= spec.max_deposits, - Invalid::MaxDepositsExceeded - ); - for (i, deposit) in block.body.deposits.iter().enumerate() { - verify_deposit(&mut state, deposit, spec).map_err(|e| e.into_with_index(i))?; - - state - .process_deposit( - deposit.deposit_data.deposit_input.pubkey.clone(), - deposit.deposit_data.amount, - deposit - .deposit_data - .deposit_input - .proof_of_possession - .clone(), - deposit.deposit_data.deposit_input.withdrawal_credentials, - None, - spec, - ) - .map_err(|_| Error::Invalid(Invalid::DepositProcessingFailed(i)))?; - - state.deposit_index += 1; - } - - // Exits - - verify!( - block.body.voluntary_exits.len() as u64 <= spec.max_voluntary_exits, - Invalid::MaxExitsExceeded - ); - for (i, exit) in block.body.voluntary_exits.iter().enumerate() { - verify_exit(&state, exit, spec).map_err(|e| e.into_with_index(i))?; - - state.initiate_validator_exit(exit.validator_index as usize); - } - - // Transfers - verify!( - block.body.transfers.len() as u64 <= spec.max_transfers, - Invalid::MaxTransfersExceed - ); - for (i, transfer) in block.body.transfers.iter().enumerate() { - verify_transfer(&state, transfer, spec).map_err(|e| e.into_with_index(i))?; - - let block_proposer = state.get_beacon_proposer_index(state.slot, spec)?; - - state.validator_balances[transfer.from as usize] -= transfer.amount + transfer.fee; - state.validator_balances[transfer.to as usize] += transfer.amount + transfer.fee; - state.validator_balances[block_proposer as usize] += transfer.fee; - } - - debug!("State transition complete."); + debug!("per_block_processing complete."); Ok(()) } @@ -227,13 +88,13 @@ fn per_block_processing_signature_optional( /// /// Spec v0.4.0 pub fn verify_block_signature( + state: &BeaconState, block: &BeaconBlock, - block_proposer: &Validator, - fork: &Fork, spec: &ChainSpec, -) -> bool { - // Let proposal = `Proposal(block.slot, BEACON_CHAIN_SHARD_NUMBER, signed_root(block, - // "signature"), block.signature)`. +) -> Result<(), Error> { + let block_proposer = + &state.validator_registry[state.get_beacon_proposer_index(block.slot, spec)?]; + let proposal = Proposal { slot: block.slot, shard: spec.beacon_chain_shard_number, @@ -243,14 +104,73 @@ pub fn verify_block_signature( let domain = spec.get_domain( block.slot.epoch(spec.slots_per_epoch), Domain::Proposal, - fork, + &state.fork, ); - // Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposal, - // "signature"), signature=proposal.signature, domain=get_domain(state.fork, - // get_current_epoch(state), DOMAIN_PROPOSAL))`. - proposal - .signature - .verify(&proposal.signed_root()[..], domain, &block_proposer.pubkey) + + verify!( + proposal + .signature + .verify(&proposal.signed_root()[..], domain, &block_proposer.pubkey), + Invalid::BadSignature + ); + + Ok(()) +} + +/// Verifies the `randao_reveal` against the block's proposer pubkey and updates +/// `state.latest_randao_mixes`. +/// +/// Spec v0.4.0 +pub fn process_randao( + state: &mut BeaconState, + block: &BeaconBlock, + spec: &ChainSpec, +) -> Result<(), Error> { + // Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. + let block_proposer = + &state.validator_registry[state.get_beacon_proposer_index(block.slot, spec)?]; + + // Verify that `bls_verify(pubkey=proposer.pubkey, + // message_hash=hash_tree_root(get_current_epoch(state)), signature=block.randao_reveal, + // domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. + verify!( + block.randao_reveal.verify( + &state.current_epoch(spec).hash_tree_root()[..], + spec.get_domain( + block.slot.epoch(spec.slots_per_epoch), + Domain::Randao, + &state.fork + ), + &block_proposer.pubkey + ), + Invalid::BadRandaoSignature + ); + + // Update the state's RANDAO mix with the one revealed in the block. + update_randao(state, &block.randao_reveal, spec)?; + + Ok(()) +} + +/// Update the `state.eth1_data_votes` based upon the `eth1_data` provided. +/// +/// Spec v0.4.0 +pub fn process_eth1_data(state: &mut BeaconState, eth1_data: &Eth1Data) -> Result<(), Error> { + // Either increment the eth1_data vote count, or add a new eth1_data. + let matching_eth1_vote_index = state + .eth1_data_votes + .iter() + .position(|vote| vote.eth1_data == *eth1_data); + if let Some(index) = matching_eth1_vote_index { + state.eth1_data_votes[index].vote_count += 1; + } else { + state.eth1_data_votes.push(Eth1DataVote { + eth1_data: eth1_data.clone(), + vote_count: 1, + }); + } + + Ok(()) } /// Updates the present randao mix. @@ -286,3 +206,179 @@ pub fn update_randao( Err(BeaconStateError::InsufficientRandaoMixes) } } + +/// Validates each `ProposerSlashing` and updates the state, short-circuiting on an invalid object. +/// +/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns +/// an `Err` describing the invalid object or cause of failure. +/// +/// Spec v0.4.0 +pub fn process_proposer_slashings( + state: &mut BeaconState, + proposer_slashings: &[ProposerSlashing], + spec: &ChainSpec, +) -> Result<(), Error> { + verify!( + proposer_slashings.len() as u64 <= spec.max_proposer_slashings, + Invalid::MaxProposerSlashingsExceeded + ); + for (i, proposer_slashing) in proposer_slashings.iter().enumerate() { + verify_proposer_slashing(proposer_slashing, &state, spec) + .map_err(|e| e.into_with_index(i))?; + state.slash_validator(proposer_slashing.proposer_index as usize, spec)?; + } + + Ok(()) +} + +/// Validates each `AttesterSlsashing` and updates the state, short-circuiting on an invalid object. +/// +/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns +/// an `Err` describing the invalid object or cause of failure. +/// +/// Spec v0.4.0 +pub fn process_attester_slashings( + state: &mut BeaconState, + attester_slashings: &[AttesterSlashing], + spec: &ChainSpec, +) -> Result<(), Error> { + verify!( + attester_slashings.len() as u64 <= spec.max_attester_slashings, + Invalid::MaxAttesterSlashingsExceed + ); + for (i, attester_slashing) in attester_slashings.iter().enumerate() { + let slashable_indices = verify_attester_slashing(&state, &attester_slashing, spec) + .map_err(|e| e.into_with_index(i))?; + for i in slashable_indices { + state.slash_validator(i as usize, spec)?; + } + } + + Ok(()) +} + +/// Validates each `Attestation` and updates the state, short-circuiting on an invalid object. +/// +/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns +/// an `Err` describing the invalid object or cause of failure. +/// +/// Spec v0.4.0 +pub fn process_attestations( + state: &mut BeaconState, + attestations: &[Attestation], + spec: &ChainSpec, +) -> Result<(), Error> { + verify!( + attestations.len() as u64 <= spec.max_attestations, + Invalid::MaxAttestationsExceeded + ); + for (i, attestation) in attestations.iter().enumerate() { + // Build the previous epoch cache only if required by an attestation. + if attestation.data.slot.epoch(spec.slots_per_epoch) == state.previous_epoch(spec) { + state.build_epoch_cache(RelativeEpoch::Previous, spec)?; + } + + validate_attestation(state, attestation, spec).map_err(|e| e.into_with_index(i))?; + + let pending_attestation = PendingAttestation { + data: attestation.data.clone(), + aggregation_bitfield: attestation.aggregation_bitfield.clone(), + custody_bitfield: attestation.custody_bitfield.clone(), + inclusion_slot: state.slot, + }; + state.latest_attestations.push(pending_attestation); + } + + Ok(()) +} + +/// Validates each `Deposit` and updates the state, short-circuiting on an invalid object. +/// +/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns +/// an `Err` describing the invalid object or cause of failure. +/// +/// Spec v0.4.0 +pub fn process_deposits( + state: &mut BeaconState, + deposits: &[Deposit], + spec: &ChainSpec, +) -> Result<(), Error> { + verify!( + deposits.len() as u64 <= spec.max_deposits, + Invalid::MaxDepositsExceeded + ); + for (i, deposit) in deposits.iter().enumerate() { + verify_deposit(state, deposit, spec).map_err(|e| e.into_with_index(i))?; + + state + .process_deposit( + deposit.deposit_data.deposit_input.pubkey.clone(), + deposit.deposit_data.amount, + deposit + .deposit_data + .deposit_input + .proof_of_possession + .clone(), + deposit.deposit_data.deposit_input.withdrawal_credentials, + None, + spec, + ) + .map_err(|_| Error::Invalid(Invalid::DepositProcessingFailed(i)))?; + + state.deposit_index += 1; + } + + Ok(()) +} + +/// Validates each `Exit` and updates the state, short-circuiting on an invalid object. +/// +/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns +/// an `Err` describing the invalid object or cause of failure. +/// +/// Spec v0.4.0 +pub fn process_exits( + state: &mut BeaconState, + voluntary_exits: &[VoluntaryExit], + spec: &ChainSpec, +) -> Result<(), Error> { + verify!( + voluntary_exits.len() as u64 <= spec.max_voluntary_exits, + Invalid::MaxExitsExceeded + ); + for (i, exit) in voluntary_exits.iter().enumerate() { + verify_exit(&state, exit, spec).map_err(|e| e.into_with_index(i))?; + + state.initiate_validator_exit(exit.validator_index as usize); + } + + Ok(()) +} + +/// Validates each `Transfer` and updates the state, short-circuiting on an invalid object. +/// +/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns +/// an `Err` describing the invalid object or cause of failure. +/// +/// Spec v0.4.0 +pub fn process_transfers( + state: &mut BeaconState, + transfers: &[Transfer], + spec: &ChainSpec, +) -> Result<(), Error> { + verify!( + transfers.len() as u64 <= spec.max_transfers, + Invalid::MaxTransfersExceed + ); + for (i, transfer) in transfers.iter().enumerate() { + verify_transfer(&state, transfer, spec).map_err(|e| e.into_with_index(i))?; + + let block_proposer = state.get_beacon_proposer_index(state.slot, spec)?; + + state.validator_balances[transfer.from as usize] -= transfer.amount + transfer.fee; + state.validator_balances[transfer.to as usize] += transfer.amount + transfer.fee; + state.validator_balances[block_proposer as usize] += transfer.fee; + } + + Ok(()) +}