use crate::VerifySignatures; use crate::per_block_processing::compute_timestamp_at_slot; use safe_arith::ArithError; use tree_hash::TreeHash; use types::{ BeaconState, BeaconStateError, BuilderIndex, ChainSpec, EthSpec, ExecutionBlockHash, Hash256, SignedExecutionPayloadEnvelope, Slot, }; macro_rules! envelope_verify { ($condition: expr, $result: expr) => { if !$condition { return Err($result); } }; } #[derive(Debug, Clone)] pub enum EnvelopeProcessingError { /// Bad Signature BadSignature, BeaconStateError(BeaconStateError), ArithError(ArithError), /// Envelope doesn't match latest beacon block header LatestBlockHeaderMismatch { envelope_root: Hash256, block_header_root: Hash256, }, /// Envelope's `parent_beacon_block_root` doesn't match the parent root of the latest /// block header. ParentBeaconBlockRootMismatch { envelope: Hash256, state: Hash256, }, /// Envelope doesn't match latest beacon block slot SlotMismatch { envelope_slot: Slot, parent_state_slot: Slot, }, /// The payload withdrawals don't match the state's payload withdrawals. WithdrawalsRootMismatch { state: Hash256, payload: Hash256, }, // The builder index doesn't match the committed bid. BuilderIndexMismatch { committed_bid: BuilderIndex, envelope: BuilderIndex, }, // The gas limit doesn't match the committed bid GasLimitMismatch { committed_bid: u64, envelope: u64, }, // The block hash doesn't match the committed bid BlockHashMismatch { committed_bid: ExecutionBlockHash, envelope: ExecutionBlockHash, }, // The parent hash doesn't match the previous execution payload ParentHashMismatch { state: ExecutionBlockHash, envelope: ExecutionBlockHash, }, // The previous randao didn't match the payload PrevRandaoMismatch { committed_bid: Hash256, envelope: Hash256, }, // The timestamp didn't match the payload TimestampMismatch { state: u64, envelope: u64, }, // The execution requests root doesn't match the committed bid ExecutionRequestsRootMismatch { committed_bid: Hash256, envelope: Hash256, }, /// The envelope was deemed invalid by the execution engine. ExecutionInvalid, } impl From for EnvelopeProcessingError { fn from(e: BeaconStateError) -> Self { EnvelopeProcessingError::BeaconStateError(e) } } impl From for EnvelopeProcessingError { fn from(e: ArithError) -> Self { EnvelopeProcessingError::ArithError(e) } } /// Verifies a `SignedExecutionPayloadEnvelope` against the beacon state. /// /// This function performs pure verification with no state mutation. The execution requests /// from the envelope are deferred to be processed in the next block via /// `process_parent_execution_payload`. /// /// `block_state_root` should be the post-block state root (used to fill in the block header /// for beacon_block_root verification). If `None`, the latest_block_header must already have /// its state_root filled in. pub fn verify_execution_payload_envelope( state: &BeaconState, signed_envelope: &SignedExecutionPayloadEnvelope, verify_signatures: VerifySignatures, block_state_root: Hash256, spec: &ChainSpec, ) -> Result<(), EnvelopeProcessingError> { if verify_signatures.is_true() && !signed_envelope.verify_signature_with_state(state, spec)? { return Err(EnvelopeProcessingError::BadSignature); } let envelope = &signed_envelope.message; let payload = &envelope.payload; // Verify consistency with the beacon block. // Use a copy of the header with state_root filled in, matching the spec's approach. let mut header = state.latest_block_header().clone(); if header.state_root == Hash256::default() { // The caller must provide the post-block state root so we can compute // the block header root without mutating state. header.state_root = block_state_root; } let latest_block_header_root = header.tree_hash_root(); envelope_verify!( envelope.beacon_block_root == latest_block_header_root, EnvelopeProcessingError::LatestBlockHeaderMismatch { envelope_root: envelope.beacon_block_root, block_header_root: latest_block_header_root, } ); envelope_verify!( envelope.parent_beacon_block_root == state.latest_block_header().parent_root, EnvelopeProcessingError::ParentBeaconBlockRootMismatch { envelope: envelope.parent_beacon_block_root, state: state.latest_block_header().parent_root, } ); envelope_verify!( envelope.slot() == state.slot(), EnvelopeProcessingError::SlotMismatch { envelope_slot: envelope.slot(), parent_state_slot: state.slot(), } ); // Verify consistency with the committed bid let committed_bid = state.latest_execution_payload_bid()?; envelope_verify!( envelope.builder_index == committed_bid.builder_index, EnvelopeProcessingError::BuilderIndexMismatch { committed_bid: committed_bid.builder_index, envelope: envelope.builder_index, } ); envelope_verify!( committed_bid.prev_randao == payload.prev_randao, EnvelopeProcessingError::PrevRandaoMismatch { committed_bid: committed_bid.prev_randao, envelope: payload.prev_randao, } ); // Verify consistency with expected withdrawals // NOTE: we don't bother hashing here except in case of error, because we can just compare for // equality directly. This equality check could be more straight-forward if the types were // changed to match (currently we are comparing VariableList to List). This could happen // coincidentally when we adopt ProgressiveList. envelope_verify!( payload.withdrawals.len() == state.payload_expected_withdrawals()?.len() && payload .withdrawals .iter() .eq(state.payload_expected_withdrawals()?.iter()), EnvelopeProcessingError::WithdrawalsRootMismatch { state: state.payload_expected_withdrawals()?.tree_hash_root(), payload: payload.withdrawals.tree_hash_root(), } ); // Verify the gas limit envelope_verify!( committed_bid.gas_limit == payload.gas_limit, EnvelopeProcessingError::GasLimitMismatch { committed_bid: committed_bid.gas_limit, envelope: payload.gas_limit, } ); // Verify the block hash envelope_verify!( committed_bid.block_hash == payload.block_hash, EnvelopeProcessingError::BlockHashMismatch { committed_bid: committed_bid.block_hash, envelope: payload.block_hash, } ); // Verify consistency of the parent hash with respect to the previous execution payload envelope_verify!( payload.parent_hash == *state.latest_block_hash()?, EnvelopeProcessingError::ParentHashMismatch { state: *state.latest_block_hash()?, envelope: payload.parent_hash, } ); // Verify timestamp let state_timestamp = compute_timestamp_at_slot(state, state.slot(), spec)?; envelope_verify!( payload.timestamp == state_timestamp, EnvelopeProcessingError::TimestampMismatch { state: state_timestamp, envelope: payload.timestamp, } ); // Verify execution requests root matches committed bid let execution_requests_root = envelope.execution_requests.tree_hash_root(); envelope_verify!( execution_requests_root == committed_bid.execution_requests_root, EnvelopeProcessingError::ExecutionRequestsRootMismatch { committed_bid: committed_bid.execution_requests_root, envelope: execution_requests_root, } ); // TODO(gloas): newPayload happens here in the spec, ensure we wire that up correctly Ok(()) }