diff --git a/README.md b/README.md index 49c6f2bb91..9b5854dd9c 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Like all Ethereum 2.0 clients, Lighthouse is a work-in-progress. Current development overview: -- Specification `v0.10.1` implemented, optimized and passing test vectors. +- Specification `v0.11.1` implemented, optimized and passing test vectors. - Rust-native libp2p with Gossipsub and Discv5. - RESTful JSON API via HTTP server. - Events via WebSocket. diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 9a2a3fad59..3da579cfe8 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -153,6 +153,8 @@ pub struct HeadInfo { pub current_justified_checkpoint: types::Checkpoint, pub finalized_checkpoint: types::Checkpoint, pub fork: Fork, + pub genesis_time: u64, + pub genesis_validators_root: Hash256, } pub trait BeaconChainTypes: Send + Sync + 'static { @@ -492,6 +494,8 @@ impl BeaconChain { current_justified_checkpoint: head.beacon_state.current_justified_checkpoint.clone(), finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(), fork: head.beacon_state.fork.clone(), + genesis_time: head.beacon_state.genesis_time, + genesis_validators_root: head.beacon_state.genesis_validators_root, }) } @@ -1031,17 +1035,23 @@ impl BeaconChain { }) .collect::, Error>>()?; - let fork = self + let (fork, genesis_validators_root) = self .canonical_head .try_read_for(HEAD_LOCK_TIMEOUT) .ok_or_else(|| Error::CanonicalHeadLockTimeout) - .map(|head| head.beacon_state.fork.clone())?; + .map(|head| { + ( + head.beacon_state.fork.clone(), + head.beacon_state.genesis_validators_root, + ) + })?; let signature_set = indexed_attestation_signature_set_from_pubkeys( pubkeys, &attestation.signature, &indexed_attestation, &fork, + genesis_validators_root, &self.spec, ) .map_err(Error::SignatureSetError)?; @@ -1074,8 +1084,12 @@ impl BeaconChain { // Provide the valid attestation to op pool, which may choose to retain the // attestation for inclusion in a future block. if self.eth1_chain.is_some() { - self.op_pool - .insert_attestation(attestation, &fork, &self.spec)?; + self.op_pool.insert_attestation( + attestation, + &fork, + genesis_validators_root, + &self.spec, + )?; }; Ok(AttestationProcessingOutcome::Processed) @@ -1547,6 +1561,7 @@ impl BeaconChain { let mut block = SignedBeaconBlock { message: BeaconBlock { slot: state.slot, + proposer_index: state.get_beacon_proposer_index(state.slot, &self.spec)? as u64, parent_root, state_root: Hash256::zero(), body: BeaconBlockBody { diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 0edd26ce3e..d9d15b7b97 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -313,7 +313,9 @@ where let randao_reveal = { let epoch = slot.epoch(E::slots_per_epoch()); - let domain = self.spec.get_domain(epoch, Domain::Randao, fork); + let domain = + self.spec + .get_domain(epoch, Domain::Randao, fork, state.genesis_validators_root); let message = epoch.signing_root(domain); Signature::new(message.as_bytes(), sk) }; @@ -323,7 +325,7 @@ where .produce_block_on_state(state, slot, randao_reveal) .expect("should produce block"); - let signed_block = block.sign(sk, &state.fork, &self.spec); + let signed_block = block.sign(sk, &state.fork, state.genesis_validators_root, &self.spec); (signed_block, state) } @@ -408,6 +410,7 @@ where attestation.data.target.epoch, Domain::BeaconAttester, fork, + state.genesis_validators_root, ); let message = attestation.data.signing_root(domain); diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index 8b12628a43..628c04142a 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -494,7 +494,15 @@ pub fn get_genesis_time( req: Request, beacon_chain: Arc>, ) -> ApiResult { - ResponseBuilder::new(&req)?.body(&beacon_chain.head()?.beacon_state.genesis_time) + ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_time) +} + +/// Read the `genesis_validators_root` from the current beacon chain state. +pub fn get_genesis_validators_root( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { + ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_validators_root) } pub fn proposer_slashing( diff --git a/beacon_node/rest_api/src/consensus.rs b/beacon_node/rest_api/src/consensus.rs index c22fb2f880..207ae073ba 100644 --- a/beacon_node/rest_api/src/consensus.rs +++ b/beacon_node/rest_api/src/consensus.rs @@ -37,13 +37,13 @@ pub struct VoteCount { impl Into for TotalBalances { fn into(self) -> VoteCount { VoteCount { - current_epoch_active_gwei: self.current_epoch, - previous_epoch_active_gwei: self.previous_epoch, - current_epoch_attesting_gwei: self.current_epoch_attesters, - current_epoch_target_attesting_gwei: self.current_epoch_target_attesters, - previous_epoch_attesting_gwei: self.previous_epoch_attesters, - previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters, - previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters, + current_epoch_active_gwei: self.current_epoch(), + previous_epoch_active_gwei: self.previous_epoch(), + current_epoch_attesting_gwei: self.current_epoch_attesters(), + current_epoch_target_attesting_gwei: self.current_epoch_target_attesters(), + previous_epoch_attesting_gwei: self.previous_epoch_attesters(), + previous_epoch_target_attesting_gwei: self.previous_epoch_target_attesters(), + previous_epoch_head_attesting_gwei: self.previous_epoch_head_attesters(), } } } diff --git a/beacon_node/rest_api/src/router.rs b/beacon_node/rest_api/src/router.rs index f7eb7fd15e..725957ee6f 100644 --- a/beacon_node/rest_api/src/router.rs +++ b/beacon_node/rest_api/src/router.rs @@ -82,6 +82,9 @@ pub fn route( (&Method::GET, "/beacon/genesis_time") => { into_boxfut(beacon::get_genesis_time::(req, beacon_chain)) } + (&Method::GET, "/beacon/genesis_validators_root") => { + into_boxfut(beacon::get_genesis_validators_root::(req, beacon_chain)) + } (&Method::GET, "/beacon/validators") => { into_boxfut(beacon::get_validators::(req, beacon_chain)) } diff --git a/beacon_node/rest_api/tests/test.rs b/beacon_node/rest_api/tests/test.rs index 7165410451..f4c47b42d8 100644 --- a/beacon_node/rest_api/tests/test.rs +++ b/beacon_node/rest_api/tests/test.rs @@ -47,17 +47,15 @@ fn get_randao_reveal( slot: Slot, spec: &ChainSpec, ) -> Signature { - let fork = beacon_chain - .head() - .expect("should get head") - .beacon_state - .fork; + let head = beacon_chain.head().expect("should get head"); + let fork = head.beacon_state.fork; + let genesis_validators_root = head.beacon_state.genesis_validators_root; let proposer_index = beacon_chain .block_proposer(slot) .expect("should get proposer index"); let keypair = generate_deterministic_keypair(proposer_index); let epoch = slot.epoch(E::slots_per_epoch()); - let domain = spec.get_domain(epoch, Domain::Randao, &fork); + let domain = spec.get_domain(epoch, Domain::Randao, &fork, genesis_validators_root); let message = epoch.signing_root(domain); Signature::new(message.as_bytes(), &keypair.sk) } @@ -68,16 +66,14 @@ fn sign_block( block: BeaconBlock, spec: &ChainSpec, ) -> SignedBeaconBlock { - let fork = beacon_chain - .head() - .expect("should get head") - .beacon_state - .fork; + let head = beacon_chain.head().expect("should get head"); + let fork = head.beacon_state.fork; + let genesis_validators_root = head.beacon_state.genesis_validators_root; let proposer_index = beacon_chain .block_proposer(block.slot) .expect("should get proposer index"); let keypair = generate_deterministic_keypair(proposer_index); - block.sign(&keypair.sk, &fork, spec) + block.sign(&keypair.sk, &fork, genesis_validators_root, spec) } #[test] @@ -156,6 +152,7 @@ fn validator_produce_attestation() { .attestation_committee_position .expect("should have committee position"), &state.fork, + state.genesis_validators_root, spec, ) .expect("should sign attestation"); @@ -564,6 +561,31 @@ fn genesis_time() { ); } +#[test] +fn genesis_validators_root() { + let mut env = build_env(); + + let node = build_node(&mut env, testing_client_config()); + let remote_node = node.remote_node().expect("should produce remote node"); + + let genesis_validators_root = env + .runtime() + .block_on(remote_node.http.beacon().get_genesis_validators_root()) + .expect("should fetch genesis time from http api"); + + assert_eq!( + node.client + .beacon_chain() + .expect("should have beacon chain") + .head() + .expect("should get head") + .beacon_state + .genesis_validators_root, + genesis_validators_root, + "should match genesis time from head state" + ); +} + #[test] fn fork() { let mut env = build_env(); @@ -903,6 +925,7 @@ fn proposer_slashing() { proposer_index as u64, &key, fork, + state.genesis_validators_root, spec, ); @@ -927,6 +950,7 @@ fn proposer_slashing() { proposer_index as u64, &key, fork, + state.genesis_validators_root, spec, ); invalid_proposer_slashing.signed_header_2 = invalid_proposer_slashing.signed_header_1.clone(); @@ -981,6 +1005,7 @@ fn attester_slashing() { &validator_indices[..], &secret_keys[..], fork, + state.genesis_validators_root, spec, ); @@ -1006,6 +1031,7 @@ fn attester_slashing() { &validator_indices[..], &secret_keys[..], fork, + state.genesis_validators_root, spec, ); invalid_attester_slashing.attestation_2 = invalid_attester_slashing.attestation_1.clone(); diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 0af5c85a21..b0947a3cd2 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -11,7 +11,7 @@ use types::*; /// /// Utilises lazy-loading from separate storage for its vector fields. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Encode, Decode)] pub struct PartialBeaconState where @@ -19,6 +19,7 @@ where { // Versioning pub genesis_time: u64, + pub genesis_validators_root: Hash256, pub slot: Slot, pub fork: Fork, @@ -72,6 +73,7 @@ impl PartialBeaconState { // TODO: could use references/Cow for fields to avoid cloning PartialBeaconState { genesis_time: s.genesis_time, + genesis_validators_root: s.genesis_validators_root, slot: s.slot, fork: s.fork.clone(), @@ -181,6 +183,7 @@ impl TryInto> for PartialBeaconState { Ok(BeaconState { genesis_time: self.genesis_time, + genesis_validators_root: self.genesis_validators_root, slot: self.slot, fork: self.fork, diff --git a/book/src/http_beacon.md b/book/src/http_beacon.md index b08d1969ef..f5fd6e765f 100644 --- a/book/src/http_beacon.md +++ b/book/src/http_beacon.md @@ -166,6 +166,7 @@ Returns an object containing a single [`SignedBeaconBlock`](https://github.com/e "beacon_block": { "message": { "slot": 0, + "proposer_index": 14, "parent_root": "0x0000000000000000000000000000000000000000000000000000000000000000", "state_root": "0xf15690b6be4ed42ea1ee0741eb4bfd4619d37be8229b84b4ddd480fb028dcc8f", "body": { @@ -444,7 +445,7 @@ canonical chain. ### Returns Returns an object containing a single -[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beaconstate) +[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconstate) and its tree hash root. ### Example Response @@ -454,6 +455,7 @@ and its tree hash root. "root": "0x528e54ca5d4c957729a73f40fc513ae312e054c7295775c4a2b21f423416a72b", "beacon_state": { "genesis_time": 1575652800, + "genesis_validators_root": "0xa8a9226edee1b2627fb4117d7dea4996e64dec2998f37f6e824f74f2ce39a538", "slot": 18478 } } @@ -505,7 +507,7 @@ Typical Responses | 200 ### Returns Returns an object containing the genesis -[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beaconstate). +[`BeaconState`](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconstate). ### Example Response @@ -565,7 +567,7 @@ Typical Responses | 200 ### Returns -Returns an object containing the [`Fork`](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#fork) of the current head. +Returns an object containing the [`Fork`](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#fork) of the current head. ### Example Response diff --git a/eth2/operation_pool/src/attestation_id.rs b/eth2/operation_pool/src/attestation_id.rs index b70a0bfc89..f496ecb3a3 100644 --- a/eth2/operation_pool/src/attestation_id.rs +++ b/eth2/operation_pool/src/attestation_id.rs @@ -1,8 +1,7 @@ -use int_to_bytes::int_to_bytes8; use serde_derive::{Deserialize, Serialize}; use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; -use types::{AttestationData, ChainSpec, Domain, Epoch, Fork}; +use types::{AttestationData, ChainSpec, Domain, Epoch, Fork, Hash256}; /// Serialized `AttestationData` augmented with a domain to encode the fork info. #[derive( @@ -13,21 +12,34 @@ pub struct AttestationId { } /// Number of domain bytes that the end of an attestation ID is padded with. -const DOMAIN_BYTES_LEN: usize = 8; +const DOMAIN_BYTES_LEN: usize = std::mem::size_of::(); impl AttestationId { - pub fn from_data(attestation: &AttestationData, fork: &Fork, spec: &ChainSpec) -> Self { + pub fn from_data( + attestation: &AttestationData, + fork: &Fork, + genesis_validators_root: Hash256, + spec: &ChainSpec, + ) -> Self { let mut bytes = ssz_encode(attestation); let epoch = attestation.target.epoch; - bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, fork, spec)); + bytes.extend_from_slice( + AttestationId::compute_domain_bytes(epoch, fork, genesis_validators_root, spec) + .as_bytes(), + ); AttestationId { v: bytes } } - pub fn compute_domain_bytes(epoch: Epoch, fork: &Fork, spec: &ChainSpec) -> Vec { - int_to_bytes8(spec.get_domain(epoch, Domain::BeaconAttester, fork)) + pub fn compute_domain_bytes( + epoch: Epoch, + fork: &Fork, + genesis_validators_root: Hash256, + spec: &ChainSpec, + ) -> Hash256 { + spec.get_domain(epoch, Domain::BeaconAttester, fork, genesis_validators_root) } - pub fn domain_bytes_match(&self, domain_bytes: &[u8]) -> bool { - &self.v[self.v.len() - DOMAIN_BYTES_LEN..] == domain_bytes + pub fn domain_bytes_match(&self, domain_bytes: &Hash256) -> bool { + &self.v[self.v.len() - DOMAIN_BYTES_LEN..] == domain_bytes.as_bytes() } } diff --git a/eth2/operation_pool/src/lib.rs b/eth2/operation_pool/src/lib.rs index 02f5457790..0e18a75fe4 100644 --- a/eth2/operation_pool/src/lib.rs +++ b/eth2/operation_pool/src/lib.rs @@ -22,7 +22,7 @@ use std::collections::{hash_map, HashMap, HashSet}; use std::marker::PhantomData; use types::{ typenum::Unsigned, Attestation, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec, - EthSpec, Fork, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit, Validator, + EthSpec, Fork, Hash256, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit, Validator, }; #[derive(Default, Debug)] @@ -58,9 +58,10 @@ impl OperationPool { &self, attestation: Attestation, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> Result<(), AttestationValidationError> { - let id = AttestationId::from_data(&attestation.data, fork, spec); + let id = AttestationId::from_data(&attestation.data, fork, genesis_validators_root, spec); // Take a write lock on the attestations map. let mut attestations = self.attestations.write(); @@ -106,9 +107,18 @@ impl OperationPool { // Attestations for the current fork, which may be from the current or previous epoch. let prev_epoch = state.previous_epoch(); let current_epoch = state.current_epoch(); - let prev_domain_bytes = AttestationId::compute_domain_bytes(prev_epoch, &state.fork, spec); - let curr_domain_bytes = - AttestationId::compute_domain_bytes(current_epoch, &state.fork, spec); + let prev_domain_bytes = AttestationId::compute_domain_bytes( + prev_epoch, + &state.fork, + state.genesis_validators_root, + spec, + ); + let curr_domain_bytes = AttestationId::compute_domain_bytes( + current_epoch, + &state.fork, + state.genesis_validators_root, + spec, + ); let reader = self.attestations.read(); let active_indices = state .get_cached_active_validator_indices(RelativeEpoch::Current) @@ -168,7 +178,7 @@ impl OperationPool { verify_proposer_slashing(&slashing, state, VerifySignatures::True, spec)?; self.proposer_slashings .write() - .insert(slashing.proposer_index, slashing); + .insert(slashing.signed_header_1.message.proposer_index, slashing); Ok(()) } @@ -181,8 +191,18 @@ impl OperationPool { spec: &ChainSpec, ) -> (AttestationId, AttestationId) { ( - AttestationId::from_data(&slashing.attestation_1.data, &state.fork, spec), - AttestationId::from_data(&slashing.attestation_2.data, &state.fork, spec), + AttestationId::from_data( + &slashing.attestation_1.data, + &state.fork, + state.genesis_validators_root, + spec, + ), + AttestationId::from_data( + &slashing.attestation_2.data, + &state.fork, + state.genesis_validators_root, + spec, + ), ) } @@ -214,7 +234,7 @@ impl OperationPool { |slashing| { state .validators - .get(slashing.proposer_index as usize) + .get(slashing.signed_header_1.message.proposer_index as usize) .map_or(false, |validator| !validator.slashed) }, T::MaxProposerSlashings::to_usize(), @@ -224,7 +244,7 @@ impl OperationPool { // slashings. let mut to_be_slashed = proposer_slashings .iter() - .map(|s| s.proposer_index) + .map(|s| s.signed_header_1.message.proposer_index) .collect::>(); let epoch = state.current_epoch(); @@ -427,6 +447,7 @@ mod release_tests { signers, &committee_keys, &state.fork, + state.genesis_validators_root, spec, ); extra_signer.map(|c_idx| { @@ -436,6 +457,7 @@ mod release_tests { &[validator_index], &[&keypairs[validator_index].sk], &state.fork, + state.genesis_validators_root, spec, ) }); @@ -548,7 +570,9 @@ mod release_tests { spec, None, ); - op_pool.insert_attestation(att, &state.fork, spec).unwrap(); + op_pool + .insert_attestation(att, &state.fork, state.genesis_validators_root, spec) + .unwrap(); } } @@ -617,9 +641,16 @@ mod release_tests { None, ); op_pool - .insert_attestation(att.clone(), &state.fork, spec) + .insert_attestation( + att.clone(), + &state.fork, + state.genesis_validators_root, + spec, + ) + .unwrap(); + op_pool + .insert_attestation(att, &state.fork, state.genesis_validators_root, spec) .unwrap(); - op_pool.insert_attestation(att, &state.fork, spec).unwrap(); } assert_eq!(op_pool.num_attestations(), committees.len()); @@ -656,7 +687,9 @@ mod release_tests { spec, None, ); - op_pool.insert_attestation(att, &state.fork, spec).unwrap(); + op_pool + .insert_attestation(att, &state.fork, state.genesis_validators_root, spec) + .unwrap(); } } @@ -704,7 +737,9 @@ mod release_tests { spec, if i == 0 { None } else { Some(0) }, ); - op_pool.insert_attestation(att, &state.fork, spec).unwrap(); + op_pool + .insert_attestation(att, &state.fork, state.genesis_validators_root, spec) + .unwrap(); } }; @@ -777,7 +812,9 @@ mod release_tests { spec, if i == 0 { None } else { Some(0) }, ); - op_pool.insert_attestation(att, &state.fork, spec).unwrap(); + op_pool + .insert_attestation(att, &state.fork, state.genesis_validators_root, spec) + .unwrap(); } }; diff --git a/eth2/operation_pool/src/persistence.rs b/eth2/operation_pool/src/persistence.rs index 592d4d18b3..547877cfde 100644 --- a/eth2/operation_pool/src/persistence.rs +++ b/eth2/operation_pool/src/persistence.rs @@ -82,7 +82,7 @@ impl PersistedOperationPool { let proposer_slashings = RwLock::new( self.proposer_slashings .into_iter() - .map(|slashing| (slashing.proposer_index, slashing)) + .map(|slashing| (slashing.signed_header_1.message.proposer_index, slashing)) .collect(), ); let voluntary_exits = RwLock::new( diff --git a/eth2/state_processing/src/common/get_attesting_indices.rs b/eth2/state_processing/src/common/get_attesting_indices.rs index 1bfbcabd50..c3d922c60a 100644 --- a/eth2/state_processing/src/common/get_attesting_indices.rs +++ b/eth2/state_processing/src/common/get_attesting_indices.rs @@ -3,7 +3,7 @@ use types::*; /// Returns validator indices which participated in the attestation, sorted by increasing index. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn get_attesting_indices( committee: &[usize], bitlist: &BitList, diff --git a/eth2/state_processing/src/common/get_base_reward.rs b/eth2/state_processing/src/common/get_base_reward.rs index d4da9ff320..1fcb5b64eb 100644 --- a/eth2/state_processing/src/common/get_base_reward.rs +++ b/eth2/state_processing/src/common/get_base_reward.rs @@ -3,7 +3,7 @@ use types::*; /// Returns the base reward for some validator. /// -/// Spec v0.9.1 +/// Spec v0.11.1 pub fn get_base_reward( state: &BeaconState, index: usize, diff --git a/eth2/state_processing/src/common/get_indexed_attestation.rs b/eth2/state_processing/src/common/get_indexed_attestation.rs index 263cdb0994..2c7ec98c87 100644 --- a/eth2/state_processing/src/common/get_indexed_attestation.rs +++ b/eth2/state_processing/src/common/get_indexed_attestation.rs @@ -6,7 +6,7 @@ type Result = std::result::Result>; /// Convert `attestation` to (almost) indexed-verifiable form. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn get_indexed_attestation( committee: &[usize], attestation: &Attestation, diff --git a/eth2/state_processing/src/common/initiate_validator_exit.rs b/eth2/state_processing/src/common/initiate_validator_exit.rs index efb26be1cb..411734bfae 100644 --- a/eth2/state_processing/src/common/initiate_validator_exit.rs +++ b/eth2/state_processing/src/common/initiate_validator_exit.rs @@ -3,7 +3,7 @@ use types::{BeaconStateError as Error, *}; /// Initiate the exit of the validator of the given `index`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn initiate_validator_exit( state: &mut BeaconState, index: usize, diff --git a/eth2/state_processing/src/common/slash_validator.rs b/eth2/state_processing/src/common/slash_validator.rs index e308c83462..eb5621293f 100644 --- a/eth2/state_processing/src/common/slash_validator.rs +++ b/eth2/state_processing/src/common/slash_validator.rs @@ -4,7 +4,7 @@ use types::{BeaconStateError as Error, *}; /// Slash the validator with index ``index``. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn slash_validator( state: &mut BeaconState, slashed_index: usize, diff --git a/eth2/state_processing/src/genesis.rs b/eth2/state_processing/src/genesis.rs index 8a950b9eee..71c66cba80 100644 --- a/eth2/state_processing/src/genesis.rs +++ b/eth2/state_processing/src/genesis.rs @@ -6,7 +6,7 @@ use types::*; /// Initialize a `BeaconState` from genesis data. /// -/// Spec v0.10.1 +/// Spec v0.11.1 // TODO: this is quite inefficient and we probably want to rethink how we do this pub fn initialize_beacon_state_from_eth1( eth1_block_hash: Hash256, @@ -42,12 +42,15 @@ pub fn initialize_beacon_state_from_eth1( // Now that we have our validators, initialize the caches (including the committees) state.build_all_caches(spec)?; + // Set genesis validators root for domain separation and chain versioning + state.genesis_validators_root = state.update_validators_tree_hash_cache()?; + Ok(state) } /// Determine whether a candidate genesis state is suitable for starting the chain. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn is_valid_genesis_state(state: &BeaconState, spec: &ChainSpec) -> bool { state.genesis_time >= spec.min_genesis_time && state.get_active_validator_indices(T::genesis_epoch()).len() as u64 @@ -56,7 +59,7 @@ pub fn is_valid_genesis_state(state: &BeaconState, spec: &ChainSp /// Activate genesis validators, if their balance is acceptable. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_activations(state: &mut BeaconState, spec: &ChainSpec) { for (index, validator) in state.validators.iter_mut().enumerate() { let balance = state.balances[index]; diff --git a/eth2/state_processing/src/per_block_processing.rs b/eth2/state_processing/src/per_block_processing.rs index 7285f2dacd..cba8dc3ffb 100644 --- a/eth2/state_processing/src/per_block_processing.rs +++ b/eth2/state_processing/src/per_block_processing.rs @@ -70,7 +70,7 @@ impl VerifySignatures { /// tree hash root of the block, NOT the signing root of the block. This function takes /// care of mixing in the domain. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn per_block_processing( mut state: &mut BeaconState, signed_block: &SignedBeaconBlock, @@ -136,14 +136,26 @@ pub fn per_block_processing( /// Processes the block header. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_block_header( state: &mut BeaconState, block: &BeaconBlock, spec: &ChainSpec, ) -> Result<(), BlockOperationError> { + // Verify that the slots match verify!(block.slot == state.slot, HeaderInvalid::StateSlotMismatch); + // Verify that proposer index is the correct index + let proposer_index = block.proposer_index as usize; + let state_proposer_index = state.get_beacon_proposer_index(block.slot, spec)?; + verify!( + proposer_index == state_proposer_index, + HeaderInvalid::ProposerIndexMismatch { + block_proposer_index: proposer_index, + state_proposer_index, + } + ); + let expected_previous_block_root = state.latest_block_header.tree_hash_root(); verify!( block.parent_root == expected_previous_block_root, @@ -156,11 +168,10 @@ pub fn process_block_header( state.latest_block_header = block.temporary_block_header(); // Verify proposer is not slashed - let proposer_idx = state.get_beacon_proposer_index(block.slot, spec)?; - let proposer = &state.validators[proposer_idx]; + let proposer = &state.validators[proposer_index]; verify!( !proposer.slashed, - HeaderInvalid::ProposerSlashed(proposer_idx) + HeaderInvalid::ProposerSlashed(proposer_index) ); Ok(()) @@ -168,7 +179,7 @@ pub fn process_block_header( /// Verifies the signature of a block. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_block_signature( state: &BeaconState, block: &SignedBeaconBlock, @@ -186,7 +197,7 @@ pub fn verify_block_signature( /// Verifies the `randao_reveal` against the block's proposer pubkey and updates /// `state.latest_randao_mixes`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_randao( state: &mut BeaconState, block: &BeaconBlock, @@ -209,7 +220,7 @@ pub fn process_randao( /// Update the `state.eth1_data_votes` based upon the `eth1_data` provided. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_eth1_data( state: &mut BeaconState, eth1_data: &Eth1Data, @@ -226,7 +237,7 @@ pub fn process_eth1_data( /// Returns `Some(eth1_data)` if adding the given `eth1_data` to `state.eth1_data_votes` would /// result in a change to `state.eth1_data`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn get_new_eth1_data( state: &BeaconState, eth1_data: &Eth1Data, @@ -250,7 +261,7 @@ pub fn get_new_eth1_data( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_proposer_slashings( state: &mut BeaconState, proposer_slashings: &[ProposerSlashing], @@ -268,7 +279,12 @@ pub fn process_proposer_slashings( // Update the state. for proposer_slashing in proposer_slashings { - slash_validator(state, proposer_slashing.proposer_index as usize, None, spec)?; + slash_validator( + state, + proposer_slashing.signed_header_1.message.proposer_index as usize, + None, + spec, + )?; } Ok(()) @@ -279,7 +295,7 @@ pub fn process_proposer_slashings( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_attester_slashings( state: &mut BeaconState, attester_slashings: &[AttesterSlashing], @@ -333,7 +349,7 @@ pub fn process_attester_slashings( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_attestations( state: &mut BeaconState, attestations: &[Attestation], @@ -379,7 +395,7 @@ pub fn process_attestations( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_deposits( state: &mut BeaconState, deposits: &[Deposit], @@ -416,7 +432,7 @@ pub fn process_deposits( /// Process a single deposit, optionally verifying its merkle proof. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_deposit( state: &mut BeaconState, deposit: &Deposit, @@ -482,7 +498,7 @@ pub fn process_deposit( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_exits( state: &mut BeaconState, voluntary_exits: &[SignedVoluntaryExit], diff --git a/eth2/state_processing/src/per_block_processing/block_processing_builder.rs b/eth2/state_processing/src/per_block_processing/block_processing_builder.rs index 695c10c032..5c79616110 100644 --- a/eth2/state_processing/src/per_block_processing/block_processing_builder.rs +++ b/eth2/state_processing/src/per_block_processing/block_processing_builder.rs @@ -56,9 +56,18 @@ impl BlockProcessingBuilder { let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap(); let keypair = &keypairs[proposer_index]; + builder.set_proposer_index(proposer_index as u64); + match randao_sk { - Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), - None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + Some(sk) => { + builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec) + } + None => builder.set_randao_reveal( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ), } self.block_builder.insert_deposits( @@ -70,7 +79,12 @@ impl BlockProcessingBuilder { spec, ); - let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + let block = self.block_builder.build( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); (block, state) } @@ -96,9 +110,18 @@ impl BlockProcessingBuilder { let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap(); let keypair = &keypairs[proposer_index]; + builder.set_proposer_index(proposer_index as u64); + match randao_sk { - Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), - None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + Some(sk) => { + builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec) + } + None => builder.set_randao_reveal( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ), } match test_task { ExitTestTask::AlreadyInitiated => { @@ -125,7 +148,12 @@ impl BlockProcessingBuilder { } } - let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + let block = self.block_builder.build( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); (block, state) } @@ -151,9 +179,18 @@ impl BlockProcessingBuilder { let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap(); let keypair = &keypairs[proposer_index]; + builder.set_proposer_index(proposer_index as u64); + match randao_sk { - Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), - None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + Some(sk) => { + builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec) + } + None => builder.set_randao_reveal( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ), } let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect(); @@ -166,7 +203,12 @@ impl BlockProcessingBuilder { spec, ) .unwrap(); - let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + let block = self.block_builder.build( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); (block, state) } @@ -192,9 +234,18 @@ impl BlockProcessingBuilder { let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap(); let keypair = &keypairs[proposer_index]; + builder.set_proposer_index(proposer_index as u64); + match randao_sk { - Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), - None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + Some(sk) => { + builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec) + } + None => builder.set_randao_reveal( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ), } let mut validator_indices = vec![]; @@ -210,10 +261,16 @@ impl BlockProcessingBuilder { &validator_indices, &secret_keys, &state.fork, + state.genesis_validators_root, spec, ); } - let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + let block = self.block_builder.build( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); (block, state) } @@ -239,9 +296,18 @@ impl BlockProcessingBuilder { let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap(); let keypair = &keypairs[proposer_index]; + builder.set_proposer_index(proposer_index as u64); + match randao_sk { - Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), - None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + Some(sk) => { + builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec) + } + None => builder.set_randao_reveal( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ), } for i in 0..num_proposer_slashings { @@ -252,10 +318,16 @@ impl BlockProcessingBuilder { validator_indices, &secret_keys, &state.fork, + state.genesis_validators_root, spec, ); } - let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + let block = self.block_builder.build( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); (block, state) } @@ -279,12 +351,26 @@ impl BlockProcessingBuilder { let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap(); let keypair = &keypairs[proposer_index]; + builder.set_proposer_index(proposer_index as u64); + match randao_sk { - Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), - None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + Some(sk) => { + builder.set_randao_reveal(&sk, &state.fork, state.genesis_validators_root, spec) + } + None => builder.set_randao_reveal( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ), } - let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + let block = self.block_builder.build( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); (block, state) } diff --git a/eth2/state_processing/src/per_block_processing/errors.rs b/eth2/state_processing/src/per_block_processing/errors.rs index 609c57bdd3..757705c993 100644 --- a/eth2/state_processing/src/per_block_processing/errors.rs +++ b/eth2/state_processing/src/per_block_processing/errors.rs @@ -159,7 +159,14 @@ impl From for BlockOperationError { pub enum HeaderInvalid { ProposalSignatureInvalid, StateSlotMismatch, - ParentBlockRootMismatch { state: Hash256, block: Hash256 }, + ProposerIndexMismatch { + block_proposer_index: usize, + state_proposer_index: usize, + }, + ParentBlockRootMismatch { + state: Hash256, + block: Hash256, + }, ProposerSlashed(usize), } @@ -171,6 +178,10 @@ pub enum ProposerSlashingInvalid { /// /// (proposal_1_slot, proposal_2_slot) ProposalSlotMismatch(Slot, Slot), + /// The two proposals have different proposer indices. + /// + /// (proposer_index_1, proposer_index_2) + ProposerIndexMismatch(u64, u64), /// The proposals are identical and therefore not slashable. ProposalsIdentical, /// The specified proposer cannot be slashed because they are already slashed, or not active. diff --git a/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs b/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs index 323f0c5bff..38741b026e 100644 --- a/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs @@ -11,7 +11,7 @@ fn error(reason: Invalid) -> BlockOperationError { /// Verify an `IndexedAttestation`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn is_valid_indexed_attestation( state: &BeaconState, indexed_attestation: &IndexedAttestation, diff --git a/eth2/state_processing/src/per_block_processing/signature_sets.rs b/eth2/state_processing/src/per_block_processing/signature_sets.rs index a91749af9e..c78ff093a0 100644 --- a/eth2/state_processing/src/per_block_processing/signature_sets.rs +++ b/eth2/state_processing/src/per_block_processing/signature_sets.rs @@ -53,6 +53,7 @@ pub fn block_proposal_signature_set<'a, T: EthSpec>( block.slot.epoch(T::slots_per_epoch()), Domain::BeaconProposer, &state.fork, + state.genesis_validators_root, ); let message = if let Some(root) = block_root { @@ -84,6 +85,7 @@ pub fn randao_signature_set<'a, T: EthSpec>( block.slot.epoch(T::slots_per_epoch()), Domain::Randao, &state.fork, + state.genesis_validators_root, ); let message = state.current_epoch().signing_root(domain); @@ -101,7 +103,7 @@ pub fn proposer_slashing_signature_set<'a, T: EthSpec>( proposer_slashing: &'a ProposerSlashing, spec: &'a ChainSpec, ) -> Result<(SignatureSet<'a>, SignatureSet<'a>)> { - let proposer_index = proposer_slashing.proposer_index as usize; + let proposer_index = proposer_slashing.signed_header_1.message.proposer_index as usize; Ok(( block_header_signature_set( @@ -130,6 +132,7 @@ fn block_header_signature_set<'a, T: EthSpec>( signed_header.message.slot.epoch(T::slots_per_epoch()), Domain::BeaconProposer, &state.fork, + state.genesis_validators_root, ); let message = signed_header @@ -162,6 +165,7 @@ pub fn indexed_attestation_signature_set<'a, 'b, T: EthSpec>( indexed_attestation.data.target.epoch, Domain::BeaconAttester, &state.fork, + state.genesis_validators_root, ); let message = indexed_attestation.data.signing_root(domain); @@ -177,6 +181,7 @@ pub fn indexed_attestation_signature_set_from_pubkeys<'a, 'b, T: EthSpec>( signature: &'a AggregateSignature, indexed_attestation: &'b IndexedAttestation, fork: &Fork, + genesis_validators_root: Hash256, spec: &'a ChainSpec, ) -> Result> { let pubkeys = pubkeys @@ -188,6 +193,7 @@ pub fn indexed_attestation_signature_set_from_pubkeys<'a, 'b, T: EthSpec>( indexed_attestation.data.target.epoch, Domain::BeaconAttester, &fork, + genesis_validators_root, ); let message = indexed_attestation.data.signing_root(domain); @@ -258,7 +264,12 @@ pub fn exit_signature_set<'a, T: EthSpec>( let exit = &signed_exit.message; let proposer_index = exit.validator_index as usize; - let domain = spec.get_domain(exit.epoch, Domain::VoluntaryExit, &state.fork); + let domain = spec.get_domain( + exit.epoch, + Domain::VoluntaryExit, + &state.fork, + state.genesis_validators_root, + ); let message = exit.signing_root(domain).as_bytes().to_vec(); diff --git a/eth2/state_processing/src/per_block_processing/tests.rs b/eth2/state_processing/src/per_block_processing/tests.rs index f86396a618..f942cc29f5 100644 --- a/eth2/state_processing/src/per_block_processing/tests.rs +++ b/eth2/state_processing/src/per_block_processing/tests.rs @@ -10,7 +10,7 @@ use types::test_utils::{ use types::*; pub const NUM_DEPOSITS: u64 = 1; -pub const VALIDATOR_COUNT: usize = 10; +pub const VALIDATOR_COUNT: usize = 64; pub const SLOT_OFFSET: u64 = 4; pub const EXIT_SLOT_OFFSET: u64 = 2048; pub const NUM_ATTESTATIONS: u64 = 1; @@ -93,7 +93,12 @@ fn invalid_block_signature() { // sign the block with a keypair that is not the expected proposer let keypair = Keypair::random(); - let block = block.message.sign(&keypair.sk, &state.fork, &spec); + let block = block.message.sign( + &keypair.sk, + &state.fork, + state.genesis_validators_root, + &spec, + ); // process block with invalid block signature let result = per_block_processing( @@ -630,7 +635,7 @@ fn invalid_attestation_wrong_justified_checkpoint() { #[test] fn invalid_attestation_bad_indexed_attestation_bad_signature() { let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec, SLOT_OFFSET, 33); // minmium number of validators required for this test + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); let test_task = AttestationTestTask::BadIndexedAttestationBadSignature; let (block, mut state) = builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec); diff --git a/eth2/state_processing/src/per_block_processing/verify_attestation.rs b/eth2/state_processing/src/per_block_processing/verify_attestation.rs index a340c3109e..2a688ef465 100644 --- a/eth2/state_processing/src/per_block_processing/verify_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/verify_attestation.rs @@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError { /// /// Optionally verifies the aggregate signature, depending on `verify_signatures`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_attestation_for_block_inclusion( state: &BeaconState, attestation: &Attestation, @@ -49,7 +49,7 @@ pub fn verify_attestation_for_block_inclusion( /// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the /// prior blocks in `state`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_attestation_for_state( state: &BeaconState, attestation: &Attestation, @@ -58,6 +58,13 @@ pub fn verify_attestation_for_state( ) -> Result<()> { let data = &attestation.data; + // This emptiness check is required *in addition* to the length check in `get_attesting_indices` + // because we can parse a bitfield and know its length, even if it has no bits set. + verify!( + !attestation.aggregation_bits.is_zero(), + Invalid::AggregationBitfieldIsEmpty + ); + verify!( data.index < state.get_committee_count_at_slot(data.slot)?, Invalid::BadCommitteeIndex @@ -76,7 +83,7 @@ pub fn verify_attestation_for_state( /// Check target epoch and source checkpoint. /// -/// Spec v0.10.1 +/// Spec v0.11.1 fn verify_casper_ffg_vote( attestation: &Attestation, state: &BeaconState, diff --git a/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs b/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs index f8b729f6a0..c1e33dd4b7 100644 --- a/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs +++ b/eth2/state_processing/src/per_block_processing/verify_attester_slashing.rs @@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError { /// /// Returns `Ok(())` if the `AttesterSlashing` is valid, otherwise indicates the reason for invalidity. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_attester_slashing( state: &BeaconState, attester_slashing: &AttesterSlashing, @@ -47,7 +47,7 @@ pub fn verify_attester_slashing( /// /// Returns Ok(indices) if `indices.len() > 0`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn get_slashable_indices( state: &BeaconState, attester_slashing: &AttesterSlashing, 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 9f624ddb8f..cd92ee895f 100644 --- a/eth2/state_processing/src/per_block_processing/verify_deposit.rs +++ b/eth2/state_processing/src/per_block_processing/verify_deposit.rs @@ -14,7 +14,7 @@ fn error(reason: DepositInvalid) -> BlockOperationError { /// Verify `Deposit.pubkey` signed `Deposit.signature`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_deposit_signature(deposit_data: &DepositData, spec: &ChainSpec) -> Result<()> { let deposit_signature_message = deposit_pubkey_signature_message(&deposit_data, spec) .ok_or_else(|| error(DepositInvalid::BadBlsBytes))?; @@ -46,7 +46,7 @@ pub fn get_existing_validator_index( /// The deposit index is provided as a parameter so we can check proofs /// before they're due to be processed, and in parallel. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_deposit_merkle_proof( state: &BeaconState, deposit: &Deposit, diff --git a/eth2/state_processing/src/per_block_processing/verify_exit.rs b/eth2/state_processing/src/per_block_processing/verify_exit.rs index a2d1854027..4ca81c6d94 100644 --- a/eth2/state_processing/src/per_block_processing/verify_exit.rs +++ b/eth2/state_processing/src/per_block_processing/verify_exit.rs @@ -13,7 +13,7 @@ fn error(reason: ExitInvalid) -> BlockOperationError { /// /// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_exit( state: &BeaconState, exit: &SignedVoluntaryExit, @@ -25,7 +25,7 @@ pub fn verify_exit( /// Like `verify_exit` but doesn't run checks which may become true in future states. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_exit_time_independent_only( state: &BeaconState, exit: &SignedVoluntaryExit, @@ -37,7 +37,7 @@ pub fn verify_exit_time_independent_only( /// Parametric version of `verify_exit` that skips some checks if `time_independent_only` is true. /// -/// Spec v0.10.1 +/// Spec v0.11.1 fn verify_exit_parametric( state: &BeaconState, signed_exit: &SignedVoluntaryExit, diff --git a/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs b/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs index 268e56c140..a9cb854743 100644 --- a/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs +++ b/eth2/state_processing/src/per_block_processing/verify_proposer_slashing.rs @@ -14,38 +14,40 @@ fn error(reason: Invalid) -> BlockOperationError { /// /// Returns `Ok(())` if the `ProposerSlashing` is valid, otherwise indicates the reason for invalidity. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn verify_proposer_slashing( proposer_slashing: &ProposerSlashing, state: &BeaconState, verify_signatures: VerifySignatures, spec: &ChainSpec, ) -> Result<()> { - let proposer = state - .validators - .get(proposer_slashing.proposer_index as usize) - .ok_or_else(|| error(Invalid::ProposerUnknown(proposer_slashing.proposer_index)))?; + let header_1 = &proposer_slashing.signed_header_1.message; + let header_2 = &proposer_slashing.signed_header_2.message; // Verify slots match verify!( - proposer_slashing.signed_header_1.message.slot - == proposer_slashing.signed_header_2.message.slot, - Invalid::ProposalSlotMismatch( - proposer_slashing.signed_header_1.message.slot, - proposer_slashing.signed_header_2.message.slot - ) + header_1.slot == header_2.slot, + Invalid::ProposalSlotMismatch(header_1.slot, header_2.slot) + ); + + // Verify header proposer indices match + verify!( + header_1.proposer_index == header_2.proposer_index, + Invalid::ProposerIndexMismatch(header_1.proposer_index, header_2.proposer_index) ); // But the headers are different - verify!( - proposer_slashing.signed_header_1 != proposer_slashing.signed_header_2, - Invalid::ProposalsIdentical - ); + verify!(header_1 != header_2, Invalid::ProposalsIdentical); // Check proposer is slashable + let proposer = state + .validators + .get(header_1.proposer_index as usize) + .ok_or_else(|| error(Invalid::ProposerUnknown(header_1.proposer_index)))?; + verify!( proposer.is_slashable_at(state.current_epoch()), - Invalid::ProposerNotSlashable(proposer_slashing.proposer_index) + Invalid::ProposerNotSlashable(header_1.proposer_index) ); if verify_signatures.is_true() { diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index 74be1b1ed2..cc995a8ac0 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -19,7 +19,7 @@ pub use validator_statuses::{TotalBalances, ValidatorStatus, ValidatorStatuses}; /// Mutates the given `BeaconState`, returning early if an error is encountered. If an error is /// returned, a state might be "half-processed" and therefore in an invalid state. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn per_epoch_processing( state: &mut BeaconState, spec: &ChainSpec, @@ -45,7 +45,11 @@ pub fn per_epoch_processing( process_registry_updates(state, spec)?; // Slashings. - process_slashings(state, validator_statuses.total_balances.current_epoch, spec)?; + process_slashings( + state, + validator_statuses.total_balances.current_epoch(), + spec, + )?; // Final updates. process_final_updates(state, spec)?; @@ -66,7 +70,7 @@ pub fn per_epoch_processing( /// - `finalized_epoch` /// - `finalized_root` /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[allow(clippy::if_same_then_else)] // For readability and consistency with spec. pub fn process_justification_and_finalization( state: &mut BeaconState, @@ -86,7 +90,7 @@ pub fn process_justification_and_finalization( state.previous_justified_checkpoint = state.current_justified_checkpoint.clone(); state.justification_bits.shift_up(1)?; - if total_balances.previous_epoch_target_attesters * 3 >= total_balances.current_epoch * 2 { + if total_balances.previous_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 { state.current_justified_checkpoint = Checkpoint { epoch: previous_epoch, root: *state.get_block_root_at_epoch(previous_epoch)?, @@ -94,7 +98,7 @@ pub fn process_justification_and_finalization( state.justification_bits.set(1, true)?; } // If the current epoch gets justified, fill the last bit. - if total_balances.current_epoch_target_attesters * 3 >= total_balances.current_epoch * 2 { + if total_balances.current_epoch_target_attesters() * 3 >= total_balances.current_epoch() * 2 { state.current_justified_checkpoint = Checkpoint { epoch: current_epoch, root: *state.get_block_root_at_epoch(current_epoch)?, @@ -134,7 +138,7 @@ pub fn process_justification_and_finalization( /// Finish up an epoch update. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_final_updates( state: &mut BeaconState, spec: &ChainSpec, @@ -148,11 +152,14 @@ pub fn process_final_updates( } // Update effective balances with hysteresis (lag). + let hysteresis_increment = spec.effective_balance_increment / spec.hysteresis_quotient; + let downward_threshold = hysteresis_increment * spec.hysteresis_downward_multiplier; + let upward_threshold = hysteresis_increment * spec.hysteresis_upward_multiplier; for (index, validator) in state.validators.iter_mut().enumerate() { let balance = state.balances[index]; - let half_increment = spec.effective_balance_increment / 2; - if balance < validator.effective_balance - || validator.effective_balance + 3 * half_increment < balance + + if balance + downward_threshold < validator.effective_balance + || validator.effective_balance + upward_threshold < balance { validator.effective_balance = std::cmp::min( balance - balance % spec.effective_balance_increment, diff --git a/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs b/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs index 83588ad7ae..1f79680fa1 100644 --- a/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs +++ b/eth2/state_processing/src/per_epoch_processing/apply_rewards.rs @@ -33,7 +33,7 @@ impl std::ops::AddAssign for Delta { /// Apply attester and proposer rewards. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_rewards_and_penalties( state: &mut BeaconState, validator_statuses: &mut ValidatorStatuses, @@ -67,7 +67,7 @@ pub fn process_rewards_and_penalties( /// For each attesting validator, reward the proposer who was first to include their attestation. /// -/// Spec v0.10.1 +/// Spec v0.11.1 fn get_proposer_deltas( deltas: &mut Vec, state: &BeaconState, @@ -83,7 +83,7 @@ fn get_proposer_deltas( let base_reward = get_base_reward( state, index, - validator_statuses.total_balances.current_epoch, + validator_statuses.total_balances.current_epoch(), spec, )?; @@ -100,7 +100,7 @@ fn get_proposer_deltas( /// Apply rewards for participation in attestations during the previous epoch. /// -/// Spec v0.10.1 +/// Spec v0.11.1 fn get_attestation_deltas( deltas: &mut Vec, state: &BeaconState, @@ -113,7 +113,7 @@ fn get_attestation_deltas( let base_reward = get_base_reward( state, index, - validator_statuses.total_balances.current_epoch, + validator_statuses.total_balances.current_epoch(), spec, )?; @@ -133,7 +133,7 @@ fn get_attestation_deltas( /// Determine the delta for a single validator, sans proposer rewards. /// -/// Spec v0.11.0 +/// Spec v0.11.1 fn get_attestation_delta( validator: &ValidatorStatus, total_balances: &TotalBalances, @@ -157,13 +157,13 @@ fn get_attestation_delta( // - increment = EFFECTIVE_BALANCE_INCREMENT // - reward_numerator = get_base_reward(state, index) * (attesting_balance // increment) // - rewards[index] = reward_numerator // (total_balance // increment) - let total_balance_ebi = total_balances.current_epoch / spec.effective_balance_increment; + let total_balance_ebi = total_balances.current_epoch() / spec.effective_balance_increment; let total_attesting_balance_ebi = - total_balances.previous_epoch_attesters / spec.effective_balance_increment; + total_balances.previous_epoch_attesters() / spec.effective_balance_increment; let matching_target_balance_ebi = - total_balances.previous_epoch_target_attesters / spec.effective_balance_increment; + total_balances.previous_epoch_target_attesters() / spec.effective_balance_increment; let matching_head_balance_ebi = - total_balances.previous_epoch_head_attesters / spec.effective_balance_increment; + total_balances.previous_epoch_head_attesters() / spec.effective_balance_increment; // Expected FFG source. // Spec: diff --git a/eth2/state_processing/src/per_epoch_processing/process_slashings.rs b/eth2/state_processing/src/per_epoch_processing/process_slashings.rs index d56894af57..4bf24468f2 100644 --- a/eth2/state_processing/src/per_epoch_processing/process_slashings.rs +++ b/eth2/state_processing/src/per_epoch_processing/process_slashings.rs @@ -2,7 +2,7 @@ use types::{BeaconStateError as Error, *}; /// Process slashings. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_slashings( state: &mut BeaconState, total_balance: u64, diff --git a/eth2/state_processing/src/per_epoch_processing/registry_updates.rs b/eth2/state_processing/src/per_epoch_processing/registry_updates.rs index 048be3b0ab..d77b1e6548 100644 --- a/eth2/state_processing/src/per_epoch_processing/registry_updates.rs +++ b/eth2/state_processing/src/per_epoch_processing/registry_updates.rs @@ -5,7 +5,7 @@ use types::*; /// Performs a validator registry update, if required. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn process_registry_updates( state: &mut BeaconState, spec: &ChainSpec, diff --git a/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs index 28521bf894..8877ca8157 100644 --- a/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs +++ b/eth2/state_processing/src/per_epoch_processing/validator_statuses.rs @@ -12,7 +12,7 @@ macro_rules! set_self_if_other_is_true { } /// The information required to reward a block producer for including an attestation in a block. -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct InclusionInfo { /// The distance between the attestation slot and the slot that attestation was included in a /// block. @@ -43,7 +43,7 @@ impl InclusionInfo { } /// Information required to reward some validator during the current and previous epoch. -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] pub struct ValidatorStatus { /// True if the validator has been slashed, ever. pub is_slashed: bool, @@ -107,30 +107,64 @@ impl ValidatorStatus { /// The total effective balances for different sets of validators during the previous and current /// epochs. -#[derive(Default, Clone, Debug)] +#[derive(Clone, Debug)] pub struct TotalBalances { + /// The effective balance increment from the spec. + effective_balance_increment: u64, /// The total effective balance of all active validators during the _current_ epoch. - pub current_epoch: u64, + current_epoch: u64, /// The total effective balance of all active validators during the _previous_ epoch. - pub previous_epoch: u64, + previous_epoch: u64, /// The total effective balance of all validators who attested during the _current_ epoch. - pub current_epoch_attesters: u64, + current_epoch_attesters: u64, /// The total effective balance of all validators who attested during the _current_ epoch and /// agreed with the state about the beacon block at the first slot of the _current_ epoch. - pub current_epoch_target_attesters: u64, + current_epoch_target_attesters: u64, /// The total effective balance of all validators who attested during the _previous_ epoch. - pub previous_epoch_attesters: u64, + previous_epoch_attesters: u64, /// The total effective balance of all validators who attested during the _previous_ epoch and /// agreed with the state about the beacon block at the first slot of the _previous_ epoch. - pub previous_epoch_target_attesters: u64, + previous_epoch_target_attesters: u64, /// The total effective balance of all validators who attested during the _previous_ epoch and /// agreed with the state about the beacon block at the time of attestation. - pub previous_epoch_head_attesters: u64, + previous_epoch_head_attesters: u64, +} + +// Generate a safe accessor for a balance in `TotalBalances`, as per spec `get_total_balance`. +macro_rules! balance_accessor { + ($field_name:ident) => { + pub fn $field_name(&self) -> u64 { + std::cmp::max(self.effective_balance_increment, self.$field_name) + } + }; +} + +impl TotalBalances { + pub fn new(spec: &ChainSpec) -> Self { + Self { + effective_balance_increment: spec.effective_balance_increment, + current_epoch: 0, + previous_epoch: 0, + current_epoch_attesters: 0, + current_epoch_target_attesters: 0, + previous_epoch_attesters: 0, + previous_epoch_target_attesters: 0, + previous_epoch_head_attesters: 0, + } + } + + balance_accessor!(current_epoch); + balance_accessor!(previous_epoch); + balance_accessor!(current_epoch_attesters); + balance_accessor!(current_epoch_target_attesters); + balance_accessor!(previous_epoch_attesters); + balance_accessor!(previous_epoch_target_attesters); + balance_accessor!(previous_epoch_head_attesters); } /// Summarised information about validator participation in the _previous and _current_ epochs of /// some `BeaconState`. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ValidatorStatuses { /// Information about each individual validator from the state's validator registry. pub statuses: Vec, @@ -144,13 +178,13 @@ impl ValidatorStatuses { /// - Active validators /// - Total balances for the current and previous epochs. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn new( state: &BeaconState, spec: &ChainSpec, ) -> Result { let mut statuses = Vec::with_capacity(state.validators.len()); - let mut total_balances = TotalBalances::default(); + let mut total_balances = TotalBalances::new(spec); for (i, validator) in state.validators.iter().enumerate() { let effective_balance = state.get_effective_balance(i, spec)?; @@ -184,7 +218,7 @@ impl ValidatorStatuses { /// Process some attestations from the given `state` updating the `statuses` and /// `total_balances` fields. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn process_attestations( &mut self, state: &BeaconState, @@ -221,10 +255,10 @@ impl ValidatorStatuses { if target_matches_epoch_start_block(a, state, state.previous_epoch())? { status.is_previous_epoch_target_attester = true; - } - if has_common_beacon_block_root(a, state)? { - status.is_previous_epoch_head_attester = true; + if has_common_beacon_block_root(a, state)? { + status.is_previous_epoch_head_attester = true; + } } } @@ -265,7 +299,7 @@ impl ValidatorStatuses { /// Returns `true` if the attestation's FFG target is equal to the hash of the `state`'s first /// beacon block in the given `epoch`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 fn target_matches_epoch_start_block( a: &PendingAttestation, state: &BeaconState, @@ -280,7 +314,7 @@ fn target_matches_epoch_start_block( /// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for /// the current slot of the `PendingAttestation`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 fn has_common_beacon_block_root( a: &PendingAttestation, state: &BeaconState, diff --git a/eth2/state_processing/src/per_slot_processing.rs b/eth2/state_processing/src/per_slot_processing.rs index 42f3ae0ccc..a98dc545bf 100644 --- a/eth2/state_processing/src/per_slot_processing.rs +++ b/eth2/state_processing/src/per_slot_processing.rs @@ -13,7 +13,7 @@ pub enum Error { /// `state_root` is `None`, the root of `state` will be computed using a cached tree hash. /// Providing the `state_root` makes this function several orders of magniude faster. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn per_slot_processing( state: &mut BeaconState, state_root: Option, diff --git a/eth2/state_processing/src/test_utils.rs b/eth2/state_processing/src/test_utils.rs index 438a10b449..f18a8e70b5 100644 --- a/eth2/state_processing/src/test_utils.rs +++ b/eth2/state_processing/src/test_utils.rs @@ -62,7 +62,14 @@ impl BlockBuilder { let proposer_keypair = &keypairs[proposer_index]; - builder.set_randao_reveal(&proposer_keypair.sk, &state.fork, spec); + builder.set_proposer_index(proposer_index as u64); + + builder.set_randao_reveal( + &proposer_keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); let parent_root = state.latest_block_header.canonical_root(); builder.set_parent_root(parent_root); @@ -79,6 +86,7 @@ impl BlockBuilder { validator_index, &keypairs[validator_index as usize].sk, &state.fork, + state.genesis_validators_root, spec, ); } @@ -106,6 +114,7 @@ impl BlockBuilder { &attesters, &secret_keys, &state.fork, + state.genesis_validators_root, spec, ); } @@ -161,9 +170,12 @@ impl BlockBuilder { // Set the eth1 data to be different from the state. self.block_builder.block.body.eth1_data.block_hash = Hash256::from_slice(&[42; 32]); - let block = self - .block_builder - .build(&proposer_keypair.sk, &state.fork, spec); + let block = self.block_builder.build( + &proposer_keypair.sk, + &state.fork, + state.genesis_validators_root, + spec, + ); (block, state) } diff --git a/eth2/types/src/attestation.rs b/eth2/types/src/attestation.rs index 40b479853c..6372bb7846 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -2,7 +2,7 @@ use super::{ AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey, Signature, SignedRoot, }; -use crate::test_utils::TestRandom; +use crate::{test_utils::TestRandom, Hash256}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -17,7 +17,7 @@ pub enum Error { /// Details an attestation that can be slashable. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct Attestation { @@ -53,6 +53,7 @@ impl Attestation { secret_key: &SecretKey, committee_position: usize, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> Result<(), Error> { if self @@ -66,7 +67,12 @@ impl Attestation { .set(committee_position, true) .map_err(Error::SszTypesError)?; - let domain = spec.get_domain(self.data.target.epoch, Domain::BeaconAttester, fork); + let domain = spec.get_domain( + self.data.target.epoch, + Domain::BeaconAttester, + fork, + genesis_validators_root, + ); let message = self.data.signing_root(domain); self.signature diff --git a/eth2/types/src/attestation_data.rs b/eth2/types/src/attestation_data.rs index aab230b1ea..fc28732de2 100644 --- a/eth2/types/src/attestation_data.rs +++ b/eth2/types/src/attestation_data.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// The data upon which an attestation is based. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive( Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom, )] diff --git a/eth2/types/src/attester_slashing.rs b/eth2/types/src/attester_slashing.rs index b5466bef8d..c5aa6b5288 100644 --- a/eth2/types/src/attester_slashing.rs +++ b/eth2/types/src/attester_slashing.rs @@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash; /// Two conflicting attestations. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct AttesterSlashing { diff --git a/eth2/types/src/beacon_block.rs b/eth2/types/src/beacon_block.rs index 629d98e6b9..5c8efa85db 100644 --- a/eth2/types/src/beacon_block.rs +++ b/eth2/types/src/beacon_block.rs @@ -10,11 +10,12 @@ use tree_hash_derive::TreeHash; /// A block of the `BeaconChain`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct BeaconBlock { pub slot: Slot, + pub proposer_index: u64, pub parent_root: Hash256, pub state_root: Hash256, pub body: BeaconBlockBody, @@ -25,10 +26,11 @@ impl SignedRoot for BeaconBlock {} impl BeaconBlock { /// Returns an empty block to be used during genesis. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn empty(spec: &ChainSpec) -> Self { BeaconBlock { slot: spec.genesis_slot, + proposer_index: 0, parent_root: Hash256::zero(), state_root: Hash256::zero(), body: BeaconBlockBody { @@ -55,7 +57,7 @@ impl BeaconBlock { /// Returns the `tree_hash_root` of the block. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.tree_hash_root()[..]) } @@ -67,10 +69,11 @@ impl BeaconBlock { /// /// Note: performs a full tree-hash of `self.body`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn block_header(&self) -> BeaconBlockHeader { BeaconBlockHeader { slot: self.slot, + proposer_index: self.proposer_index, parent_root: self.parent_root, state_root: self.state_root, body_root: Hash256::from_slice(&self.body.tree_hash_root()[..]), @@ -79,7 +82,7 @@ impl BeaconBlock { /// Returns a "temporary" header, where the `state_root` is `Hash256::zero()`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn temporary_block_header(&self) -> BeaconBlockHeader { BeaconBlockHeader { state_root: Hash256::zero(), @@ -92,9 +95,15 @@ impl BeaconBlock { self, secret_key: &SecretKey, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> SignedBeaconBlock { - let domain = spec.get_domain(self.epoch(), Domain::BeaconProposer, fork); + let domain = spec.get_domain( + self.epoch(), + Domain::BeaconProposer, + fork, + genesis_validators_root, + ); let message = self.signing_root(domain); let signature = Signature::new(message.as_bytes(), secret_key); SignedBeaconBlock { diff --git a/eth2/types/src/beacon_block_body.rs b/eth2/types/src/beacon_block_body.rs index fcc419b15d..643db5ecac 100644 --- a/eth2/types/src/beacon_block_body.rs +++ b/eth2/types/src/beacon_block_body.rs @@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash; /// The body of a `BeaconChain` block, containing operations. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct BeaconBlockBody { diff --git a/eth2/types/src/beacon_block_header.rs b/eth2/types/src/beacon_block_header.rs index f93a9fa4e1..fb2f532b12 100644 --- a/eth2/types/src/beacon_block_header.rs +++ b/eth2/types/src/beacon_block_header.rs @@ -9,10 +9,11 @@ use tree_hash_derive::TreeHash; /// A header of a `BeaconBlock`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct BeaconBlockHeader { pub slot: Slot, + pub proposer_index: u64, pub parent_root: Hash256, pub state_root: Hash256, pub body_root: Hash256, @@ -23,17 +24,18 @@ impl SignedRoot for BeaconBlockHeader {} impl BeaconBlockHeader { /// Returns the `tree_hash_root` of the header. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.tree_hash_root()[..]) } /// Given a `body`, consumes `self` and returns a complete `BeaconBlock`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn into_block(self, body: BeaconBlockBody) -> BeaconBlock { BeaconBlock { slot: self.slot, + proposer_index: self.proposer_index, parent_root: self.parent_root, state_root: self.state_root, body, @@ -45,10 +47,11 @@ impl BeaconBlockHeader { self, secret_key: &SecretKey, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> SignedBeaconBlockHeader { let epoch = self.slot.epoch(E::slots_per_epoch()); - let domain = spec.get_domain(epoch, Domain::BeaconProposer, fork); + let domain = spec.get_domain(epoch, Domain::BeaconProposer, fork, genesis_validators_root); let message = self.signing_root(domain); let signature = Signature::new(message.as_bytes(), secret_key); SignedBeaconBlockHeader { diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 45c71774f9..b4a79e5336 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -91,7 +91,7 @@ impl AllowNextEpoch { /// The state of the `BeaconChain` at some slot. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive( Debug, PartialEq, @@ -111,6 +111,7 @@ where { // Versioning pub genesis_time: u64, + pub genesis_validators_root: Hash256, pub slot: Slot, pub fork: Fork, @@ -182,11 +183,12 @@ impl BeaconState { /// /// Not a complete genesis state, see `initialize_beacon_state_from_eth1`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn new(genesis_time: u64, eth1_data: Eth1Data, spec: &ChainSpec) -> Self { BeaconState { // Versioning genesis_time, + genesis_validators_root: Hash256::zero(), // Set later. slot: spec.genesis_slot, fork: Fork { previous_version: spec.genesis_fork_version, @@ -239,7 +241,7 @@ impl BeaconState { /// Returns the `tree_hash_root` of the state. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.tree_hash_root()[..]) } @@ -268,7 +270,7 @@ impl BeaconState { /// The epoch corresponding to `self.slot`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn current_epoch(&self) -> Epoch { self.slot.epoch(T::slots_per_epoch()) } @@ -277,7 +279,7 @@ impl BeaconState { /// /// If the current epoch is the genesis epoch, the genesis_epoch is returned. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn previous_epoch(&self) -> Epoch { let current_epoch = self.current_epoch(); if current_epoch > T::genesis_epoch() { @@ -289,7 +291,7 @@ impl BeaconState { /// The epoch following `self.current_epoch()`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn next_epoch(&self) -> Epoch { self.current_epoch() + 1 } @@ -298,7 +300,7 @@ impl BeaconState { /// /// Makes use of the committee cache and will fail if no cache exists for the slot's epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_committee_count_at_slot(&self, slot: Slot) -> Result { let cache = self.committee_cache_at_slot(slot)?; Ok(cache.committees_per_slot() as u64) @@ -306,7 +308,7 @@ impl BeaconState { /// Compute the number of committees in an entire epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { let cache = self.committee_cache(relative_epoch)?; Ok(cache.epoch_committee_count() as u64) @@ -330,7 +332,7 @@ impl BeaconState { /// /// Does not utilize the cache, performs a full iteration over the validator registry. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec { get_active_validator_indices(&self.validators, epoch) } @@ -350,7 +352,7 @@ impl BeaconState { /// /// Utilises the committee cache. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_beacon_committee( &self, slot: Slot, @@ -369,7 +371,7 @@ impl BeaconState { /// /// Utilises the committee cache. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_beacon_committees_at_slot(&self, slot: Slot) -> Result, Error> { let cache = self.committee_cache_at_slot(slot)?; cache.get_beacon_committees_at_slot(slot) @@ -379,7 +381,7 @@ impl BeaconState { /// /// Utilises the committee cache. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_beacon_committees_at_epoch( &self, relative_epoch: RelativeEpoch, @@ -390,7 +392,7 @@ impl BeaconState { /// Compute the proposer (not necessarily for the Beacon chain) from a list of indices. /// - /// Spec v0.10.1 + /// Spec v0.11.1 // NOTE: be sure to test this bad boy. pub fn compute_proposer_index( &self, @@ -429,7 +431,7 @@ impl BeaconState { /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result { let epoch = slot.epoch(T::slots_per_epoch()); let seed = self.get_beacon_proposer_seed(slot, spec)?; @@ -440,7 +442,7 @@ impl BeaconState { /// Compute the seed to use for the beacon proposer selection at the given `slot`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn get_beacon_proposer_seed(&self, slot: Slot, spec: &ChainSpec) -> Result, Error> { let epoch = slot.epoch(T::slots_per_epoch()); let mut preimage = self @@ -455,7 +457,7 @@ impl BeaconState { /// /// It needs filling in on all slots where there isn't a skip. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_latest_block_root(&self, current_state_root: Hash256) -> Hash256 { if self.latest_block_header.state_root.is_zero() { let mut latest_block_header = self.latest_block_header.clone(); @@ -468,7 +470,7 @@ impl BeaconState { /// Safely obtains the index for latest block roots, given some `slot`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn get_latest_block_roots_index(&self, slot: Slot) -> Result { if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) { Ok(slot.as_usize() % self.block_roots.len()) @@ -479,7 +481,7 @@ impl BeaconState { /// Return the block root at a recent `slot`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> { let i = self.get_latest_block_roots_index(slot)?; Ok(&self.block_roots[i]) @@ -487,7 +489,7 @@ impl BeaconState { /// Return the block root at a recent `epoch`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 // NOTE: the spec calls this get_block_root pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> { self.get_block_root(epoch.start_slot(T::slots_per_epoch())) @@ -495,7 +497,7 @@ impl BeaconState { /// Sets the block root for some given slot. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn set_block_root( &mut self, slot: Slot, @@ -513,7 +515,7 @@ impl BeaconState { /// Safely obtains the index for `randao_mixes` /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn get_randao_mix_index( &self, epoch: Epoch, @@ -535,7 +537,7 @@ impl BeaconState { /// /// See `Self::get_randao_mix`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> { let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize(); @@ -548,7 +550,7 @@ impl BeaconState { /// Return the randao mix at a recent ``epoch``. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> { let i = self.get_randao_mix_index(epoch, AllowNextEpoch::False)?; Ok(&self.randao_mixes[i]) @@ -556,7 +558,7 @@ impl BeaconState { /// Set the randao mix at a recent ``epoch``. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> { let i = self.get_randao_mix_index(epoch, AllowNextEpoch::True)?; self.randao_mixes[i] = mix; @@ -565,7 +567,7 @@ impl BeaconState { /// Safely obtains the index for latest state roots, given some `slot`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn get_latest_state_roots_index(&self, slot: Slot) -> Result { if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) { Ok(slot.as_usize() % self.state_roots.len()) @@ -576,7 +578,7 @@ impl BeaconState { /// Gets the state root for some slot. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> { let i = self.get_latest_state_roots_index(slot)?; Ok(&self.state_roots[i]) @@ -584,7 +586,7 @@ impl BeaconState { /// Gets the oldest (earliest slot) state root. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> { let i = self.get_latest_state_roots_index(self.slot - Slot::from(self.state_roots.len()))?; @@ -593,7 +595,7 @@ impl BeaconState { /// Gets the oldest (earliest slot) block root. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_oldest_block_root(&self) -> Result<&Hash256, Error> { let i = self.get_latest_block_roots_index(self.slot - self.block_roots.len() as u64)?; Ok(&self.block_roots[i]) @@ -601,7 +603,7 @@ impl BeaconState { /// Sets the latest state root for slot. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> { let i = self.get_latest_state_roots_index(slot)?; self.state_roots[i] = state_root; @@ -610,7 +612,7 @@ impl BeaconState { /// Safely obtain the index for `slashings`, given some `epoch`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn get_slashings_index( &self, epoch: Epoch, @@ -630,14 +632,14 @@ impl BeaconState { /// Get a reference to the entire `slashings` vector. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_all_slashings(&self) -> &[u64] { &self.slashings } /// Get the total slashed balances for some epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_slashings(&self, epoch: Epoch) -> Result { let i = self.get_slashings_index(epoch, AllowNextEpoch::False)?; Ok(self.slashings[i]) @@ -645,7 +647,7 @@ impl BeaconState { /// Set the total slashed balances for some epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), Error> { let i = self.get_slashings_index(epoch, AllowNextEpoch::True)?; self.slashings[i] = value; @@ -654,7 +656,7 @@ impl BeaconState { /// Get the attestations from the current or previous epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_matching_source_attestations( &self, epoch: Epoch, @@ -670,7 +672,7 @@ impl BeaconState { /// Generate a seed for the given `epoch`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_seed( &self, epoch: Epoch, @@ -701,7 +703,7 @@ impl BeaconState { /// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_effective_balance( &self, validator_index: usize, @@ -715,7 +717,7 @@ impl BeaconState { /// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn compute_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch { epoch + 1 + spec.max_seed_lookahead } @@ -724,7 +726,7 @@ impl BeaconState { /// /// Uses the epoch cache, and will error if it isn't initialized. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result { Ok(std::cmp::max( spec.min_per_epoch_churn_limit, @@ -739,7 +741,7 @@ impl BeaconState { /// /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_attestation_duties( &self, validator_index: usize, @@ -752,7 +754,7 @@ impl BeaconState { /// Return the combined effective balance of an array of validators. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_total_balance( &self, validator_indices: &[usize], @@ -925,7 +927,26 @@ impl BeaconState { if let Some(mut cache) = cache { // Note: we return early if the tree hash fails, leaving `self.tree_hash_cache` as // None. There's no need to keep a cache that fails. - let root = cache.recalculate_tree_hash_root(self)?; + let root = cache.recalculate_tree_hash_root(&self)?; + self.tree_hash_cache = Some(cache); + Ok(root) + } else { + Err(Error::TreeHashCacheNotInitialized) + } + } + + /// Compute the tree hash root of the validators using the tree hash cache. + /// + /// Initialize the tree hash cache if it isn't already initialized. + pub fn update_validators_tree_hash_cache(&mut self) -> Result { + self.initialize_tree_hash_cache(); + + let cache = self.tree_hash_cache.take(); + + if let Some(mut cache) = cache { + // Note: we return early if the tree hash fails, leaving `self.tree_hash_cache` as + // None. There's no need to keep a cache that fails. + let root = cache.recalculate_validators_tree_hash_root(&self.validators)?; self.tree_hash_cache = Some(cache); Ok(root) } else { @@ -959,6 +980,7 @@ impl BeaconState { pub fn clone_with(&self, config: CloneConfig) -> Self { BeaconState { genesis_time: self.genesis_time, + genesis_validators_root: self.genesis_validators_root, slot: self.slot, fork: self.fork.clone(), latest_block_header: self.latest_block_header.clone(), diff --git a/eth2/types/src/beacon_state/committee_cache.rs b/eth2/types/src/beacon_state/committee_cache.rs index 099defd7ce..5783bdbc4c 100644 --- a/eth2/types/src/beacon_state/committee_cache.rs +++ b/eth2/types/src/beacon_state/committee_cache.rs @@ -22,7 +22,7 @@ pub struct CommitteeCache { impl CommitteeCache { /// Return a new, fully initialized cache. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn initialized( state: &BeaconState, epoch: Epoch, @@ -87,7 +87,7 @@ impl CommitteeCache { /// /// Always returns `&[]` for a non-initialized epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn active_validator_indices(&self) -> &[usize] { &self.shuffling } @@ -96,7 +96,7 @@ impl CommitteeCache { /// /// Always returns `&[]` for a non-initialized epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn shuffling(&self) -> &[usize] { &self.shuffling } @@ -202,7 +202,7 @@ impl CommitteeCache { /// /// Always returns `usize::default()` for a non-initialized epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn active_validator_count(&self) -> usize { self.shuffling.len() } @@ -211,7 +211,7 @@ impl CommitteeCache { /// /// Always returns `usize::default()` for a non-initialized epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn epoch_committee_count(&self) -> usize { self.committees_per_slot as usize * self.slots_per_epoch as usize } @@ -223,7 +223,7 @@ impl CommitteeCache { /// Returns a slice of `self.shuffling` that represents the `index`'th committee in the epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn compute_committee(&self, index: usize) -> Option<&[usize]> { Some(&self.shuffling[self.compute_committee_range(index)?]) } @@ -234,7 +234,7 @@ impl CommitteeCache { /// /// Will also return `None` if the index is out of bounds. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn compute_committee_range(&self, index: usize) -> Option> { let count = self.epoch_committee_count(); if count == 0 || index >= count { @@ -261,7 +261,7 @@ impl CommitteeCache { /// Returns a list of all `validators` indices where the validator is active at the given /// `epoch`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec { let mut active = Vec::with_capacity(validators.len()); diff --git a/eth2/types/src/beacon_state/tree_hash_cache.rs b/eth2/types/src/beacon_state/tree_hash_cache.rs index 4449827acc..75b14e8eef 100644 --- a/eth2/types/src/beacon_state/tree_hash_cache.rs +++ b/eth2/types/src/beacon_state/tree_hash_cache.rs @@ -82,6 +82,7 @@ impl BeaconTreeHashCache { let mut hasher = MerkleHasher::with_leaves(NUM_BEACON_STATE_HASHING_FIELDS); hasher.write(state.genesis_time.tree_hash_root().as_bytes())?; + hasher.write(state.genesis_validators_root.tree_hash_root().as_bytes())?; hasher.write(state.slot.tree_hash_root().as_bytes())?; hasher.write(state.fork.tree_hash_root().as_bytes())?; hasher.write(state.latest_block_header.tree_hash_root().as_bytes())?; @@ -153,6 +154,14 @@ impl BeaconTreeHashCache { hasher.finish().map_err(Into::into) } + + /// Updates the cache and provides the root of the given `validators`. + pub fn recalculate_validators_tree_hash_root( + &mut self, + validators: &[Validator], + ) -> Result { + self.validators.recalculate_tree_hash_root(validators) + } } /// A specialized cache for computing the tree hash root of `state.validators`. diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 188d5842a5..f82d2b352f 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -3,6 +3,7 @@ use int_to_bytes::int_to_bytes4; use serde_derive::{Deserialize, Serialize}; use std::fs::File; use std::path::Path; +use tree_hash::TreeHash; use utils::{ fork_from_hex_str, fork_to_hex_str, u32_from_hex_str, u32_to_hex_str, u8_from_hex_str, u8_to_hex_str, @@ -10,18 +11,21 @@ use utils::{ /// Each of the BLS signature domains. /// -/// Spec v0.10.1 +/// Spec v0.11.1 +#[derive(Debug, PartialEq, Clone, Copy)] pub enum Domain { BeaconProposer, BeaconAttester, Randao, Deposit, VoluntaryExit, + SelectionProof, + AggregateAndProof, } /// Holds all the "constants" for a BeaconChain. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] #[serde(default)] pub struct ChainSpec { @@ -44,6 +48,9 @@ pub struct ChainSpec { pub shuffle_round_count: u8, pub min_genesis_active_validator_count: u64, pub min_genesis_time: u64, + pub hysteresis_quotient: u64, + pub hysteresis_downward_multiplier: u64, + pub hysteresis_upward_multiplier: u64, /* * Gwei values @@ -93,6 +100,8 @@ pub struct ChainSpec { domain_randao: u32, domain_deposit: u32, domain_voluntary_exit: u32, + domain_selection_proof: u32, + domain_aggregate_and_proof: u32, /* * Fork choice @@ -112,7 +121,7 @@ pub struct ChainSpec { impl ChainSpec { /// Get the domain number, unmodified by the fork. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_domain_constant(&self, domain: Domain) -> u32 { match domain { Domain::BeaconProposer => self.domain_beacon_proposer, @@ -120,15 +129,23 @@ impl ChainSpec { Domain::Randao => self.domain_randao, Domain::Deposit => self.domain_deposit, Domain::VoluntaryExit => self.domain_voluntary_exit, + Domain::SelectionProof => self.domain_selection_proof, + Domain::AggregateAndProof => self.domain_aggregate_and_proof, } } - /// Get the domain number that represents the fork meta and signature domain. + /// Get the domain that represents the fork meta and signature domain. /// - /// Spec v0.10.1 - pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 { + /// Spec v0.11.1 + pub fn get_domain( + &self, + epoch: Epoch, + domain: Domain, + fork: &Fork, + genesis_validators_root: Hash256, + ) -> Hash256 { let fork_version = fork.get_fork_version(epoch); - self.compute_domain(domain, fork_version) + self.compute_domain(domain, fork_version, genesis_validators_root) } /// Get the domain for a deposit signature. @@ -136,29 +153,64 @@ impl ChainSpec { /// Deposits are valid across forks, thus the deposit domain is computed /// with the genesis fork version. /// - /// Spec v0.10.1 - pub fn get_deposit_domain(&self) -> u64 { - self.compute_domain(Domain::Deposit, self.genesis_fork_version) + /// Spec v0.11.1 + pub fn get_deposit_domain(&self) -> Hash256 { + self.compute_domain(Domain::Deposit, self.genesis_fork_version, Hash256::zero()) + } + + /// Return the 32-byte fork data root for the `current_version` and `genesis_validators_root`. + /// + /// This is used primarily in signature domains to avoid collisions across forks/chains. + /// + /// Spec v0.11.1 + pub fn compute_fork_data_root( + current_version: [u8; 4], + genesis_validators_root: Hash256, + ) -> Hash256 { + ForkData { + current_version, + genesis_validators_root, + } + .tree_hash_root() + } + + /// Return the 4-byte fork digest for the `current_version` and `genesis_validators_root`. + /// + /// This is a digest primarily used for domain separation on the p2p layer. + /// 4-bytes suffices for practical separation of forks/chains. + pub fn compute_fork_digest( + current_version: [u8; 4], + genesis_validators_root: Hash256, + ) -> [u8; 4] { + let mut result = [0; 4]; + let root = Self::compute_fork_data_root(current_version, genesis_validators_root); + result.copy_from_slice(&root.as_bytes()[0..4]); + result } /// Compute a domain by applying the given `fork_version`. /// - /// Spec v0.10.1 - pub fn compute_domain(&self, domain: Domain, fork_version: [u8; 4]) -> u64 { + /// Spec v0.11.1 + pub fn compute_domain( + &self, + domain: Domain, + fork_version: [u8; 4], + genesis_validators_root: Hash256, + ) -> Hash256 { let domain_constant = self.get_domain_constant(domain); - let mut bytes: Vec = int_to_bytes4(domain_constant); - bytes.append(&mut fork_version.to_vec()); + let mut domain = [0; 32]; + domain[0..4].copy_from_slice(&int_to_bytes4(domain_constant)); + domain[4..].copy_from_slice( + &Self::compute_fork_data_root(fork_version, genesis_validators_root)[..28], + ); - let mut fork_and_domain = [0; 8]; - fork_and_domain.copy_from_slice(&bytes); - - u64::from_le_bytes(fork_and_domain) + Hash256::from(domain) } /// Returns a `ChainSpec` compatible with the Ethereum Foundation specification. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn mainnet() -> Self { Self { /* @@ -179,6 +231,9 @@ impl ChainSpec { shuffle_round_count: 90, min_genesis_active_validator_count: 16_384, min_genesis_time: 1_578_009_600, // Jan 3, 2020 + hysteresis_quotient: 4, + hysteresis_downward_multiplier: 1, + hysteresis_upward_multiplier: 5, /* * Gwei values @@ -223,6 +278,8 @@ impl ChainSpec { domain_randao: 2, domain_deposit: 3, domain_voluntary_exit: 4, + domain_selection_proof: 5, + domain_aggregate_and_proof: 6, /* * Fork choice @@ -245,7 +302,7 @@ impl ChainSpec { /// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn minimal() -> Self { // Note: bootnodes to be updated when static nodes exist. let boot_nodes = vec![]; @@ -257,6 +314,7 @@ impl ChainSpec { min_genesis_active_validator_count: 64, eth1_follow_distance: 16, genesis_fork_version: [0x00, 0x00, 0x00, 0x01], + persistent_committee_period: 128, min_genesis_delay: 300, milliseconds_per_slot: 6_000, network_id: 2, // lighthouse testnet network id @@ -291,7 +349,6 @@ impl Default for ChainSpec { #[cfg(test)] mod tests { use super::*; - use int_to_bytes::int_to_bytes8; #[test] fn test_mainnet_spec_can_be_constructed() { @@ -299,19 +356,27 @@ mod tests { } fn test_domain(domain_type: Domain, raw_domain: u32, spec: &ChainSpec) { + let previous_version = [0, 0, 0, 1]; + let current_version = [0, 0, 0, 2]; + let genesis_validators_root = Hash256::from_low_u64_le(77); + let fork_epoch = Epoch::new(1024); let fork = Fork { - previous_version: spec.genesis_fork_version, - current_version: spec.genesis_fork_version, - epoch: MinimalEthSpec::genesis_epoch(), + previous_version, + current_version, + epoch: fork_epoch, }; - let epoch = Epoch::new(0); - let domain = spec.get_domain(epoch, domain_type, &fork); + for (epoch, version) in vec![ + (fork_epoch - 1, previous_version), + (fork_epoch, current_version), + (fork_epoch + 1, current_version), + ] { + let domain1 = spec.get_domain(epoch, domain_type, &fork, genesis_validators_root); + let domain2 = spec.compute_domain(domain_type, version, genesis_validators_root); - let mut expected = int_to_bytes4(raw_domain); - expected.append(&mut fork.get_fork_version(epoch).to_vec()); - - assert_eq!(int_to_bytes8(domain), expected); + assert_eq!(domain1, domain2); + assert_eq!(&domain1.as_bytes()[0..4], &int_to_bytes4(raw_domain)[..]); + } } #[test] @@ -323,18 +388,25 @@ mod tests { test_domain(Domain::Randao, spec.domain_randao, &spec); test_domain(Domain::Deposit, spec.domain_deposit, &spec); test_domain(Domain::VoluntaryExit, spec.domain_voluntary_exit, &spec); + test_domain(Domain::SelectionProof, spec.domain_selection_proof, &spec); + test_domain( + Domain::AggregateAndProof, + spec.domain_aggregate_and_proof, + &spec, + ); } } /// Union of a ChainSpec struct and an EthSpec struct that holds constants used for the configs /// from the Ethereum 2 specs repo (https://github.com/ethereum/eth2.0-specs/tree/dev/configs) /// -/// Spec v0.10.1 +/// Doesn't include fields of the YAML that we don't need yet (e.g. Phase 1 stuff). +/// +/// Spec v0.11.1 // Yaml Config is declared here in order to access domain fields of ChainSpec which are private. #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[serde(rename_all = "UPPERCASE")] #[serde(default)] -#[serde(deny_unknown_fields)] pub struct YamlConfig { // ChainSpec far_future_epoch: u64, @@ -352,6 +424,9 @@ pub struct YamlConfig { max_effective_balance: u64, ejection_balance: u64, effective_balance_increment: u64, + hysteresis_quotient: u64, + hysteresis_downward_multiplier: u64, + hysteresis_upward_multiplier: u64, genesis_slot: u64, #[serde( serialize_with = "fork_to_hex_str", @@ -403,12 +478,22 @@ pub struct YamlConfig { deserialize_with = "u32_from_hex_str", serialize_with = "u32_to_hex_str" )] + domain_selection_proof: u32, + #[serde( + deserialize_with = "u32_from_hex_str", + serialize_with = "u32_to_hex_str" + )] + domain_aggregate_and_proof: u32, + #[serde( + deserialize_with = "u32_from_hex_str", + serialize_with = "u32_to_hex_str" + )] // EthSpec justification_bits_length: u32, max_validators_per_committee: u32, genesis_epoch: Epoch, slots_per_epoch: u64, - slots_per_eth1_voting_period: usize, + epochs_per_eth1_voting_period: u64, slots_per_historical_root: usize, epochs_per_historical_vector: usize, epochs_per_slashings_vector: usize, @@ -426,34 +511,6 @@ pub struct YamlConfig { random_subnets_per_validator: u64, epochs_per_random_subnet_subscription: u64, seconds_per_eth1_block: u64, - - // Deposit Contract (unused) - #[serde(skip_serializing)] - deposit_contract_address: String, - - // Phase 1 - #[serde(skip_serializing)] - epochs_per_custody_period: u32, - #[serde(skip_serializing)] - custody_period_to_randao_padding: u32, - #[serde(skip_serializing)] - shard_slots_per_beacon_slot: u32, - #[serde(skip_serializing)] - epochs_per_shard_period: u32, - #[serde(skip_serializing)] - phase_1_fork_epoch: u32, - #[serde(skip_serializing)] - phase_1_fork_slot: u32, - #[serde(skip_serializing)] - domain_custody_bit_challenge: u32, - #[serde(skip_serializing)] - domain_shard_proposer: u32, - #[serde(skip_serializing)] - domain_shard_attester: u32, - #[serde(skip_serializing)] - max_epochs_per_crosslink: u64, - #[serde(skip_serializing)] - early_derived_secret_penalty_max_future_epochs: u32, } impl Default for YamlConfig { @@ -463,7 +520,7 @@ impl Default for YamlConfig { } } -/// Spec v0.10.1 +/// Spec v0.11.1 impl YamlConfig { pub fn from_spec(spec: &ChainSpec) -> Self { Self { @@ -483,6 +540,9 @@ impl YamlConfig { max_effective_balance: spec.max_effective_balance, ejection_balance: spec.ejection_balance, effective_balance_increment: spec.effective_balance_increment, + hysteresis_quotient: spec.hysteresis_quotient, + hysteresis_downward_multiplier: spec.hysteresis_downward_multiplier, + hysteresis_upward_multiplier: spec.hysteresis_upward_multiplier, genesis_slot: spec.genesis_slot.into(), bls_withdrawal_prefix: spec.bls_withdrawal_prefix_byte, seconds_per_slot: spec.milliseconds_per_slot / 1000, @@ -504,13 +564,15 @@ impl YamlConfig { domain_randao: spec.domain_randao, domain_deposit: spec.domain_deposit, domain_voluntary_exit: spec.domain_voluntary_exit, + domain_selection_proof: spec.domain_selection_proof, + domain_aggregate_and_proof: spec.domain_aggregate_and_proof, // EthSpec justification_bits_length: T::JustificationBitsLength::to_u32(), max_validators_per_committee: T::MaxValidatorsPerCommittee::to_u32(), genesis_epoch: T::genesis_epoch(), slots_per_epoch: T::slots_per_epoch(), - slots_per_eth1_voting_period: T::slots_per_eth1_voting_period(), + epochs_per_eth1_voting_period: T::EpochsPerEth1VotingPeriod::to_u64(), slots_per_historical_root: T::slots_per_historical_root(), epochs_per_historical_vector: T::epochs_per_historical_vector(), epochs_per_slashings_vector: T::EpochsPerSlashingsVector::to_usize(), @@ -528,22 +590,6 @@ impl YamlConfig { random_subnets_per_validator: 0, epochs_per_random_subnet_subscription: 0, seconds_per_eth1_block: spec.seconds_per_eth1_block, - - // Deposit Contract (unused) - deposit_contract_address: String::new(), - - // Phase 1 - epochs_per_custody_period: 0, - custody_period_to_randao_padding: 0, - shard_slots_per_beacon_slot: 0, - epochs_per_shard_period: 0, - phase_1_fork_epoch: 0, - phase_1_fork_slot: 0, - domain_custody_bit_challenge: 0, - domain_shard_proposer: 0, - domain_shard_attester: 0, - max_epochs_per_crosslink: 0, - early_derived_secret_penalty_max_future_epochs: 0, } } @@ -560,7 +606,7 @@ impl YamlConfig { || self.max_validators_per_committee != T::MaxValidatorsPerCommittee::to_u32() || self.genesis_epoch != T::genesis_epoch() || self.slots_per_epoch != T::slots_per_epoch() - || self.slots_per_eth1_voting_period != T::slots_per_eth1_voting_period() + || self.epochs_per_eth1_voting_period != T::EpochsPerEth1VotingPeriod::to_u64() || self.slots_per_historical_root != T::slots_per_historical_root() || self.epochs_per_historical_vector != T::epochs_per_historical_vector() || self.epochs_per_slashings_vector != T::EpochsPerSlashingsVector::to_usize() @@ -589,6 +635,9 @@ impl YamlConfig { min_deposit_amount: self.min_deposit_amount, min_genesis_delay: self.min_genesis_delay, max_effective_balance: self.max_effective_balance, + hysteresis_quotient: self.hysteresis_quotient, + hysteresis_downward_multiplier: self.hysteresis_downward_multiplier, + hysteresis_upward_multiplier: self.hysteresis_upward_multiplier, ejection_balance: self.ejection_balance, effective_balance_increment: self.effective_balance_increment, genesis_slot: Slot::from(self.genesis_slot), @@ -608,6 +657,7 @@ impl YamlConfig { inactivity_penalty_quotient: self.inactivity_penalty_quotient, min_slashing_penalty_quotient: self.min_slashing_penalty_quotient, domain_beacon_proposer: self.domain_beacon_proposer, + domain_beacon_attester: self.domain_beacon_attester, domain_randao: self.domain_randao, domain_deposit: self.domain_deposit, domain_voluntary_exit: self.domain_voluntary_exit, diff --git a/eth2/types/src/checkpoint.rs b/eth2/types/src/checkpoint.rs index a1cf187e7d..45321d6ff5 100644 --- a/eth2/types/src/checkpoint.rs +++ b/eth2/types/src/checkpoint.rs @@ -7,7 +7,7 @@ use tree_hash_derive::TreeHash; /// Casper FFG checkpoint, used in attestations. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive( Debug, Clone, diff --git a/eth2/types/src/deposit.rs b/eth2/types/src/deposit.rs index d6781559ec..5cd8482448 100644 --- a/eth2/types/src/deposit.rs +++ b/eth2/types/src/deposit.rs @@ -11,7 +11,7 @@ pub const DEPOSIT_TREE_DEPTH: usize = 32; /// A deposit to potentially become a beacon chain validator. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct Deposit { pub proof: FixedVector, diff --git a/eth2/types/src/deposit_data.rs b/eth2/types/src/deposit_data.rs index a9607513e4..14019a7b6d 100644 --- a/eth2/types/src/deposit_data.rs +++ b/eth2/types/src/deposit_data.rs @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash; /// The data supplied by the user to the deposit contract. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct DepositData { pub pubkey: PublicKeyBytes, @@ -21,7 +21,7 @@ pub struct DepositData { impl DepositData { /// Create a `DepositMessage` corresponding to this `DepositData`, for signature verification. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn as_deposit_message(&self) -> DepositMessage { DepositMessage { pubkey: self.pubkey.clone(), @@ -32,7 +32,7 @@ impl DepositData { /// Generate the signature for a given DepositData details. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn create_signature(&self, secret_key: &SecretKey, spec: &ChainSpec) -> SignatureBytes { let domain = spec.get_deposit_domain(); let msg = self.as_deposit_message().signing_root(domain); diff --git a/eth2/types/src/deposit_message.rs b/eth2/types/src/deposit_message.rs index 9a25c1651e..3ed43122dc 100644 --- a/eth2/types/src/deposit_message.rs +++ b/eth2/types/src/deposit_message.rs @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash; /// The data supplied by the user to the deposit contract. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct DepositMessage { pub pubkey: PublicKeyBytes, diff --git a/eth2/types/src/eth1_data.rs b/eth2/types/src/eth1_data.rs index 9c1215a438..5ba6870e7e 100644 --- a/eth2/types/src/eth1_data.rs +++ b/eth2/types/src/eth1_data.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// Contains data obtained from the Eth1 chain. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive( Debug, PartialEq, diff --git a/eth2/types/src/eth_spec.rs b/eth2/types/src/eth_spec.rs index 5a86d390dd..b19d836c44 100644 --- a/eth2/types/src/eth_spec.rs +++ b/eth2/types/src/eth_spec.rs @@ -1,7 +1,7 @@ use crate::*; use serde_derive::{Deserialize, Serialize}; use ssz_types::typenum::{ - Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U2048, U32, U4, U4096, U64, + Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U2, U2048, U32, U4, U4096, U64, U65536, U8, U8192, }; use std::fmt::Debug; @@ -20,7 +20,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { * Time parameters */ type SlotsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq; - type SlotsPerEth1VotingPeriod: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type EpochsPerEth1VotingPeriod: Unsigned + Clone + Sync + Send + Debug + PartialEq; type SlotsPerHistoricalRoot: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * State list lengths @@ -43,9 +43,13 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { /// The length of the `{previous,current}_epoch_attestations` lists. /// /// Must be set to `MaxAttestations * SlotsPerEpoch` - // NOTE: we could safely instantiate this by using type-level arithmetic, but doing + // NOTE: we could safely instantiate these by using type-level arithmetic, but doing // so adds ~25s to the time required to type-check this crate type MaxPendingAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq; + /// The length of `eth1_data_votes`. + /// + /// Must be set to `EpochsPerEth1VotingPeriod * SlotsPerEpoch` + type SlotsPerEth1VotingPeriod: Unsigned + Clone + Sync + Send + Debug + PartialEq; fn default_spec() -> ChainSpec; @@ -58,7 +62,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { /// Note: the number of committees per slot is constant in each epoch, and depends only on /// the `active_validator_count` during the slot's epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn get_committee_count_per_slot(active_validator_count: usize, spec: &ChainSpec) -> usize { let slots_per_epoch = Self::SlotsPerEpoch::to_usize(); @@ -82,28 +86,28 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { /// Returns the `SLOTS_PER_EPOCH` constant for this specification. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn slots_per_epoch() -> u64 { Self::SlotsPerEpoch::to_u64() } /// Returns the `SLOTS_PER_HISTORICAL_ROOT` constant for this specification. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn slots_per_historical_root() -> usize { Self::SlotsPerHistoricalRoot::to_usize() } /// Returns the `EPOCHS_PER_HISTORICAL_VECTOR` constant for this specification. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn epochs_per_historical_vector() -> usize { Self::EpochsPerHistoricalVector::to_usize() } /// Returns the `SLOTS_PER_ETH1_VOTING_PERIOD` constant for this specification. /// - /// Spec v0.10.1 + /// Spec v0.11.1 fn slots_per_eth1_voting_period() -> usize { Self::SlotsPerEth1VotingPeriod::to_usize() } @@ -119,7 +123,7 @@ macro_rules! params_from_eth_spec { /// Ethereum Foundation specifications. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] pub struct MainnetEthSpec; @@ -128,7 +132,7 @@ impl EthSpec for MainnetEthSpec { type MaxValidatorsPerCommittee = U2048; type GenesisEpoch = U0; type SlotsPerEpoch = U32; - type SlotsPerEth1VotingPeriod = U1024; + type EpochsPerEth1VotingPeriod = U32; type SlotsPerHistoricalRoot = U8192; type EpochsPerHistoricalVector = U65536; type EpochsPerSlashingsVector = U8192; @@ -140,6 +144,7 @@ impl EthSpec for MainnetEthSpec { type MaxDeposits = U16; type MaxVoluntaryExits = U16; type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch + type SlotsPerEth1VotingPeriod = U1024; // 32 epochs * 32 slots per epoch fn default_spec() -> ChainSpec { ChainSpec::mainnet() @@ -150,17 +155,18 @@ pub type FoundationBeaconState = BeaconState; /// Ethereum Foundation minimal spec, as defined in the eth2.0-specs repo. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] pub struct MinimalEthSpec; impl EthSpec for MinimalEthSpec { type SlotsPerEpoch = U8; - type SlotsPerEth1VotingPeriod = U16; + type EpochsPerEth1VotingPeriod = U2; type SlotsPerHistoricalRoot = U64; type EpochsPerHistoricalVector = U64; type EpochsPerSlashingsVector = U64; type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch + type SlotsPerEth1VotingPeriod = U16; // 2 epochs * 8 slots per epoch params_from_eth_spec!(MainnetEthSpec { JustificationBitsLength, @@ -188,11 +194,12 @@ pub struct InteropEthSpec; impl EthSpec for InteropEthSpec { type SlotsPerEpoch = U8; + type EpochsPerEth1VotingPeriod = U2; type SlotsPerHistoricalRoot = U64; - type SlotsPerEth1VotingPeriod = U16; type EpochsPerHistoricalVector = U64; type EpochsPerSlashingsVector = U64; type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch + type SlotsPerEth1VotingPeriod = U16; // 2 epochs * 8 slots per epoch params_from_eth_spec!(MainnetEthSpec { JustificationBitsLength, diff --git a/eth2/types/src/fork.rs b/eth2/types/src/fork.rs index 1d2acc5f9a..7d9bfb1295 100644 --- a/eth2/types/src/fork.rs +++ b/eth2/types/src/fork.rs @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash; /// Specifies a fork of the `BeaconChain`, to prevent replay attacks. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive( Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, )] @@ -30,7 +30,7 @@ pub struct Fork { impl Fork { /// Return the fork version of the given ``epoch``. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn get_fork_version(&self, epoch: Epoch) -> [u8; 4] { if epoch < self.epoch { return self.previous_version; diff --git a/eth2/types/src/fork_data.rs b/eth2/types/src/fork_data.rs new file mode 100644 index 0000000000..ac5848012f --- /dev/null +++ b/eth2/types/src/fork_data.rs @@ -0,0 +1,32 @@ +use crate::test_utils::TestRandom; +use crate::utils::{fork_from_hex_str, fork_to_hex_str}; +use crate::{Hash256, SignedRoot}; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::TreeHash; + +/// Specifies a fork of the `BeaconChain`, to prevent replay attacks. +/// +/// Spec v0.11.1 +#[derive( + Debug, Clone, PartialEq, Default, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, +)] +pub struct ForkData { + #[serde( + serialize_with = "fork_to_hex_str", + deserialize_with = "fork_from_hex_str" + )] + pub current_version: [u8; 4], + pub genesis_validators_root: Hash256, +} + +impl SignedRoot for ForkData {} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_and_tree_hash_tests!(ForkData); +} diff --git a/eth2/types/src/historical_batch.rs b/eth2/types/src/historical_batch.rs index a7bd8900e4..9b7e04f558 100644 --- a/eth2/types/src/historical_batch.rs +++ b/eth2/types/src/historical_batch.rs @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash; /// Historical block and state roots. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct HistoricalBatch { pub block_roots: FixedVector, diff --git a/eth2/types/src/indexed_attestation.rs b/eth2/types/src/indexed_attestation.rs index d9d2e57720..c77b6f401e 100644 --- a/eth2/types/src/indexed_attestation.rs +++ b/eth2/types/src/indexed_attestation.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// /// To be included in an `AttesterSlashing`. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[serde(bound = "T: EthSpec")] pub struct IndexedAttestation { @@ -21,14 +21,14 @@ pub struct IndexedAttestation { impl IndexedAttestation { /// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn is_double_vote(&self, other: &Self) -> bool { self.data.target.epoch == other.data.target.epoch && self.data != other.data } /// Check if ``attestation_data_1`` surrounds ``attestation_data_2``. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn is_surround_vote(&self, other: &Self) -> bool { self.data.source.epoch < other.data.source.epoch && other.data.target.epoch < self.data.target.epoch diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 6418d19d6b..9ced60090c 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -23,6 +23,7 @@ pub mod deposit_message; pub mod eth1_data; pub mod eth_spec; pub mod fork; +pub mod fork_data; pub mod free_attestation; pub mod historical_batch; pub mod indexed_attestation; @@ -59,6 +60,7 @@ pub use crate::deposit_data::DepositData; pub use crate::deposit_message::DepositMessage; pub use crate::eth1_data::Eth1Data; pub use crate::fork::Fork; +pub use crate::fork_data::ForkData; pub use crate::free_attestation::FreeAttestation; pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::IndexedAttestation; diff --git a/eth2/types/src/pending_attestation.rs b/eth2/types/src/pending_attestation.rs index 900be9a66a..9d78d9cd69 100644 --- a/eth2/types/src/pending_attestation.rs +++ b/eth2/types/src/pending_attestation.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// An attestation that has been included in the state but not yet fully processed. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct PendingAttestation { pub aggregation_bits: BitList, diff --git a/eth2/types/src/proposer_slashing.rs b/eth2/types/src/proposer_slashing.rs index 7b4daa5e79..f82fdd4aeb 100644 --- a/eth2/types/src/proposer_slashing.rs +++ b/eth2/types/src/proposer_slashing.rs @@ -8,10 +8,9 @@ use tree_hash_derive::TreeHash; /// Two conflicting proposals from the same proposer (validator). /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct ProposerSlashing { - pub proposer_index: u64, pub signed_header_1: SignedBeaconBlockHeader, pub signed_header_2: SignedBeaconBlockHeader, } diff --git a/eth2/types/src/relative_epoch.rs b/eth2/types/src/relative_epoch.rs index 2c8e05f026..f179b0c707 100644 --- a/eth2/types/src/relative_epoch.rs +++ b/eth2/types/src/relative_epoch.rs @@ -9,7 +9,7 @@ pub enum Error { /// Defines the epochs relative to some epoch. Most useful when referring to the committees prior /// to and following some epoch. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Copy)] pub enum RelativeEpoch { /// The prior epoch. @@ -23,7 +23,7 @@ pub enum RelativeEpoch { impl RelativeEpoch { /// Returns the `epoch` that `self` refers to, with respect to the `base` epoch. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn into_epoch(self, base: Epoch) -> Epoch { match self { // Due to saturating nature of epoch, check for current first. @@ -40,7 +40,7 @@ impl RelativeEpoch { /// - `EpochTooLow` when `other` is more than 1 prior to `base`. /// - `EpochTooHigh` when `other` is more than 1 after `base`. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn from_epoch(base: Epoch, other: Epoch) -> Result { // Due to saturating nature of epoch, check for current first. if other == base { diff --git a/eth2/types/src/signed_beacon_block.rs b/eth2/types/src/signed_beacon_block.rs index b07137de53..02f376fc23 100644 --- a/eth2/types/src/signed_beacon_block.rs +++ b/eth2/types/src/signed_beacon_block.rs @@ -8,7 +8,7 @@ use tree_hash::TreeHash; /// A `BeaconBlock` and a signature from its proposer. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TestRandom)] #[serde(bound = "E: EthSpec")] pub struct SignedBeaconBlock { @@ -34,7 +34,7 @@ impl SignedBeaconBlock { /// Returns the `tree_hash_root` of the block. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.message.tree_hash_root()[..]) } diff --git a/eth2/types/src/signed_beacon_block_header.rs b/eth2/types/src/signed_beacon_block_header.rs index d478c761a3..11627b9ee6 100644 --- a/eth2/types/src/signed_beacon_block_header.rs +++ b/eth2/types/src/signed_beacon_block_header.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// An exit voluntarily submitted a validator who wishes to withdraw. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct SignedBeaconBlockHeader { pub message: BeaconBlockHeader, diff --git a/eth2/types/src/signed_voluntary_exit.rs b/eth2/types/src/signed_voluntary_exit.rs index 251840e616..6507d67961 100644 --- a/eth2/types/src/signed_voluntary_exit.rs +++ b/eth2/types/src/signed_voluntary_exit.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// An exit voluntarily submitted a validator who wishes to withdraw. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct SignedVoluntaryExit { pub message: VoluntaryExit, diff --git a/eth2/types/src/signing_root.rs b/eth2/types/src/signing_root.rs index 456064f52e..f3a96df1f6 100644 --- a/eth2/types/src/signing_root.rs +++ b/eth2/types/src/signing_root.rs @@ -9,11 +9,11 @@ use tree_hash_derive::TreeHash; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct SigningRoot { pub object_root: Hash256, - pub domain: u64, + pub domain: Hash256, } pub trait SignedRoot: TreeHash { - fn signing_root(&self, domain: u64) -> Hash256 { + fn signing_root(&self, domain: Hash256) -> Hash256 { SigningRoot { object_root: self.tree_hash_root(), domain, diff --git a/eth2/types/src/test_utils/builders/testing_attestation_builder.rs b/eth2/types/src/test_utils/builders/testing_attestation_builder.rs index f2d428a905..6c08346bb8 100644 --- a/eth2/types/src/test_utils/builders/testing_attestation_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attestation_builder.rs @@ -55,6 +55,7 @@ impl TestingAttestationBuilder { signing_validators: &[usize], secret_keys: &[&SecretKey], fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> &mut Self { assert_eq!( @@ -77,15 +78,28 @@ impl TestingAttestationBuilder { }; self.attestation - .sign(secret_keys[index], committee_index, fork, spec) + .sign( + secret_keys[index], + committee_index, + fork, + genesis_validators_root, + spec, + ) .expect("can sign attestation"); - if let AttestationTestTask::BadIndexedAttestationBadSignature = test_task { - self.attestation - .aggregation_bits - .set(committee_index, false) - .unwrap(); - } + self.attestation + .aggregation_bits + .set(committee_index, true) + .unwrap(); + } + + if test_task == AttestationTestTask::BadIndexedAttestationBadSignature { + // Flip an aggregation bit, to make the aggregate invalid + // (We also want to avoid making it completely empty) + self.attestation + .aggregation_bits + .set(0, !self.attestation.aggregation_bits.get(0).unwrap()) + .unwrap(); } self diff --git a/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs b/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs index 451424bb50..7ec3bbfbd6 100644 --- a/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs @@ -22,6 +22,7 @@ impl TestingAttesterSlashingBuilder { validator_indices: &[u64], signer: F, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> AttesterSlashing where @@ -83,8 +84,12 @@ impl TestingAttesterSlashingBuilder { }; let add_signatures = |attestation: &mut IndexedAttestation| { - let domain = - spec.get_domain(attestation.data.target.epoch, Domain::BeaconAttester, fork); + let domain = spec.get_domain( + attestation.data.target.epoch, + Domain::BeaconAttester, + fork, + genesis_validators_root, + ); let message = attestation.data.signing_root(domain); for validator_index in validator_indices { diff --git a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs index 84f2296d64..b0d0640afa 100644 --- a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs @@ -97,12 +97,23 @@ impl TestingBeaconBlockBuilder { self.block.slot = slot; } + /// Set the proposer index of the block. + pub fn set_proposer_index(&mut self, proposer_index: u64) { + self.block.proposer_index = proposer_index; + } + /// Sets the randao to be a signature across the blocks epoch. /// /// Modifying the block's slot after signing may invalidate the signature. - pub fn set_randao_reveal(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) { + pub fn set_randao_reveal( + &mut self, + sk: &SecretKey, + fork: &Fork, + genesis_validators_root: Hash256, + spec: &ChainSpec, + ) { let epoch = self.block.slot.epoch(T::slots_per_epoch()); - let domain = spec.get_domain(epoch, Domain::Randao, fork); + let domain = spec.get_domain(epoch, Domain::Randao, fork, genesis_validators_root); let message = epoch.signing_root(domain); self.block.body.randao_reveal = Signature::new(message.as_bytes(), sk); } @@ -119,10 +130,17 @@ impl TestingBeaconBlockBuilder { validator_index: u64, secret_key: &SecretKey, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) { - let proposer_slashing = - build_proposer_slashing::(test_task, validator_index, secret_key, fork, spec); + let proposer_slashing = build_proposer_slashing::( + test_task, + validator_index, + secret_key, + fork, + genesis_validators_root, + spec, + ); self.block .body .proposer_slashings @@ -137,6 +155,7 @@ impl TestingBeaconBlockBuilder { validator_indices: &[u64], secret_keys: &[&SecretKey], fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) { let attester_slashing = build_double_vote_attester_slashing( @@ -144,6 +163,7 @@ impl TestingBeaconBlockBuilder { validator_indices, secret_keys, fork, + genesis_validators_root, spec, ); let _ = self.block.body.attester_slashings.push(attester_slashing); @@ -246,6 +266,7 @@ impl TestingBeaconBlockBuilder { signing_validators, &signing_secret_keys, &state.fork, + state.genesis_validators_root, spec, ); @@ -355,14 +376,20 @@ impl TestingBeaconBlockBuilder { } let builder = TestingVoluntaryExitBuilder::new(exit_epoch, validator_index); - let exit = builder.build(sk, &state.fork, spec); + let exit = builder.build(sk, &state.fork, state.genesis_validators_root, spec); self.block.body.voluntary_exits.push(exit).unwrap(); } /// Signs and returns the block, consuming the builder. - pub fn build(self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> SignedBeaconBlock { - self.block.sign(sk, fork, spec) + pub fn build( + self, + sk: &SecretKey, + fork: &Fork, + genesis_validators_root: Hash256, + spec: &ChainSpec, + ) -> SignedBeaconBlock { + self.block.sign(sk, fork, genesis_validators_root, spec) } /// Returns the block, consuming the builder. @@ -382,6 +409,7 @@ pub fn build_proposer_slashing( validator_index: u64, secret_key: &SecretKey, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> ProposerSlashing { TestingProposerSlashingBuilder::double_vote::( @@ -389,6 +417,7 @@ pub fn build_proposer_slashing( validator_index, secret_key, fork, + genesis_validators_root, spec, ) } @@ -401,6 +430,7 @@ pub fn build_double_vote_attester_slashing( validator_indices: &[u64], secret_keys: &[&SecretKey], fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> AttesterSlashing { let signer = |validator_index: u64, message: &[u8]| { @@ -411,5 +441,12 @@ pub fn build_double_vote_attester_slashing( Signature::new(message, secret_keys[key_index]) }; - TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer, fork, spec) + TestingAttesterSlashingBuilder::double_vote( + test_task, + validator_indices, + signer, + fork, + genesis_validators_root, + spec, + ) } diff --git a/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs b/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs index 124cc423e7..a969843fad 100644 --- a/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs @@ -12,9 +12,10 @@ impl TestingProposerSlashingBuilder { /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`). pub fn double_vote( test_task: ProposerSlashingTestTask, - mut proposer_index: u64, + proposer_index: u64, secret_key: &SecretKey, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> ProposerSlashing where @@ -31,6 +32,7 @@ impl TestingProposerSlashingBuilder { let mut signed_header_1 = SignedBeaconBlockHeader { message: BeaconBlockHeader { slot, + proposer_index, parent_root: hash_1, state_root: hash_1, body_root: hash_1, @@ -54,19 +56,25 @@ impl TestingProposerSlashingBuilder { }; if test_task != ProposerSlashingTestTask::BadProposal1Signature { - signed_header_1 = signed_header_1.message.sign::(secret_key, fork, spec); + signed_header_1 = + signed_header_1 + .message + .sign::(secret_key, fork, genesis_validators_root, spec); } if test_task != ProposerSlashingTestTask::BadProposal2Signature { - signed_header_2 = signed_header_2.message.sign::(secret_key, fork, spec); + signed_header_2 = + signed_header_2 + .message + .sign::(secret_key, fork, genesis_validators_root, spec); } if test_task == ProposerSlashingTestTask::ProposerUnknown { - proposer_index = 3_141_592; + signed_header_1.message.proposer_index = 3_141_592; + signed_header_2.message.proposer_index = 3_141_592; } ProposerSlashing { - proposer_index, signed_header_1, signed_header_2, } diff --git a/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs b/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs index edc365a61c..330b48ad5f 100644 --- a/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_voluntary_exit_builder.rs @@ -25,8 +25,10 @@ impl TestingVoluntaryExitBuilder { self, secret_key: &SecretKey, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> SignedVoluntaryExit { - self.exit.sign(secret_key, fork, spec) + self.exit + .sign(secret_key, fork, genesis_validators_root, spec) } } diff --git a/eth2/types/src/validator.rs b/eth2/types/src/validator.rs index 2901aa510d..dbdea61ab5 100644 --- a/eth2/types/src/validator.rs +++ b/eth2/types/src/validator.rs @@ -8,7 +8,7 @@ use tree_hash_derive::TreeHash; /// Information about a `BeaconChain` validator. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TestRandom, TreeHash)] pub struct Validator { pub pubkey: PublicKeyBytes, @@ -44,7 +44,7 @@ impl Validator { /// Returns `true` if the validator is eligible to join the activation queue. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn is_eligible_for_activation_queue(&self, spec: &ChainSpec) -> bool { self.activation_eligibility_epoch == spec.far_future_epoch && self.effective_balance == spec.max_effective_balance @@ -52,7 +52,7 @@ impl Validator { /// Returns `true` if the validator is eligible to be activated. /// - /// Spec v0.10.1 + /// Spec v0.11.1 pub fn is_eligible_for_activation( &self, state: &BeaconState, diff --git a/eth2/types/src/voluntary_exit.rs b/eth2/types/src/voluntary_exit.rs index cb39f3d081..19feca8eef 100644 --- a/eth2/types/src/voluntary_exit.rs +++ b/eth2/types/src/voluntary_exit.rs @@ -1,6 +1,6 @@ use crate::{ - test_utils::TestRandom, ChainSpec, Domain, Epoch, Fork, SecretKey, Signature, SignedRoot, - SignedVoluntaryExit, + test_utils::TestRandom, ChainSpec, Domain, Epoch, Fork, Hash256, SecretKey, Signature, + SignedRoot, SignedVoluntaryExit, }; use serde_derive::{Deserialize, Serialize}; @@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash; /// An exit voluntarily submitted a validator who wishes to withdraw. /// -/// Spec v0.10.1 +/// Spec v0.11.1 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] pub struct VoluntaryExit { /// Earliest epoch when voluntary exit can be processed. @@ -25,9 +25,15 @@ impl VoluntaryExit { self, secret_key: &SecretKey, fork: &Fork, + genesis_validators_root: Hash256, spec: &ChainSpec, ) -> SignedVoluntaryExit { - let domain = spec.get_domain(self.epoch, Domain::VoluntaryExit, fork); + let domain = spec.get_domain( + self.epoch, + Domain::VoluntaryExit, + fork, + genesis_validators_root, + ); let message = self.signing_root(domain); let signature = Signature::new(message.as_bytes(), &secret_key); SignedVoluntaryExit { diff --git a/eth2/utils/deposit_contract/build.rs b/eth2/utils/deposit_contract/build.rs index 88e2ff3e3d..752b2e5bb8 100644 --- a/eth2/utils/deposit_contract/build.rs +++ b/eth2/utils/deposit_contract/build.rs @@ -9,9 +9,9 @@ use std::fs::File; use std::io::Write; use std::path::PathBuf; -const TAG: &str = "v0.10.1"; +const TAG: &str = "v0.11.1"; // NOTE: the version of the unsafe contract lags the main tag, but the v0.9.2.1 code is compatible -// with the unmodified v0.10.1 contract +// with the unmodified v0.11.1 contract const UNSAFE_TAG: &str = "v0.9.2.1"; fn spec_url() -> String { diff --git a/eth2/utils/deposit_contract/src/lib.rs b/eth2/utils/deposit_contract/src/lib.rs index d85c89e678..11a831c0fa 100644 --- a/eth2/utils/deposit_contract/src/lib.rs +++ b/eth2/utils/deposit_contract/src/lib.rs @@ -23,15 +23,15 @@ impl From for DecodeError { pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000; pub const DEPOSIT_GAS: usize = 4_000_000; -pub const ABI: &[u8] = include_bytes!("../contracts/v0.10.1_validator_registration.json"); -pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.10.1_validator_registration.bytecode"); +pub const ABI: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.json"); +pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.11.1_validator_registration.bytecode"); pub const DEPOSIT_DATA_LEN: usize = 420; // lol pub mod testnet { pub const ABI: &[u8] = - include_bytes!("../contracts/v0.10.1_testnet_validator_registration.json"); + include_bytes!("../contracts/v0.11.1_testnet_validator_registration.json"); pub const BYTECODE: &[u8] = - include_bytes!("../contracts/v0.10.1_testnet_validator_registration.bytecode"); + include_bytes!("../contracts/v0.11.1_testnet_validator_registration.bytecode"); } pub fn encode_eth1_tx_data(deposit_data: &DepositData) -> Result, Error> { diff --git a/eth2/utils/eth2_testnet_config/src/lib.rs b/eth2/utils/eth2_testnet_config/src/lib.rs index a054193d45..20ab80f079 100644 --- a/eth2/utils/eth2_testnet_config/src/lib.rs +++ b/eth2/utils/eth2_testnet_config/src/lib.rs @@ -202,6 +202,7 @@ mod tests { type E = MainnetEthSpec; + /* TODO: disabled until testnet config is updated for v0.11 #[test] fn hard_coded_works() { let dir: Eth2TestnetConfig = @@ -211,6 +212,7 @@ mod tests { assert!(dir.genesis_state.is_some()); assert!(dir.yaml_config.is_some()); } + */ #[test] fn round_trip() { diff --git a/eth2/utils/int_to_bytes/src/lib.rs b/eth2/utils/int_to_bytes/src/lib.rs index 7b89e4192e..8d8b236cae 100644 --- a/eth2/utils/int_to_bytes/src/lib.rs +++ b/eth2/utils/int_to_bytes/src/lib.rs @@ -32,10 +32,8 @@ pub fn int_to_bytes3(int: u32) -> Option> { } /// Returns `int` as little-endian bytes with a length of 4. -pub fn int_to_bytes4(int: u32) -> Vec { - let mut bytes = BytesMut::with_capacity(4); - bytes.put_u32_le(int); - bytes.to_vec() +pub fn int_to_bytes4(int: u32) -> [u8; 4] { + int.to_le_bytes() } /// Returns `int` as little-endian bytes with a length of 8. @@ -128,7 +126,7 @@ mod tests { 1 => assert_eq!(int_to_bytes1(int as u8), bytes), 2 => assert_eq!(int_to_bytes2(int as u16), bytes), 3 => assert_eq!(int_to_bytes3(int as u32), Some(bytes)), - 4 => assert_eq!(int_to_bytes4(int as u32), bytes), + 4 => assert_eq!(&int_to_bytes4(int as u32)[..], &bytes[..]), 8 => assert_eq!(int_to_bytes8(int), bytes), 32 => assert_eq!(int_to_bytes32(int), bytes), 48 => assert_eq!(int_to_bytes48(int), bytes), diff --git a/eth2/utils/remote_beacon_node/src/lib.rs b/eth2/utils/remote_beacon_node/src/lib.rs index e7b235256a..0727dc4acd 100644 --- a/eth2/utils/remote_beacon_node/src/lib.rs +++ b/eth2/utils/remote_beacon_node/src/lib.rs @@ -324,6 +324,14 @@ impl Beacon { .and_then(move |url| client.json_get(url, vec![])) } + /// Returns the genesis validators root. + pub fn get_genesis_validators_root(&self) -> impl Future { + let client = self.0.clone(); + self.url("genesis_validators_root") + .into_future() + .and_then(move |url| client.json_get(url, vec![])) + } + /// Returns the fork at the head of the beacon chain. pub fn get_fork(&self) -> impl Future { let client = self.0.clone(); diff --git a/eth2/utils/ssz/src/lib.rs b/eth2/utils/ssz/src/lib.rs index 046ebb5f7e..35b1ad5d34 100644 --- a/eth2/utils/ssz/src/lib.rs +++ b/eth2/utils/ssz/src/lib.rs @@ -2,8 +2,8 @@ //! format designed for use in Ethereum 2.0. //! //! Adheres to the Ethereum 2.0 [SSZ -//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/ssz/simple-serialize.md) -//! at v0.10.1. +//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/ssz/simple-serialize.md) +//! at v0.11.1. //! //! ## Example //! diff --git a/eth2/utils/ssz_types/src/lib.rs b/eth2/utils/ssz_types/src/lib.rs index 44c4cbc881..182be39e12 100644 --- a/eth2/utils/ssz_types/src/lib.rs +++ b/eth2/utils/ssz_types/src/lib.rs @@ -9,8 +9,8 @@ //! for padding and verification. //! //! Adheres to the Ethereum 2.0 [SSZ -//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/ssz/simple-serialize.md) -//! at v0.10.1. +//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/ssz/simple-serialize.md) +//! at v0.11.1. //! //! ## Example //! ``` diff --git a/eth2/utils/swap_or_not_shuffle/src/lib.rs b/eth2/utils/swap_or_not_shuffle/src/lib.rs index b6de6daa01..db6c33c036 100644 --- a/eth2/utils/swap_or_not_shuffle/src/lib.rs +++ b/eth2/utils/swap_or_not_shuffle/src/lib.rs @@ -1,7 +1,7 @@ //! Provides list-shuffling functions matching the Ethereum 2.0 specification. //! //! See -//! [compute_shuffled_index](https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_shuffled_index) +//! [compute_shuffled_index](https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_shuffled_index) //! for specifications. //! //! There are two functions exported by this crate: diff --git a/tests/ef_tests/Makefile b/tests/ef_tests/Makefile index d43186f37a..9dcb2b3568 100644 --- a/tests/ef_tests/Makefile +++ b/tests/ef_tests/Makefile @@ -1,5 +1,5 @@ # Bump the test tag here and in .gitlab-ci.yml and CI will take care of updating the cached tarballs -TESTS_TAG := v0.10.1 +TESTS_TAG := v0.11.1 TESTS = general minimal mainnet TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS)) diff --git a/tests/ef_tests/src/cases/epoch_processing.rs b/tests/ef_tests/src/cases/epoch_processing.rs index 07d679f13a..fe19d7bd01 100644 --- a/tests/ef_tests/src/cases/epoch_processing.rs +++ b/tests/ef_tests/src/cases/epoch_processing.rs @@ -81,7 +81,11 @@ impl EpochTransition for Slashings { fn run(state: &mut BeaconState, spec: &ChainSpec) -> Result<(), EpochProcessingError> { let mut validator_statuses = ValidatorStatuses::new(&state, spec)?; validator_statuses.process_attestations(&state, spec)?; - process_slashings(state, validator_statuses.total_balances.current_epoch, spec)?; + process_slashings( + state, + validator_statuses.total_balances.current_epoch(), + spec, + )?; Ok(()) } } diff --git a/tests/ef_tests/tests/tests.rs b/tests/ef_tests/tests/tests.rs index e11e23cbd9..b3a85b8ea3 100644 --- a/tests/ef_tests/tests/tests.rs +++ b/tests/ef_tests/tests/tests.rs @@ -176,7 +176,6 @@ mod ssz_static { ssz_static_test!(voluntary_exit, VoluntaryExit); } -/* NOTE: SSZ generic tests disabled, missing from v0.10.0 #[test] fn ssz_generic() { SszGenericHandler::::run(); @@ -186,7 +185,6 @@ fn ssz_generic() { SszGenericHandler::::run(); SszGenericHandler::::run(); } -*/ #[test] fn epoch_processing_justification_and_finalization() { diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index 9d529ab41e..6dfa29e40a 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -143,101 +143,125 @@ impl ProductionValidatorClient { box_future }) }) - .and_then(move |(beacon_node, remote_eth2_config, genesis_time)| { - let log = log_4.clone(); + .and_then(|(beacon_node, eth2_config, genesis_time)| { + beacon_node + .http + .beacon() + .get_genesis_validators_root() + .map(move |genesis_validators_root| { + ( + beacon_node, + eth2_config, + genesis_time, + genesis_validators_root, + ) + }) + .map_err(|e| { + format!( + "Unable to read genesis validators root from beacon node: {:?}", + e + ) + }) + }) + .and_then( + move |(beacon_node, remote_eth2_config, genesis_time, genesis_validators_root)| { + let log = log_4.clone(); - // Do not permit a connection to a beacon node using different spec constants. - if context.eth2_config.spec_constants != remote_eth2_config.spec_constants { - return Err(format!( - "Beacon node is using an incompatible spec. Got {}, expected {}", - remote_eth2_config.spec_constants, context.eth2_config.spec_constants - )); - } + // Do not permit a connection to a beacon node using different spec constants. + if context.eth2_config.spec_constants != remote_eth2_config.spec_constants { + return Err(format!( + "Beacon node is using an incompatible spec. Got {}, expected {}", + remote_eth2_config.spec_constants, context.eth2_config.spec_constants + )); + } - // Note: here we just assume the spec variables of the remote node. This is very useful - // for testnets, but perhaps a security issue when it comes to mainnet. - // - // A damaging attack would be for a beacon node to convince the validator client of a - // different `SLOTS_PER_EPOCH` variable. This could result in slashable messages being - // produced. We are safe from this because `SLOTS_PER_EPOCH` is a type-level constant - // for Lighthouse. - context.eth2_config = remote_eth2_config; + // Note: here we just assume the spec variables of the remote node. This is very useful + // for testnets, but perhaps a security issue when it comes to mainnet. + // + // A damaging attack would be for a beacon node to convince the validator client of a + // different `SLOTS_PER_EPOCH` variable. This could result in slashable messages being + // produced. We are safe from this because `SLOTS_PER_EPOCH` is a type-level constant + // for Lighthouse. + context.eth2_config = remote_eth2_config; - let slot_clock = SystemTimeSlotClock::new( - context.eth2_config.spec.genesis_slot, - Duration::from_secs(genesis_time), - Duration::from_millis(context.eth2_config.spec.milliseconds_per_slot), - ); + let slot_clock = SystemTimeSlotClock::new( + context.eth2_config.spec.genesis_slot, + Duration::from_secs(genesis_time), + Duration::from_millis(context.eth2_config.spec.milliseconds_per_slot), + ); - let fork_service = ForkServiceBuilder::new() - .slot_clock(slot_clock.clone()) - .beacon_node(beacon_node.clone()) - .runtime_context(context.service_context("fork".into())) - .build()?; + let fork_service = ForkServiceBuilder::new() + .slot_clock(slot_clock.clone()) + .beacon_node(beacon_node.clone()) + .runtime_context(context.service_context("fork".into())) + .build()?; - let validator_store: ValidatorStore = - match &config.key_source { - // Load pre-existing validators from the data dir. - // - // Use the `account_manager` to generate these files. - KeySource::Disk => ValidatorStore::load_from_disk( - config.data_dir.clone(), - context.eth2_config.spec.clone(), - fork_service.clone(), - log.clone(), - )?, - // Generate ephemeral insecure keypairs for testing purposes. - // - // Do not use in production. - KeySource::InsecureKeypairs(indices) => { - ValidatorStore::insecure_ephemeral_validators( - &indices, + let validator_store: ValidatorStore = + match &config.key_source { + // Load pre-existing validators from the data dir. + // + // Use the `account_manager` to generate these files. + KeySource::Disk => ValidatorStore::load_from_disk( + config.data_dir.clone(), + genesis_validators_root, context.eth2_config.spec.clone(), fork_service.clone(), log.clone(), - )? - } - }; + )?, + // Generate ephemeral insecure keypairs for testing purposes. + // + // Do not use in production. + KeySource::InsecureKeypairs(indices) => { + ValidatorStore::insecure_ephemeral_validators( + &indices, + genesis_validators_root, + context.eth2_config.spec.clone(), + fork_service.clone(), + log.clone(), + )? + } + }; - info!( - log, - "Loaded validator keypair store"; - "voting_validators" => validator_store.num_voting_validators() - ); + info!( + log, + "Loaded validator keypair store"; + "voting_validators" => validator_store.num_voting_validators() + ); - let duties_service = DutiesServiceBuilder::new() - .slot_clock(slot_clock.clone()) - .validator_store(validator_store.clone()) - .beacon_node(beacon_node.clone()) - .runtime_context(context.service_context("duties".into())) - .allow_unsynced_beacon_node(config.allow_unsynced_beacon_node) - .build()?; + let duties_service = DutiesServiceBuilder::new() + .slot_clock(slot_clock.clone()) + .validator_store(validator_store.clone()) + .beacon_node(beacon_node.clone()) + .runtime_context(context.service_context("duties".into())) + .allow_unsynced_beacon_node(config.allow_unsynced_beacon_node) + .build()?; - let block_service = BlockServiceBuilder::new() - .duties_service(duties_service.clone()) - .slot_clock(slot_clock.clone()) - .validator_store(validator_store.clone()) - .beacon_node(beacon_node.clone()) - .runtime_context(context.service_context("block".into())) - .build()?; + let block_service = BlockServiceBuilder::new() + .duties_service(duties_service.clone()) + .slot_clock(slot_clock.clone()) + .validator_store(validator_store.clone()) + .beacon_node(beacon_node.clone()) + .runtime_context(context.service_context("block".into())) + .build()?; - let attestation_service = AttestationServiceBuilder::new() - .duties_service(duties_service.clone()) - .slot_clock(slot_clock) - .validator_store(validator_store) - .beacon_node(beacon_node) - .runtime_context(context.service_context("attestation".into())) - .build()?; + let attestation_service = AttestationServiceBuilder::new() + .duties_service(duties_service.clone()) + .slot_clock(slot_clock) + .validator_store(validator_store) + .beacon_node(beacon_node) + .runtime_context(context.service_context("attestation".into())) + .build()?; - Ok(Self { - context, - duties_service, - fork_service, - block_service, - attestation_service, - exit_signals: vec![], - }) - }) + Ok(Self { + context, + duties_service, + fork_service, + block_service, + attestation_service, + exit_signals: vec![], + }) + }, + ) } pub fn start_service(&mut self) -> Result<(), String> { diff --git a/validator_client/src/validator_store.rs b/validator_client/src/validator_store.rs index 8700b5c1e0..d1d2a18410 100644 --- a/validator_client/src/validator_store.rs +++ b/validator_client/src/validator_store.rs @@ -12,13 +12,14 @@ use std::path::PathBuf; use std::sync::Arc; use tempdir::TempDir; use types::{ - Attestation, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, PublicKey, Signature, - SignedBeaconBlock, SignedRoot, + Attestation, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, Hash256, PublicKey, + Signature, SignedBeaconBlock, SignedRoot, }; #[derive(Clone)] pub struct ValidatorStore { validators: Arc>>, + genesis_validators_root: Hash256, spec: Arc, log: Logger, temp_dir: Option>, @@ -29,6 +30,7 @@ pub struct ValidatorStore { impl ValidatorStore { pub fn load_from_disk( base_dir: PathBuf, + genesis_validators_root: Hash256, spec: ChainSpec, fork_service: ForkService, log: Logger, @@ -66,6 +68,7 @@ impl ValidatorStore { Ok(Self { validators: Arc::new(RwLock::new(HashMap::from_par_iter(validator_key_values))), + genesis_validators_root, spec: Arc::new(spec), log, temp_dir: None, @@ -76,6 +79,7 @@ impl ValidatorStore { pub fn insecure_ephemeral_validators( validator_indices: &[usize], + genesis_validators_root: Hash256, spec: ChainSpec, fork_service: ForkService, log: Logger, @@ -107,6 +111,7 @@ impl ValidatorStore { Ok(Self { validators: Arc::new(RwLock::new(HashMap::from_iter(validators))), + genesis_validators_root, spec: Arc::new(spec), log, temp_dir: Some(Arc::new(temp_dir)), @@ -144,7 +149,12 @@ impl ValidatorStore { .get(validator_pubkey) .and_then(|validator_dir| { let voting_keypair = validator_dir.voting_keypair.as_ref()?; - let domain = self.spec.get_domain(epoch, Domain::Randao, &self.fork()?); + let domain = self.spec.get_domain( + epoch, + Domain::Randao, + &self.fork()?, + self.genesis_validators_root, + ); let message = epoch.signing_root(domain); Some(Signature::new(message.as_bytes(), &voting_keypair.sk)) @@ -162,7 +172,12 @@ impl ValidatorStore { .get(validator_pubkey) .and_then(|validator_dir| { let voting_keypair = validator_dir.voting_keypair.as_ref()?; - Some(block.sign(&voting_keypair.sk, &self.fork()?, &self.spec)) + Some(block.sign( + &voting_keypair.sk, + &self.fork()?, + self.genesis_validators_root, + &self.spec, + )) }) } @@ -184,6 +199,7 @@ impl ValidatorStore { &voting_keypair.sk, validator_committee_position, &self.fork()?, + self.genesis_validators_root, &self.spec, ) .map_err(|e| {