From fe03ff0f216ae92414c1b4a684b2e97d578006cc Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Wed, 3 Jun 2020 13:34:01 +1000 Subject: [PATCH] Consensus updates for v0.12 (#1228) * Update state processing for v0.12 * Fix EF test runners for v0.12 * Fix some tests * Fix broken attestation verification test * More test fixes * Fix typo found in review --- .../tests/attestation_verification.rs | 35 ++- beacon_node/src/config.rs | 9 +- book/src/http/spec.md | 4 +- common/clap_utils/src/lib.rs | 3 +- common/eth2_testnet_config/src/lib.rs | 71 +++-- consensus/state_processing/benches/benches.rs | 2 +- .../src/per_block_processing.rs | 9 + .../src/per_block_processing/errors.rs | 13 +- .../is_valid_indexed_attestation.rs | 7 +- .../per_block_processing/signature_sets.rs | 4 +- .../verify_attestation.rs | 7 - .../src/per_block_processing/verify_exit.rs | 4 +- .../src/per_epoch_processing/apply_rewards.rs | 292 ++++++++++-------- consensus/state_processing/tests/tests.rs | 2 +- consensus/types/src/chain_spec.rs | 14 +- consensus/types/src/eth_spec.rs | 8 +- consensus/types/src/lib.rs | 4 +- consensus/types/src/signed_beacon_block.rs | 4 +- .../src/{signing_root.rs => signing_data.rs} | 4 +- lighthouse/environment/src/lib.rs | 14 +- .../environment/tests/environment_builder.rs | 81 +---- .../tests/minimal_spec/eth2-spec.toml | 2 +- lighthouse/src/main.rs | 5 +- testing/ef_tests/Makefile | 2 +- .../ef_tests/src/cases/bls_aggregate_sigs.rs | 13 +- .../src/cases/bls_aggregate_verify.rs | 23 +- testing/ef_tests/src/cases/operations.rs | 21 +- testing/ef_tests/tests/tests.rs | 19 ++ testing/state_transition_vectors/src/exit.rs | 6 +- 29 files changed, 359 insertions(+), 323 deletions(-) rename consensus/types/src/{signing_root.rs => signing_data.rs} (93%) diff --git a/beacon_node/beacon_chain/tests/attestation_verification.rs b/beacon_node/beacon_chain/tests/attestation_verification.rs index 7a06ca65b..f58ae575a 100644 --- a/beacon_node/beacon_chain/tests/attestation_verification.rs +++ b/beacon_node/beacon_chain/tests/attestation_verification.rs @@ -87,7 +87,7 @@ fn get_valid_unaggregated_attestation( fn get_valid_aggregated_attestation( chain: &BeaconChain, - aggregate: Attestation, + mut aggregate: Attestation, ) -> (SignedAggregateAndProof, usize, SecretKey) { let state = &chain.head().expect("should get head").beacon_state; let current_slot = chain.slot().expect("should get slot"); @@ -97,10 +97,11 @@ fn get_valid_aggregated_attestation( .expect("should get committees"); let committee_len = committee.committee.len(); - let (aggregator_index, aggregator_sk) = committee + let (aggregator_committee_pos, aggregator_index, aggregator_sk) = committee .committee .iter() - .find_map(|&val_index| { + .enumerate() + .find_map(|(committee_pos, &val_index)| { let aggregator_sk = generate_deterministic_keypair(val_index).sk; let proof = SelectionProof::new::( @@ -112,13 +113,26 @@ fn get_valid_aggregated_attestation( ); if proof.is_aggregator(committee_len, &chain.spec).unwrap() { - Some((val_index, aggregator_sk)) + Some((committee_pos, val_index, aggregator_sk)) } else { None } }) .expect("should find aggregator for committee"); + // FIXME(v0.12): this can be removed once the verification rules are updated for v0.12 + // I needed to add it because the test only *happened* to work because aggregator and attester + // indices were the same before! + aggregate + .sign( + &aggregator_sk, + aggregator_committee_pos, + &state.fork, + chain.genesis_validators_root, + &chain.spec, + ) + .expect("should sign attestation"); + let signed_aggregate = SignedAggregateAndProof::from_aggregate( aggregator_index as u64, aggregate, @@ -950,7 +964,7 @@ fn attestation_that_skips_epochs() { harness.extend_chain( MainnetEthSpec::slots_per_epoch() as usize * 3 + 1, BlockStrategy::OnCanonicalHead, - AttestationStrategy::AllValidators, + AttestationStrategy::SomeValidators(vec![]), ); let current_slot = chain.slot().expect("should get slot"); @@ -999,11 +1013,8 @@ fn attestation_that_skips_epochs() { "the attestation must skip more than two epochs" ); - assert!( - harness - .chain - .verify_unaggregated_attestation_for_gossip(attestation) - .is_ok(), - "should gossip verify attestation that skips slots" - ); + harness + .chain + .verify_unaggregated_attestation_for_gossip(attestation) + .expect("should gossip verify attestation that skips slots"); } diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index cd4630351..5c129196b 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -404,13 +404,14 @@ pub fn get_testnet_dir(cli_args: &ArgMatches) -> Option { pub fn get_eth2_testnet_config( testnet_dir: &Option, ) -> Result, String> { - Ok(if let Some(testnet_dir) = testnet_dir { + if let Some(testnet_dir) = testnet_dir { Eth2TestnetConfig::load(testnet_dir.clone()) - .map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))? + .map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e)) } else { Eth2TestnetConfig::hard_coded() - .map_err(|e| format!("{} Error : {}", BAD_TESTNET_DIR_MESSAGE, e))? - }) + .map_err(|e| format!("Error parsing hardcoded testnet: {}", e))? + .ok_or_else(|| format!("{}", BAD_TESTNET_DIR_MESSAGE)) + } } /// A bit of hack to find an unused port. diff --git a/book/src/http/spec.md b/book/src/http/spec.md index 9fb8c0c98..3f222c960 100644 --- a/book/src/http/spec.md +++ b/book/src/http/spec.md @@ -51,7 +51,7 @@ Typical Responses | 200 "max_seed_lookahead": 4, "min_epochs_to_inactivity_penalty": 4, "min_validator_withdrawability_delay": 256, - "persistent_committee_period": 2048, + "shard_committee_period": 2048, "base_reward_factor": 64, "whistleblower_reward_quotient": 512, "proposer_reward_quotient": 8, @@ -113,7 +113,7 @@ Typical Responses | 200 "max_seed_lookahead": 4, "min_epochs_to_inactivity_penalty": 4, "min_validator_withdrawability_delay": 256, - "persistent_committee_period": 2048, + "shard_committee_period": 2048, "base_reward_factor": 64, "whistleblower_reward_quotient": 512, "proposer_reward_quotient": 8, diff --git a/common/clap_utils/src/lib.rs b/common/clap_utils/src/lib.rs index 7bc1b07c0..4fa15cfde 100644 --- a/common/clap_utils/src/lib.rs +++ b/common/clap_utils/src/lib.rs @@ -20,10 +20,11 @@ pub const BAD_TESTNET_DIR_MESSAGE: &str = "The hard-coded testnet directory was pub fn parse_testnet_dir_with_hardcoded_default( matches: &ArgMatches, name: &'static str, -) -> Result, String> { +) -> Result>, String> { if let Some(path) = parse_optional::(matches, name)? { Eth2TestnetConfig::load(path.clone()) .map_err(|e| format!("Unable to open testnet dir at {:?}: {}", path, e)) + .map(Some) } else { Eth2TestnetConfig::hard_coded() .map_err(|e| format!("{} Error : {}", BAD_TESTNET_DIR_MESSAGE, e)) diff --git a/common/eth2_testnet_config/src/lib.rs b/common/eth2_testnet_config/src/lib.rs index 969dcab5f..79cc75098 100644 --- a/common/eth2_testnet_config/src/lib.rs +++ b/common/eth2_testnet_config/src/lib.rs @@ -20,7 +20,10 @@ pub const BOOT_ENR_FILE: &str = "boot_enr.yaml"; pub const GENESIS_STATE_FILE: &str = "genesis.ssz"; pub const YAML_CONFIG_FILE: &str = "config.yaml"; -pub const HARDCODED_TESTNET: &str = "witti-v0-11-3"; +/// The name of the testnet to hardcode. +/// +/// Should be set to `None` when no existing testnet is compatible with the codebase. +pub const HARDCODED_TESTNET: Option<&str> = None; pub const HARDCODED_YAML_CONFIG: &[u8] = include_bytes!("../witti-v0-11-3/config.yaml"); pub const HARDCODED_DEPLOY_BLOCK: &[u8] = include_bytes!("../witti-v0-11-3/deploy_block.txt"); @@ -42,29 +45,34 @@ pub struct Eth2TestnetConfig { } impl Eth2TestnetConfig { - // Creates the `Eth2TestnetConfig` that was included in the binary at compile time. This can be - // considered the default Lighthouse testnet. - // - // Returns an error if those included bytes are invalid (this is unlikely). - pub fn hard_coded() -> Result { - Ok(Self { - deposit_contract_address: serde_yaml::from_reader(HARDCODED_DEPOSIT_CONTRACT) - .map_err(|e| format!("Unable to parse contract address: {:?}", e))?, - deposit_contract_deploy_block: serde_yaml::from_reader(HARDCODED_DEPLOY_BLOCK) - .map_err(|e| format!("Unable to parse deploy block: {:?}", e))?, - boot_enr: Some( - serde_yaml::from_reader(HARDCODED_BOOT_ENR) - .map_err(|e| format!("Unable to parse boot enr: {:?}", e))?, - ), - genesis_state: Some( - BeaconState::from_ssz_bytes(HARDCODED_GENESIS_STATE) - .map_err(|e| format!("Unable to parse genesis state: {:?}", e))?, - ), - yaml_config: Some( - serde_yaml::from_reader(HARDCODED_YAML_CONFIG) - .map_err(|e| format!("Unable to parse genesis state: {:?}", e))?, - ), - }) + /// Creates the `Eth2TestnetConfig` that was included in the binary at compile time. This can be + /// considered the default Lighthouse testnet. + /// + /// Returns an error if those included bytes are invalid (this is unlikely). + /// Returns `None` if the hardcoded testnet is disabled. + pub fn hard_coded() -> Result, String> { + if HARDCODED_TESTNET.is_some() { + Ok(Some(Self { + deposit_contract_address: serde_yaml::from_reader(HARDCODED_DEPOSIT_CONTRACT) + .map_err(|e| format!("Unable to parse contract address: {:?}", e))?, + deposit_contract_deploy_block: serde_yaml::from_reader(HARDCODED_DEPLOY_BLOCK) + .map_err(|e| format!("Unable to parse deploy block: {:?}", e))?, + boot_enr: Some( + serde_yaml::from_reader(HARDCODED_BOOT_ENR) + .map_err(|e| format!("Unable to parse boot enr: {:?}", e))?, + ), + genesis_state: Some( + BeaconState::from_ssz_bytes(HARDCODED_GENESIS_STATE) + .map_err(|e| format!("Unable to parse genesis state: {:?}", e))?, + ), + yaml_config: Some( + serde_yaml::from_reader(HARDCODED_YAML_CONFIG) + .map_err(|e| format!("Unable to parse genesis state: {:?}", e))?, + ), + })) + } else { + Ok(None) + } } // Write the files to the directory. @@ -207,17 +215,16 @@ mod tests { type E = MainnetEthSpec; - /* TODO: disabled until testnet config is updated for v0.11 #[test] fn hard_coded_works() { - let dir: Eth2TestnetConfig = - Eth2TestnetConfig::hard_coded().expect("should decode hard_coded params"); - - assert!(dir.boot_enr.is_some()); - assert!(dir.genesis_state.is_some()); - assert!(dir.yaml_config.is_some()); + if let Some(dir) = + Eth2TestnetConfig::::hard_coded().expect("should decode hard_coded params") + { + assert!(dir.boot_enr.is_some()); + assert!(dir.genesis_state.is_some()); + assert!(dir.yaml_config.is_some()); + } } - */ #[test] fn round_trip() { diff --git a/consensus/state_processing/benches/benches.rs b/consensus/state_processing/benches/benches.rs index cdd713948..0bd1f4b9c 100644 --- a/consensus/state_processing/benches/benches.rs +++ b/consensus/state_processing/benches/benches.rs @@ -36,7 +36,7 @@ fn worst_bench(c: &mut Criterion, spec_desc: &str, validator_count: let mut spec = &mut T::default_spec(); // Allows the exits to be processed sucessfully. - spec.persistent_committee_period = 0; + spec.shard_committee_period = 0; let (block, state) = get_worst_block(validator_count, spec); bench_block::(c, block, state, spec, spec_desc, "high_complexity_block"); diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index edca41458..270e91e2e 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -157,6 +157,15 @@ pub fn process_block_header( // Verify that the slots match verify!(block.slot == state.slot, HeaderInvalid::StateSlotMismatch); + // Verify that the block is newer than the latest block header + verify!( + block.slot > state.latest_block_header.slot, + HeaderInvalid::OlderThanLatestBlockHeader { + block_slot: block.slot, + latest_block_header_slot: state.latest_block_header.slot, + } + ); + // 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)?; diff --git a/consensus/state_processing/src/per_block_processing/errors.rs b/consensus/state_processing/src/per_block_processing/errors.rs index 514699ee6..204663184 100644 --- a/consensus/state_processing/src/per_block_processing/errors.rs +++ b/consensus/state_processing/src/per_block_processing/errors.rs @@ -176,6 +176,10 @@ impl From for BlockOperationError { pub enum HeaderInvalid { ProposalSignatureInvalid, StateSlotMismatch, + OlderThanLatestBlockHeader { + latest_block_header_slot: Slot, + block_slot: Slot, + }, ProposerIndexMismatch { block_proposer_index: usize, state_proposer_index: usize, @@ -255,9 +259,6 @@ pub enum AttestationInvalid { attestation: Checkpoint, is_current: bool, }, - /// There are no set bits on the attestation -- an attestation must be signed by at least one - /// validator. - AggregationBitfieldIsEmpty, /// The aggregation bitfield length is not the smallest possible size to represent the committee. BadAggregationBitfieldLength { committee_len: usize, @@ -291,10 +292,8 @@ impl From> #[derive(Debug, PartialEq, Clone)] pub enum IndexedAttestationInvalid { - /// The number of indices exceeds the global maximum. - /// - /// (max_indices, indices_given) - MaxIndicesExceed(usize, usize), + /// The number of indices is 0. + IndicesEmpty, /// The validator indices were not in increasing order. /// /// The error occurred between the given `index` and `index + 1` diff --git a/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs b/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs index 67aa46b46..c9cf9b58d 100644 --- a/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/is_valid_indexed_attestation.rs @@ -20,11 +20,8 @@ pub fn is_valid_indexed_attestation( ) -> Result<()> { let indices = &indexed_attestation.attesting_indices; - // Verify max number of indices - verify!( - indices.len() <= T::MaxValidatorsPerCommittee::to_usize(), - Invalid::MaxIndicesExceed(T::MaxValidatorsPerCommittee::to_usize(), indices.len()) - ); + // Verify that indices aren't empty + verify!(!indices.is_empty(), Invalid::IndicesEmpty); // Check that indices are sorted and unique let check_sorted = |list: &[u64]| -> Result<()> { diff --git a/consensus/state_processing/src/per_block_processing/signature_sets.rs b/consensus/state_processing/src/per_block_processing/signature_sets.rs index 5f050cd2d..b22fec43b 100644 --- a/consensus/state_processing/src/per_block_processing/signature_sets.rs +++ b/consensus/state_processing/src/per_block_processing/signature_sets.rs @@ -11,7 +11,7 @@ use types::{ AggregateSignature, AttesterSlashing, BeaconBlock, BeaconState, BeaconStateError, ChainSpec, DepositData, Domain, EthSpec, Fork, Hash256, IndexedAttestation, ProposerSlashing, PublicKey, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot, - SignedVoluntaryExit, SigningRoot, + SignedVoluntaryExit, SigningData, }; pub type Result = std::result::Result; @@ -92,7 +92,7 @@ where ); let message = if let Some(root) = block_root { - SigningRoot { + SigningData { object_root: root, domain, } diff --git a/consensus/state_processing/src/per_block_processing/verify_attestation.rs b/consensus/state_processing/src/per_block_processing/verify_attestation.rs index 2a688ef46..3c37238e5 100644 --- a/consensus/state_processing/src/per_block_processing/verify_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/verify_attestation.rs @@ -58,13 +58,6 @@ 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 diff --git a/consensus/state_processing/src/per_block_processing/verify_exit.rs b/consensus/state_processing/src/per_block_processing/verify_exit.rs index d4f6befff..cd2c4235a 100644 --- a/consensus/state_processing/src/per_block_processing/verify_exit.rs +++ b/consensus/state_processing/src/per_block_processing/verify_exit.rs @@ -78,10 +78,10 @@ fn verify_exit_parametric( // Verify the validator has been active long enough. verify!( - state.current_epoch() >= validator.activation_epoch + spec.persistent_committee_period, + state.current_epoch() >= validator.activation_epoch + spec.shard_committee_period, ExitInvalid::TooYoungToExit { current_epoch: state.current_epoch(), - earliest_exit_epoch: validator.activation_epoch + spec.persistent_committee_period, + earliest_exit_epoch: validator.activation_epoch + spec.shard_committee_period, } ); diff --git a/consensus/state_processing/src/per_epoch_processing/apply_rewards.rs b/consensus/state_processing/src/per_epoch_processing/apply_rewards.rs index dc9a4f32b..9a099ac09 100644 --- a/consensus/state_processing/src/per_epoch_processing/apply_rewards.rs +++ b/consensus/state_processing/src/per_epoch_processing/apply_rewards.rs @@ -51,11 +51,7 @@ pub fn process_rewards_and_penalties( return Err(Error::ValidatorStatusesInconsistent); } - let mut deltas = vec![Delta::default(); state.balances.len()]; - - get_attestation_deltas(&mut deltas, state, &validator_statuses, spec)?; - - get_proposer_deltas(&mut deltas, state, validator_statuses, spec)?; + let deltas = get_attestation_deltas(state, &validator_statuses, spec)?; // Apply the deltas, erroring on overflow above but not on overflow below (saturating at 0 // instead). @@ -67,78 +63,63 @@ pub fn process_rewards_and_penalties( Ok(()) } -/// For each attesting validator, reward the proposer who was first to include their attestation. -/// -/// Spec v0.11.1 -fn get_proposer_deltas( - deltas: &mut Vec, - state: &BeaconState, - validator_statuses: &mut ValidatorStatuses, - spec: &ChainSpec, -) -> Result<(), Error> { - for (index, validator) in validator_statuses.statuses.iter().enumerate() { - if validator.is_previous_epoch_attester && !validator.is_slashed { - let inclusion = validator - .inclusion_info - .expect("It is a logic error for an attester not to have an inclusion delay."); - - let base_reward = get_base_reward( - state, - index, - validator_statuses.total_balances.current_epoch(), - spec, - )?; - - if inclusion.proposer_index >= deltas.len() { - return Err(Error::ValidatorStatusesInconsistent); - } - - deltas[inclusion.proposer_index] - .reward(base_reward.safe_div(spec.proposer_reward_quotient)?)?; - } - } - - Ok(()) -} - /// Apply rewards for participation in attestations during the previous epoch. /// /// Spec v0.11.1 fn get_attestation_deltas( - deltas: &mut Vec, state: &BeaconState, validator_statuses: &ValidatorStatuses, spec: &ChainSpec, -) -> Result<(), Error> { +) -> Result, Error> { let finality_delay = (state.previous_epoch() - state.finalized_checkpoint.epoch).as_u64(); - for (index, validator) in validator_statuses.statuses.iter().enumerate() { - let base_reward = get_base_reward( - state, - index, - validator_statuses.total_balances.current_epoch(), - spec, - )?; + let mut deltas = vec![Delta::default(); state.validators.len()]; - let delta = get_attestation_delta::( - &validator, - &validator_statuses.total_balances, - base_reward, - finality_delay, - spec, - )?; + let total_balances = &validator_statuses.total_balances; - deltas[index].combine(delta)?; + // Filter out ineligible validators. All sub-functions of the spec do this except for + // `get_inclusion_delay_deltas`. It's safe to do so here because any validator that is in the + // unslashed indices of the matching source attestations is active, and therefore eligible. + for (index, validator) in validator_statuses + .statuses + .iter() + .enumerate() + .filter(|(_, validator)| is_eligible_validator(validator)) + { + let base_reward = get_base_reward(state, index, total_balances.current_epoch(), spec)?; + + let source_delta = + get_source_delta(validator, base_reward, total_balances, finality_delay, spec)?; + let target_delta = + get_target_delta(validator, base_reward, total_balances, finality_delay, spec)?; + let head_delta = + get_head_delta(validator, base_reward, total_balances, finality_delay, spec)?; + let (inclusion_delay_delta, proposer_delta) = + get_inclusion_delay_delta(validator, base_reward, spec)?; + let inactivity_penalty_delta = + get_inactivity_penalty_delta(validator, base_reward, finality_delay, spec)?; + + deltas[index].combine(source_delta)?; + deltas[index].combine(target_delta)?; + deltas[index].combine(head_delta)?; + deltas[index].combine(inclusion_delay_delta)?; + deltas[index].combine(inactivity_penalty_delta)?; + + if let Some((proposer_index, proposer_delta)) = proposer_delta { + if proposer_index >= deltas.len() { + return Err(Error::ValidatorStatusesInconsistent); + } + + deltas[proposer_index].combine(proposer_delta)?; + } } - Ok(()) + Ok(deltas) } -/// Determine the delta for a single validator, sans proposer rewards. -/// -/// Spec v0.11.1 -fn get_attestation_delta( - validator: &ValidatorStatus, +fn get_attestation_component_delta( + index_in_unslashed_attesting_indices: bool, + attesting_balance: u64, total_balances: &TotalBalances, base_reward: u64, finality_delay: u64, @@ -146,83 +127,122 @@ fn get_attestation_delta( ) -> Result { let mut delta = Delta::default(); - // Is this validator eligible to be rewarded or penalized? - // Spec: validator index in `eligible_validator_indices` - let is_eligible = validator.is_active_in_previous_epoch - || (validator.is_slashed && !validator.is_withdrawable_in_current_epoch); + let total_balance = total_balances.current_epoch(); - if !is_eligible { - return Ok(delta); + if index_in_unslashed_attesting_indices { + if finality_delay > spec.min_epochs_to_inactivity_penalty { + // Since full base reward will be canceled out by inactivity penalty deltas, + // optimal participation receives full base reward compensation here. + delta.reward(base_reward)?; + } else { + let reward_numerator = base_reward + .safe_mul(attesting_balance.safe_div(spec.effective_balance_increment)?)?; + delta.reward( + reward_numerator + .safe_div(total_balance.safe_div(spec.effective_balance_increment)?)?, + )?; + } + } else { + delta.penalize(base_reward)?; } - // Handle integer overflow by dividing these quantities by EFFECTIVE_BALANCE_INCREMENT - // Spec: - // - 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() - .safe_div(spec.effective_balance_increment)?; - let total_attesting_balance_ebi = total_balances - .previous_epoch_attesters() - .safe_div(spec.effective_balance_increment)?; - let matching_target_balance_ebi = total_balances - .previous_epoch_target_attesters() - .safe_div(spec.effective_balance_increment)?; - let matching_head_balance_ebi = total_balances - .previous_epoch_head_attesters() - .safe_div(spec.effective_balance_increment)?; + Ok(delta) +} - // Expected FFG source. - // Spec: - // - validator index in `get_unslashed_attesting_indices(state, matching_source_attestations)` +fn get_source_delta( + validator: &ValidatorStatus, + base_reward: u64, + total_balances: &TotalBalances, + finality_delay: u64, + spec: &ChainSpec, +) -> Result { + get_attestation_component_delta( + validator.is_previous_epoch_attester && !validator.is_slashed, + total_balances.previous_epoch_attesters(), + total_balances, + base_reward, + finality_delay, + spec, + ) +} + +fn get_target_delta( + validator: &ValidatorStatus, + base_reward: u64, + total_balances: &TotalBalances, + finality_delay: u64, + spec: &ChainSpec, +) -> Result { + get_attestation_component_delta( + validator.is_previous_epoch_target_attester && !validator.is_slashed, + total_balances.previous_epoch_target_attesters(), + total_balances, + base_reward, + finality_delay, + spec, + ) +} + +fn get_head_delta( + validator: &ValidatorStatus, + base_reward: u64, + total_balances: &TotalBalances, + finality_delay: u64, + spec: &ChainSpec, +) -> Result { + get_attestation_component_delta( + validator.is_previous_epoch_head_attester && !validator.is_slashed, + total_balances.previous_epoch_head_attesters(), + total_balances, + base_reward, + finality_delay, + spec, + ) +} + +fn get_inclusion_delay_delta( + validator: &ValidatorStatus, + base_reward: u64, + spec: &ChainSpec, +) -> Result<(Delta, Option<(usize, Delta)>), Error> { + // Spec: `index in get_unslashed_attesting_indices(state, matching_source_attestations)` if validator.is_previous_epoch_attester && !validator.is_slashed { - delta.reward( - base_reward - .safe_mul(total_attesting_balance_ebi)? - .safe_div(total_balance_ebi)?, - )?; - // Inclusion speed bonus - let proposer_reward = base_reward.safe_div(spec.proposer_reward_quotient)?; - let max_attester_reward = base_reward.safe_sub(proposer_reward)?; - let inclusion = validator + let mut delta = Delta::default(); + let mut proposer_delta = Delta::default(); + + let inclusion_info = validator .inclusion_info - .expect("It is a logic error for an attester not to have an inclusion delay."); - delta.reward(max_attester_reward.safe_div(inclusion.delay)?)?; - } else { - delta.penalize(base_reward)?; - } + .ok_or(Error::ValidatorStatusesInconsistent)?; - // Expected FFG target. - // Spec: - // - validator index in `get_unslashed_attesting_indices(state, matching_target_attestations)` - if validator.is_previous_epoch_target_attester && !validator.is_slashed { - delta.reward( - base_reward - .safe_mul(matching_target_balance_ebi)? - .safe_div(total_balance_ebi)?, - )?; - } else { - delta.penalize(base_reward)?; - } + let proposer_reward = get_proposer_reward(base_reward, spec)?; + proposer_delta.reward(proposer_reward)?; - // Expected head. - // Spec: - // - validator index in `get_unslashed_attesting_indices(state, matching_head_attestations)` - if validator.is_previous_epoch_head_attester && !validator.is_slashed { - delta.reward( - base_reward - .safe_mul(matching_head_balance_ebi)? - .safe_div(total_balance_ebi)?, - )?; + let max_attester_reward = base_reward.safe_sub(proposer_reward)?; + delta.reward(max_attester_reward.safe_div(inclusion_info.delay)?)?; + + let proposer_index = inclusion_info.proposer_index as usize; + Ok((delta, Some((proposer_index, proposer_delta)))) } else { - delta.penalize(base_reward)?; + Ok((Delta::default(), None)) } +} + +fn get_inactivity_penalty_delta( + validator: &ValidatorStatus, + base_reward: u64, + finality_delay: u64, + spec: &ChainSpec, +) -> Result { + let mut delta = Delta::default(); // Inactivity penalty if finality_delay > spec.min_epochs_to_inactivity_penalty { - // All eligible validators are penalized - delta.penalize(spec.base_rewards_per_epoch.safe_mul(base_reward)?)?; + // If validator is performing optimally this cancels all rewards for a neutral balance + delta.penalize( + spec.base_rewards_per_epoch + .safe_mul(base_reward)? + .safe_sub(get_proposer_reward(base_reward, spec)?)?, + )?; // Additionally, all validators whose FFG target didn't match are penalized extra // This condition is equivalent to this condition from the spec: @@ -237,10 +257,20 @@ fn get_attestation_delta( } } - // Proposer bonus is handled in `get_proposer_deltas`. - // - // This function only computes the delta for a single validator, so it cannot also return a - // delta for a validator. - Ok(delta) } + +/// Compute the reward awarded to a proposer for including an attestation from a validator. +/// +/// The `base_reward` param should be the `base_reward` of the attesting validator. +fn get_proposer_reward(base_reward: u64, spec: &ChainSpec) -> Result { + Ok(base_reward.safe_div(spec.proposer_reward_quotient)?) +} + +/// Is the validator eligible for penalties and rewards at the current epoch? +/// +/// Spec: v0.12.0 +fn is_eligible_validator(validator: &ValidatorStatus) -> bool { + validator.is_active_in_previous_epoch + || (validator.is_slashed && !validator.is_withdrawable_in_current_epoch) +} diff --git a/consensus/state_processing/tests/tests.rs b/consensus/state_processing/tests/tests.rs index db4f7cde0..d2d4b432b 100644 --- a/consensus/state_processing/tests/tests.rs +++ b/consensus/state_processing/tests/tests.rs @@ -215,7 +215,7 @@ mod signatures_minimal { let mut spec = &mut TestEthSpec::default_spec(); // Allows the test to pass. - spec.persistent_committee_period = 0; + spec.shard_committee_period = 0; test_scenario::( |mut builder| { diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index 22c248ce8..0075d3bdd 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -82,7 +82,7 @@ pub struct ChainSpec { pub max_seed_lookahead: Epoch, pub min_epochs_to_inactivity_penalty: u64, pub min_validator_withdrawability_delay: Epoch, - pub persistent_committee_period: u64, + pub shard_committee_period: u64, /* * Reward and penalty quotients @@ -292,7 +292,7 @@ impl ChainSpec { max_seed_lookahead: Epoch::new(4), min_epochs_to_inactivity_penalty: 4, min_validator_withdrawability_delay: Epoch::new(256), - persistent_committee_period: 2_048, + shard_committee_period: 256, /* * Reward and penalty quotients @@ -300,7 +300,7 @@ impl ChainSpec { base_reward_factor: 64, whistleblower_reward_quotient: 512, proposer_reward_quotient: 8, - inactivity_penalty_quotient: 33_554_432, + inactivity_penalty_quotient: u64::pow(2, 24), min_slashing_penalty_quotient: 32, /* @@ -353,7 +353,7 @@ impl ChainSpec { min_genesis_active_validator_count: 64, eth1_follow_distance: 16, genesis_fork_version: [0x00, 0x00, 0x00, 0x01], - persistent_committee_period: 128, + shard_committee_period: 64, min_genesis_delay: 300, milliseconds_per_slot: 6_000, safe_slots_to_update_justified: 2, @@ -481,7 +481,7 @@ pub struct YamlConfig { max_seed_lookahead: u64, min_epochs_to_inactivity_penalty: u64, min_validator_withdrawability_delay: u64, - persistent_committee_period: u64, + shard_committee_period: u64, base_reward_factor: u64, whistleblower_reward_quotient: u64, proposer_reward_quotient: u64, @@ -591,7 +591,7 @@ impl YamlConfig { min_seed_lookahead: spec.min_seed_lookahead.into(), max_seed_lookahead: spec.max_seed_lookahead.into(), min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay.into(), - persistent_committee_period: spec.persistent_committee_period, + shard_committee_period: spec.shard_committee_period, min_epochs_to_inactivity_penalty: spec.min_epochs_to_inactivity_penalty, base_reward_factor: spec.base_reward_factor, whistleblower_reward_quotient: spec.whistleblower_reward_quotient, @@ -690,7 +690,7 @@ impl YamlConfig { min_validator_withdrawability_delay: Epoch::from( self.min_validator_withdrawability_delay, ), - persistent_committee_period: self.persistent_committee_period, + shard_committee_period: self.shard_committee_period, min_epochs_to_inactivity_penalty: self.min_epochs_to_inactivity_penalty, base_reward_factor: self.base_reward_factor, whistleblower_reward_quotient: self.whistleblower_reward_quotient, diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index 6f03a7b32..c2cfe8ba4 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -3,7 +3,7 @@ use crate::*; use safe_arith::SafeArith; use serde_derive::{Deserialize, Serialize}; use ssz_types::typenum::{ - Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U2, U2048, U32, U4, U4096, U64, + Unsigned, U0, U1024, U1099511627776, U128, U16, U16777216, U2, U2048, U32, U4, U4096, U64, U65536, U8, U8192, }; use std::fmt::Debug; @@ -151,7 +151,7 @@ impl EthSpec for MainnetEthSpec { type HistoricalRootsLimit = U16777216; type ValidatorRegistryLimit = U1099511627776; type MaxProposerSlashings = U16; - type MaxAttesterSlashings = U1; + type MaxAttesterSlashings = U2; type MaxAttestations = U128; type MaxDeposits = U16; type MaxVoluntaryExits = U16; @@ -178,12 +178,12 @@ pub struct MinimalEthSpec; impl EthSpec for MinimalEthSpec { type SlotsPerEpoch = U8; - type EpochsPerEth1VotingPeriod = U2; + type EpochsPerEth1VotingPeriod = U4; 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 + type SlotsPerEth1VotingPeriod = U32; // 4 epochs * 8 slots per epoch params_from_eth_spec!(MainnetEthSpec { JustificationBitsLength, diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index c982100d5..c2dd21fb8 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -39,7 +39,7 @@ pub mod signed_aggregate_and_proof; pub mod signed_beacon_block; pub mod signed_beacon_block_header; pub mod signed_voluntary_exit; -pub mod signing_root; +pub mod signing_data; pub mod utils; pub mod validator; pub mod voluntary_exit; @@ -84,7 +84,7 @@ pub use crate::signed_aggregate_and_proof::SignedAggregateAndProof; pub use crate::signed_beacon_block::{SignedBeaconBlock, SignedBeaconBlockHash}; pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader; pub use crate::signed_voluntary_exit::SignedVoluntaryExit; -pub use crate::signing_root::{SignedRoot, SigningRoot}; +pub use crate::signing_data::{SignedRoot, SigningData}; pub use crate::slot_epoch::{Epoch, Slot}; pub use crate::subnet_id::SubnetId; pub use crate::validator::Validator; diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/signed_beacon_block.rs index a43ade048..6bc654303 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/signed_beacon_block.rs @@ -1,6 +1,6 @@ use crate::{ test_utils::TestRandom, BeaconBlock, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, - SignedRoot, SigningRoot, Slot, + SignedRoot, SigningData, Slot, }; use bls::Signature; use serde_derive::{Deserialize, Serialize}; @@ -69,7 +69,7 @@ impl SignedBeaconBlock { ); let message = if let Some(object_root) = object_root_opt { - SigningRoot { + SigningData { object_root, domain, } diff --git a/consensus/types/src/signing_root.rs b/consensus/types/src/signing_data.rs similarity index 93% rename from consensus/types/src/signing_root.rs rename to consensus/types/src/signing_data.rs index 6d060bf07..61f7e839f 100644 --- a/consensus/types/src/signing_root.rs +++ b/consensus/types/src/signing_data.rs @@ -9,14 +9,14 @@ use tree_hash_derive::TreeHash; #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] -pub struct SigningRoot { +pub struct SigningData { pub object_root: Hash256, pub domain: Hash256, } pub trait SignedRoot: TreeHash { fn signing_root(&self, domain: Hash256) -> Hash256 { - SigningRoot { + SigningData { object_root: self.tree_hash_root(), domain, } diff --git a/lighthouse/environment/src/lib.rs b/lighthouse/environment/src/lib.rs index 8ba106d99..b20e5f3eb 100644 --- a/lighthouse/environment/src/lib.rs +++ b/lighthouse/environment/src/lib.rs @@ -147,7 +147,7 @@ impl EnvironmentBuilder { Ok(self) } - /// Setups eth2 config using the CLI arguments. + /// Adds a testnet configuration to the environment. pub fn eth2_testnet_config( mut self, eth2_testnet_config: Eth2TestnetConfig, @@ -170,6 +170,18 @@ impl EnvironmentBuilder { Ok(self) } + /// Optionally adds a testnet configuration to the environment. + pub fn optional_eth2_testnet_config( + self, + optional_config: Option>, + ) -> Result { + if let Some(config) = optional_config { + self.eth2_testnet_config(config) + } else { + Ok(self) + } + } + /// Consumes the builder, returning an `Environment`. pub fn build(self) -> Result, String> { Ok(Environment { diff --git a/lighthouse/environment/tests/environment_builder.rs b/lighthouse/environment/tests/environment_builder.rs index 17f7c4310..d46531302 100644 --- a/lighthouse/environment/tests/environment_builder.rs +++ b/lighthouse/environment/tests/environment_builder.rs @@ -1,11 +1,5 @@ -/* - * - * TODO: disabled until hardcoded testnet config is updated for v0.11 - * - * #![cfg(test)] -use clap::ArgMatches; use environment::EnvironmentBuilder; use eth2_testnet_config::Eth2TestnetConfig; use std::path::PathBuf; @@ -19,75 +13,32 @@ fn builder() -> EnvironmentBuilder { .expect("should set logger") } -fn dummy_data_dir() -> PathBuf { - PathBuf::from("./tests/datadir_that_does_not_exist") -} - -fn eth2_testnet_config() -> Eth2TestnetConfig { +fn eth2_testnet_config() -> Option> { Eth2TestnetConfig::hard_coded().expect("should decode hard_coded params") } mod setup_eth2_config { use super::*; - #[test] - fn returns_err_if_the_loaded_config_doesnt_match() { - // `Minimal` spec - let path_to_minimal_spec = PathBuf::from("./tests/minimal_spec"); - - // `Mainnet` spec - let builder = builder(); - - let result = builder.setup_eth2_config( - path_to_minimal_spec, - eth2_testnet_config(), - &ArgMatches::default(), - ); - - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - "Eth2 config loaded from disk does not match client spec version. Got minimal expected mainnet" - ); - } - - #[test] - fn update_slot_time() { - // testnet - let cli_args = - beacon_node::cli_app().get_matches_from(vec!["app", "testnet", "--slot-time", "999"]); - - let environment = builder() - .setup_eth2_config(dummy_data_dir(), eth2_testnet_config(), &cli_args) - .expect("should setup eth2_config") - .build() - .expect("should build environment"); - - assert_eq!(environment.eth2_config.spec.milliseconds_per_slot, 999); - } - #[test] fn update_spec_with_yaml_config() { - let config_yaml = PathBuf::from("./tests/testnet_dir/config.yaml"); + if let Some(mut eth2_testnet_config) = eth2_testnet_config() { + let config_yaml = PathBuf::from("./tests/testnet_dir/config.yaml"); - let mut eth2_testnet_config = eth2_testnet_config(); - eth2_testnet_config.yaml_config = - Some(YamlConfig::from_file(config_yaml.as_path()).expect("should load yaml config")); + eth2_testnet_config.yaml_config = Some( + YamlConfig::from_file(config_yaml.as_path()).expect("should load yaml config"), + ); - let environment = builder() - .setup_eth2_config( - dummy_data_dir(), - eth2_testnet_config, - &ArgMatches::default(), - ) - .expect("should setup eth2_config") - .build() - .expect("should build environment"); + let environment = builder() + .eth2_testnet_config(eth2_testnet_config) + .expect("should setup eth2_config") + .build() + .expect("should build environment"); - assert_eq!( - environment.eth2_config.spec.far_future_epoch, - Epoch::new(999) // see testnet_dir/config.yaml - ); + assert_eq!( + environment.eth2_config.spec.far_future_epoch, + Epoch::new(999) // see testnet_dir/config.yaml + ); + } } } -*/ diff --git a/lighthouse/environment/tests/minimal_spec/eth2-spec.toml b/lighthouse/environment/tests/minimal_spec/eth2-spec.toml index c880b867d..3ef104612 100644 --- a/lighthouse/environment/tests/minimal_spec/eth2-spec.toml +++ b/lighthouse/environment/tests/minimal_spec/eth2-spec.toml @@ -24,7 +24,7 @@ min_seed_lookahead = 1 max_seed_lookahead = 4 min_epochs_to_inactivity_penalty = 4 min_validator_withdrawability_delay = 256 -persistent_committee_period = 2048 +shard_committee_period = 2048 base_reward_factor = 64 whistleblower_reward_quotient = 512 proposer_reward_quotient = 8 diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index b037f6d78..e2554fc3a 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -132,13 +132,14 @@ fn run( .ok_or_else(|| "Expected --debug-level flag".to_string())?; let log_format = matches.value_of("log-format"); - let eth2_testnet_config = + + let optional_testnet_config = clap_utils::parse_testnet_dir_with_hardcoded_default(matches, "testnet-dir")?; let mut environment = environment_builder .async_logger(debug_level, log_format)? .multi_threaded_tokio_runtime()? - .eth2_testnet_config(eth2_testnet_config)? + .optional_eth2_testnet_config(optional_testnet_config)? .build()?; let log = environment.core_context().log; diff --git a/testing/ef_tests/Makefile b/testing/ef_tests/Makefile index 9dcb2b356..562cc886e 100644 --- a/testing/ef_tests/Makefile +++ b/testing/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.11.1 +TESTS_TAG := v0.12.0 TESTS = general minimal mainnet TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS)) diff --git a/testing/ef_tests/src/cases/bls_aggregate_sigs.rs b/testing/ef_tests/src/cases/bls_aggregate_sigs.rs index 561c7bc64..71554705b 100644 --- a/testing/ef_tests/src/cases/bls_aggregate_sigs.rs +++ b/testing/ef_tests/src/cases/bls_aggregate_sigs.rs @@ -25,12 +25,17 @@ impl Case for BlsAggregateSigs { aggregate_signature.add(&sig); } - let output_bytes = Some( + // Check for YAML null value, indicating invalid input. This is a bit of a hack, + // as our mutating `aggregate_signature.add` API doesn't play nicely with aggregating 0 + // inputs. + let output_bytes = if self.output == "~" { + AggregateSignature::new().as_bytes().to_vec() + } else { hex::decode(&self.output[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?, - ); + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))? + }; let aggregate_signature = Ok(aggregate_signature.as_bytes().to_vec()); - compare_result::, Vec>(&aggregate_signature, &output_bytes) + compare_result::, Vec>(&aggregate_signature, &Some(output_bytes)) } } diff --git a/testing/ef_tests/src/cases/bls_aggregate_verify.rs b/testing/ef_tests/src/cases/bls_aggregate_verify.rs index 7cac4e906..a2f74c0b2 100644 --- a/testing/ef_tests/src/cases/bls_aggregate_verify.rs +++ b/testing/ef_tests/src/cases/bls_aggregate_verify.rs @@ -4,15 +4,10 @@ use crate::cases::common::BlsCase; use bls::{AggregateSignature, PublicKey}; use serde_derive::Deserialize; -#[derive(Debug, Clone, Deserialize)] -pub struct BlsAggregatePair { - pub pubkey: PublicKey, - pub message: String, -} - #[derive(Debug, Clone, Deserialize)] pub struct BlsAggregateVerifyInput { - pub pairs: Vec, + pub pubkeys: Vec, + pub messages: Vec, pub signature: String, } @@ -28,11 +23,10 @@ impl Case for BlsAggregateVerify { fn result(&self, _case_index: usize) -> Result<(), Error> { let messages = self .input - .pairs + .messages .iter() - .map(|pair| { - hex::decode(&pair.message[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e))) + .map(|message| { + hex::decode(&message[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e))) }) .collect::>, _>>()?; @@ -41,12 +35,7 @@ impl Case for BlsAggregateVerify { .map(|x| x.as_slice()) .collect::>(); - let pubkey_refs = self - .input - .pairs - .iter() - .map(|p| &p.pubkey) - .collect::>(); + let pubkey_refs = self.input.pubkeys.iter().collect::>(); let signature_ok = hex::decode(&self.input.signature[2..]) .ok() diff --git a/testing/ef_tests/src/cases/operations.rs b/testing/ef_tests/src/cases/operations.rs index c2af3aaf1..ff6dad6dd 100644 --- a/testing/ef_tests/src/cases/operations.rs +++ b/testing/ef_tests/src/cases/operations.rs @@ -27,7 +27,7 @@ struct Metadata { pub struct Operations> { metadata: Metadata, pub pre: BeaconState, - pub operation: O, + pub operation: Option, pub post: Option>, } @@ -135,8 +135,16 @@ impl> LoadCase for Operations { } else { Metadata::default() }; + let pre = ssz_decode_file(&path.join("pre.ssz"))?; - let operation = ssz_decode_file(&path.join(O::filename()))?; + + // Check BLS setting here before SSZ deserialization, as most types require signatures + // to be valid. + let operation = if metadata.bls_setting.unwrap_or_default().check().is_ok() { + Some(ssz_decode_file(&path.join(O::filename()))?) + } else { + None + }; let post_filename = path.join("post.ssz"); let post = if post_filename.is_file() { Some(ssz_decode_file(&post_filename)?) @@ -162,8 +170,6 @@ impl> Case for Operations { } fn result(&self, _case_index: usize) -> Result<(), Error> { - self.metadata.bls_setting.unwrap_or_default().check()?; - let spec = &E::default_spec(); let mut state = self.pre.clone(); let mut expected = self.post.clone(); @@ -173,7 +179,12 @@ impl> Case for Operations { .build_all_committee_caches(spec) .expect("committee caches OK"); - let mut result = self.operation.apply_to(&mut state, spec).map(|()| state); + let mut result = self + .operation + .as_ref() + .ok_or(Error::SkippedBls)? + .apply_to(&mut state, spec) + .map(|()| state); compare_beacon_state_results_without_caches(&mut result, &mut expected) } diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index 3bec38af8..d20ac5ec7 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -28,6 +28,25 @@ fn minimal_config_ok() { config_test::(); } +// Check that the hand-computed multiplications on EthSpec are correctly computed. +// This test lives here because one is most likely to muck these up during a spec update. +fn check_typenum_values() { + assert_eq!( + E::MaxPendingAttestations::to_u64(), + E::MaxAttestations::to_u64() * E::SlotsPerEpoch::to_u64() + ); + assert_eq!( + E::SlotsPerEth1VotingPeriod::to_u64(), + E::EpochsPerEth1VotingPeriod::to_u64() * E::SlotsPerEpoch::to_u64() + ); +} + +#[test] +fn derived_typenum_values() { + check_typenum_values::(); + check_typenum_values::(); +} + #[test] fn shuffling() { ShufflingHandler::::run(); diff --git a/testing/state_transition_vectors/src/exit.rs b/testing/state_transition_vectors/src/exit.rs index 3f156d29e..743cece95 100644 --- a/testing/state_transition_vectors/src/exit.rs +++ b/testing/state_transition_vectors/src/exit.rs @@ -7,8 +7,8 @@ use types::{BeaconBlock, BeaconState, Epoch, EthSpec, SignedBeaconBlock}; // Default validator index to exit. pub const VALIDATOR_INDEX: u64 = 0; -// Epoch that the state will be transitioned to by default, equal to PERSISTENT_COMMITTEE_PERIOD. -pub const STATE_EPOCH: Epoch = Epoch::new(2048); +// Epoch that the state will be transitioned to by default, equal to SHARD_COMMITTEE_PERIOD. +pub const STATE_EPOCH: Epoch = Epoch::new(256); struct ExitTest { validator_index: u64, @@ -62,7 +62,7 @@ impl ExitTest { fn run(self) -> BeaconState { let spec = &E::default_spec(); let expected = self.expected.clone(); - assert_eq!(STATE_EPOCH, spec.persistent_committee_period); + assert_eq!(STATE_EPOCH, spec.shard_committee_period); let (block, mut state) = self.block_and_pre_state();