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
This commit is contained in:
parent
197adeff0b
commit
fe03ff0f21
@ -87,7 +87,7 @@ fn get_valid_unaggregated_attestation<T: BeaconChainTypes>(
|
|||||||
|
|
||||||
fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
|
fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
|
||||||
chain: &BeaconChain<T>,
|
chain: &BeaconChain<T>,
|
||||||
aggregate: Attestation<T::EthSpec>,
|
mut aggregate: Attestation<T::EthSpec>,
|
||||||
) -> (SignedAggregateAndProof<T::EthSpec>, usize, SecretKey) {
|
) -> (SignedAggregateAndProof<T::EthSpec>, usize, SecretKey) {
|
||||||
let state = &chain.head().expect("should get head").beacon_state;
|
let state = &chain.head().expect("should get head").beacon_state;
|
||||||
let current_slot = chain.slot().expect("should get slot");
|
let current_slot = chain.slot().expect("should get slot");
|
||||||
@ -97,10 +97,11 @@ fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
|
|||||||
.expect("should get committees");
|
.expect("should get committees");
|
||||||
let committee_len = committee.committee.len();
|
let committee_len = committee.committee.len();
|
||||||
|
|
||||||
let (aggregator_index, aggregator_sk) = committee
|
let (aggregator_committee_pos, aggregator_index, aggregator_sk) = committee
|
||||||
.committee
|
.committee
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|&val_index| {
|
.enumerate()
|
||||||
|
.find_map(|(committee_pos, &val_index)| {
|
||||||
let aggregator_sk = generate_deterministic_keypair(val_index).sk;
|
let aggregator_sk = generate_deterministic_keypair(val_index).sk;
|
||||||
|
|
||||||
let proof = SelectionProof::new::<T::EthSpec>(
|
let proof = SelectionProof::new::<T::EthSpec>(
|
||||||
@ -112,13 +113,26 @@ fn get_valid_aggregated_attestation<T: BeaconChainTypes>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if proof.is_aggregator(committee_len, &chain.spec).unwrap() {
|
if proof.is_aggregator(committee_len, &chain.spec).unwrap() {
|
||||||
Some((val_index, aggregator_sk))
|
Some((committee_pos, val_index, aggregator_sk))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.expect("should find aggregator for committee");
|
.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(
|
let signed_aggregate = SignedAggregateAndProof::from_aggregate(
|
||||||
aggregator_index as u64,
|
aggregator_index as u64,
|
||||||
aggregate,
|
aggregate,
|
||||||
@ -950,7 +964,7 @@ fn attestation_that_skips_epochs() {
|
|||||||
harness.extend_chain(
|
harness.extend_chain(
|
||||||
MainnetEthSpec::slots_per_epoch() as usize * 3 + 1,
|
MainnetEthSpec::slots_per_epoch() as usize * 3 + 1,
|
||||||
BlockStrategy::OnCanonicalHead,
|
BlockStrategy::OnCanonicalHead,
|
||||||
AttestationStrategy::AllValidators,
|
AttestationStrategy::SomeValidators(vec![]),
|
||||||
);
|
);
|
||||||
|
|
||||||
let current_slot = chain.slot().expect("should get slot");
|
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"
|
"the attestation must skip more than two epochs"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(
|
harness
|
||||||
harness
|
.chain
|
||||||
.chain
|
.verify_unaggregated_attestation_for_gossip(attestation)
|
||||||
.verify_unaggregated_attestation_for_gossip(attestation)
|
.expect("should gossip verify attestation that skips slots");
|
||||||
.is_ok(),
|
|
||||||
"should gossip verify attestation that skips slots"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -404,13 +404,14 @@ pub fn get_testnet_dir(cli_args: &ArgMatches) -> Option<PathBuf> {
|
|||||||
pub fn get_eth2_testnet_config<E: EthSpec>(
|
pub fn get_eth2_testnet_config<E: EthSpec>(
|
||||||
testnet_dir: &Option<PathBuf>,
|
testnet_dir: &Option<PathBuf>,
|
||||||
) -> Result<Eth2TestnetConfig<E>, String> {
|
) -> Result<Eth2TestnetConfig<E>, String> {
|
||||||
Ok(if let Some(testnet_dir) = testnet_dir {
|
if let Some(testnet_dir) = testnet_dir {
|
||||||
Eth2TestnetConfig::load(testnet_dir.clone())
|
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 {
|
} else {
|
||||||
Eth2TestnetConfig::hard_coded()
|
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.
|
/// A bit of hack to find an unused port.
|
||||||
|
@ -51,7 +51,7 @@ Typical Responses | 200
|
|||||||
"max_seed_lookahead": 4,
|
"max_seed_lookahead": 4,
|
||||||
"min_epochs_to_inactivity_penalty": 4,
|
"min_epochs_to_inactivity_penalty": 4,
|
||||||
"min_validator_withdrawability_delay": 256,
|
"min_validator_withdrawability_delay": 256,
|
||||||
"persistent_committee_period": 2048,
|
"shard_committee_period": 2048,
|
||||||
"base_reward_factor": 64,
|
"base_reward_factor": 64,
|
||||||
"whistleblower_reward_quotient": 512,
|
"whistleblower_reward_quotient": 512,
|
||||||
"proposer_reward_quotient": 8,
|
"proposer_reward_quotient": 8,
|
||||||
@ -113,7 +113,7 @@ Typical Responses | 200
|
|||||||
"max_seed_lookahead": 4,
|
"max_seed_lookahead": 4,
|
||||||
"min_epochs_to_inactivity_penalty": 4,
|
"min_epochs_to_inactivity_penalty": 4,
|
||||||
"min_validator_withdrawability_delay": 256,
|
"min_validator_withdrawability_delay": 256,
|
||||||
"persistent_committee_period": 2048,
|
"shard_committee_period": 2048,
|
||||||
"base_reward_factor": 64,
|
"base_reward_factor": 64,
|
||||||
"whistleblower_reward_quotient": 512,
|
"whistleblower_reward_quotient": 512,
|
||||||
"proposer_reward_quotient": 8,
|
"proposer_reward_quotient": 8,
|
||||||
|
@ -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<E: EthSpec>(
|
pub fn parse_testnet_dir_with_hardcoded_default<E: EthSpec>(
|
||||||
matches: &ArgMatches,
|
matches: &ArgMatches,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
) -> Result<Eth2TestnetConfig<E>, String> {
|
) -> Result<Option<Eth2TestnetConfig<E>>, String> {
|
||||||
if let Some(path) = parse_optional::<PathBuf>(matches, name)? {
|
if let Some(path) = parse_optional::<PathBuf>(matches, name)? {
|
||||||
Eth2TestnetConfig::load(path.clone())
|
Eth2TestnetConfig::load(path.clone())
|
||||||
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", path, e))
|
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", path, e))
|
||||||
|
.map(Some)
|
||||||
} else {
|
} else {
|
||||||
Eth2TestnetConfig::hard_coded()
|
Eth2TestnetConfig::hard_coded()
|
||||||
.map_err(|e| format!("{} Error : {}", BAD_TESTNET_DIR_MESSAGE, e))
|
.map_err(|e| format!("{} Error : {}", BAD_TESTNET_DIR_MESSAGE, e))
|
||||||
|
@ -20,7 +20,10 @@ pub const BOOT_ENR_FILE: &str = "boot_enr.yaml";
|
|||||||
pub const GENESIS_STATE_FILE: &str = "genesis.ssz";
|
pub const GENESIS_STATE_FILE: &str = "genesis.ssz";
|
||||||
pub const YAML_CONFIG_FILE: &str = "config.yaml";
|
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_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");
|
pub const HARDCODED_DEPLOY_BLOCK: &[u8] = include_bytes!("../witti-v0-11-3/deploy_block.txt");
|
||||||
@ -42,29 +45,34 @@ pub struct Eth2TestnetConfig<E: EthSpec> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec> Eth2TestnetConfig<E> {
|
impl<E: EthSpec> Eth2TestnetConfig<E> {
|
||||||
// Creates the `Eth2TestnetConfig` that was included in the binary at compile time. This can be
|
/// Creates the `Eth2TestnetConfig` that was included in the binary at compile time. This can be
|
||||||
// considered the default Lighthouse testnet.
|
/// considered the default Lighthouse testnet.
|
||||||
//
|
///
|
||||||
// Returns an error if those included bytes are invalid (this is unlikely).
|
/// Returns an error if those included bytes are invalid (this is unlikely).
|
||||||
pub fn hard_coded() -> Result<Self, String> {
|
/// Returns `None` if the hardcoded testnet is disabled.
|
||||||
Ok(Self {
|
pub fn hard_coded() -> Result<Option<Self>, String> {
|
||||||
deposit_contract_address: serde_yaml::from_reader(HARDCODED_DEPOSIT_CONTRACT)
|
if HARDCODED_TESTNET.is_some() {
|
||||||
.map_err(|e| format!("Unable to parse contract address: {:?}", e))?,
|
Ok(Some(Self {
|
||||||
deposit_contract_deploy_block: serde_yaml::from_reader(HARDCODED_DEPLOY_BLOCK)
|
deposit_contract_address: serde_yaml::from_reader(HARDCODED_DEPOSIT_CONTRACT)
|
||||||
.map_err(|e| format!("Unable to parse deploy block: {:?}", e))?,
|
.map_err(|e| format!("Unable to parse contract address: {:?}", e))?,
|
||||||
boot_enr: Some(
|
deposit_contract_deploy_block: serde_yaml::from_reader(HARDCODED_DEPLOY_BLOCK)
|
||||||
serde_yaml::from_reader(HARDCODED_BOOT_ENR)
|
.map_err(|e| format!("Unable to parse deploy block: {:?}", e))?,
|
||||||
.map_err(|e| format!("Unable to parse boot enr: {:?}", e))?,
|
boot_enr: Some(
|
||||||
),
|
serde_yaml::from_reader(HARDCODED_BOOT_ENR)
|
||||||
genesis_state: Some(
|
.map_err(|e| format!("Unable to parse boot enr: {:?}", e))?,
|
||||||
BeaconState::from_ssz_bytes(HARDCODED_GENESIS_STATE)
|
),
|
||||||
.map_err(|e| format!("Unable to parse genesis state: {:?}", e))?,
|
genesis_state: Some(
|
||||||
),
|
BeaconState::from_ssz_bytes(HARDCODED_GENESIS_STATE)
|
||||||
yaml_config: Some(
|
.map_err(|e| format!("Unable to parse genesis state: {:?}", e))?,
|
||||||
serde_yaml::from_reader(HARDCODED_YAML_CONFIG)
|
),
|
||||||
.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.
|
// Write the files to the directory.
|
||||||
@ -207,17 +215,16 @@ mod tests {
|
|||||||
|
|
||||||
type E = MainnetEthSpec;
|
type E = MainnetEthSpec;
|
||||||
|
|
||||||
/* TODO: disabled until testnet config is updated for v0.11
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hard_coded_works() {
|
fn hard_coded_works() {
|
||||||
let dir: Eth2TestnetConfig<E> =
|
if let Some(dir) =
|
||||||
Eth2TestnetConfig::hard_coded().expect("should decode hard_coded params");
|
Eth2TestnetConfig::<E>::hard_coded().expect("should decode hard_coded params")
|
||||||
|
{
|
||||||
assert!(dir.boot_enr.is_some());
|
assert!(dir.boot_enr.is_some());
|
||||||
assert!(dir.genesis_state.is_some());
|
assert!(dir.genesis_state.is_some());
|
||||||
assert!(dir.yaml_config.is_some());
|
assert!(dir.yaml_config.is_some());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip() {
|
fn round_trip() {
|
||||||
|
@ -36,7 +36,7 @@ fn worst_bench<T: EthSpec>(c: &mut Criterion, spec_desc: &str, validator_count:
|
|||||||
let mut spec = &mut T::default_spec();
|
let mut spec = &mut T::default_spec();
|
||||||
|
|
||||||
// Allows the exits to be processed sucessfully.
|
// 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);
|
let (block, state) = get_worst_block(validator_count, spec);
|
||||||
bench_block::<T>(c, block, state, spec, spec_desc, "high_complexity_block");
|
bench_block::<T>(c, block, state, spec, spec_desc, "high_complexity_block");
|
||||||
|
@ -157,6 +157,15 @@ pub fn process_block_header<T: EthSpec>(
|
|||||||
// Verify that the slots match
|
// Verify that the slots match
|
||||||
verify!(block.slot == state.slot, HeaderInvalid::StateSlotMismatch);
|
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
|
// Verify that proposer index is the correct index
|
||||||
let proposer_index = block.proposer_index as usize;
|
let proposer_index = block.proposer_index as usize;
|
||||||
let state_proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
|
let state_proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
|
||||||
|
@ -176,6 +176,10 @@ impl<T> From<ArithError> for BlockOperationError<T> {
|
|||||||
pub enum HeaderInvalid {
|
pub enum HeaderInvalid {
|
||||||
ProposalSignatureInvalid,
|
ProposalSignatureInvalid,
|
||||||
StateSlotMismatch,
|
StateSlotMismatch,
|
||||||
|
OlderThanLatestBlockHeader {
|
||||||
|
latest_block_header_slot: Slot,
|
||||||
|
block_slot: Slot,
|
||||||
|
},
|
||||||
ProposerIndexMismatch {
|
ProposerIndexMismatch {
|
||||||
block_proposer_index: usize,
|
block_proposer_index: usize,
|
||||||
state_proposer_index: usize,
|
state_proposer_index: usize,
|
||||||
@ -255,9 +259,6 @@ pub enum AttestationInvalid {
|
|||||||
attestation: Checkpoint,
|
attestation: Checkpoint,
|
||||||
is_current: bool,
|
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.
|
/// The aggregation bitfield length is not the smallest possible size to represent the committee.
|
||||||
BadAggregationBitfieldLength {
|
BadAggregationBitfieldLength {
|
||||||
committee_len: usize,
|
committee_len: usize,
|
||||||
@ -291,10 +292,8 @@ impl From<BlockOperationError<IndexedAttestationInvalid>>
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum IndexedAttestationInvalid {
|
pub enum IndexedAttestationInvalid {
|
||||||
/// The number of indices exceeds the global maximum.
|
/// The number of indices is 0.
|
||||||
///
|
IndicesEmpty,
|
||||||
/// (max_indices, indices_given)
|
|
||||||
MaxIndicesExceed(usize, usize),
|
|
||||||
/// The validator indices were not in increasing order.
|
/// The validator indices were not in increasing order.
|
||||||
///
|
///
|
||||||
/// The error occurred between the given `index` and `index + 1`
|
/// The error occurred between the given `index` and `index + 1`
|
||||||
|
@ -20,11 +20,8 @@ pub fn is_valid_indexed_attestation<T: EthSpec>(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let indices = &indexed_attestation.attesting_indices;
|
let indices = &indexed_attestation.attesting_indices;
|
||||||
|
|
||||||
// Verify max number of indices
|
// Verify that indices aren't empty
|
||||||
verify!(
|
verify!(!indices.is_empty(), Invalid::IndicesEmpty);
|
||||||
indices.len() <= T::MaxValidatorsPerCommittee::to_usize(),
|
|
||||||
Invalid::MaxIndicesExceed(T::MaxValidatorsPerCommittee::to_usize(), indices.len())
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check that indices are sorted and unique
|
// Check that indices are sorted and unique
|
||||||
let check_sorted = |list: &[u64]| -> Result<()> {
|
let check_sorted = |list: &[u64]| -> Result<()> {
|
||||||
|
@ -11,7 +11,7 @@ use types::{
|
|||||||
AggregateSignature, AttesterSlashing, BeaconBlock, BeaconState, BeaconStateError, ChainSpec,
|
AggregateSignature, AttesterSlashing, BeaconBlock, BeaconState, BeaconStateError, ChainSpec,
|
||||||
DepositData, Domain, EthSpec, Fork, Hash256, IndexedAttestation, ProposerSlashing, PublicKey,
|
DepositData, Domain, EthSpec, Fork, Hash256, IndexedAttestation, ProposerSlashing, PublicKey,
|
||||||
Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot,
|
Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot,
|
||||||
SignedVoluntaryExit, SigningRoot,
|
SignedVoluntaryExit, SigningData,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@ -92,7 +92,7 @@ where
|
|||||||
);
|
);
|
||||||
|
|
||||||
let message = if let Some(root) = block_root {
|
let message = if let Some(root) = block_root {
|
||||||
SigningRoot {
|
SigningData {
|
||||||
object_root: root,
|
object_root: root,
|
||||||
domain,
|
domain,
|
||||||
}
|
}
|
||||||
|
@ -58,13 +58,6 @@ pub fn verify_attestation_for_state<T: EthSpec>(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let data = &attestation.data;
|
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!(
|
verify!(
|
||||||
data.index < state.get_committee_count_at_slot(data.slot)?,
|
data.index < state.get_committee_count_at_slot(data.slot)?,
|
||||||
Invalid::BadCommitteeIndex
|
Invalid::BadCommitteeIndex
|
||||||
|
@ -78,10 +78,10 @@ fn verify_exit_parametric<T: EthSpec>(
|
|||||||
|
|
||||||
// Verify the validator has been active long enough.
|
// Verify the validator has been active long enough.
|
||||||
verify!(
|
verify!(
|
||||||
state.current_epoch() >= validator.activation_epoch + spec.persistent_committee_period,
|
state.current_epoch() >= validator.activation_epoch + spec.shard_committee_period,
|
||||||
ExitInvalid::TooYoungToExit {
|
ExitInvalid::TooYoungToExit {
|
||||||
current_epoch: state.current_epoch(),
|
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,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -51,11 +51,7 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
|
|||||||
return Err(Error::ValidatorStatusesInconsistent);
|
return Err(Error::ValidatorStatusesInconsistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut deltas = vec![Delta::default(); state.balances.len()];
|
let deltas = get_attestation_deltas(state, &validator_statuses, spec)?;
|
||||||
|
|
||||||
get_attestation_deltas(&mut deltas, state, &validator_statuses, spec)?;
|
|
||||||
|
|
||||||
get_proposer_deltas(&mut deltas, state, validator_statuses, spec)?;
|
|
||||||
|
|
||||||
// Apply the deltas, erroring on overflow above but not on overflow below (saturating at 0
|
// Apply the deltas, erroring on overflow above but not on overflow below (saturating at 0
|
||||||
// instead).
|
// instead).
|
||||||
@ -67,78 +63,63 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For each attesting validator, reward the proposer who was first to include their attestation.
|
|
||||||
///
|
|
||||||
/// Spec v0.11.1
|
|
||||||
fn get_proposer_deltas<T: EthSpec>(
|
|
||||||
deltas: &mut Vec<Delta>,
|
|
||||||
state: &BeaconState<T>,
|
|
||||||
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.
|
/// Apply rewards for participation in attestations during the previous epoch.
|
||||||
///
|
///
|
||||||
/// Spec v0.11.1
|
/// Spec v0.11.1
|
||||||
fn get_attestation_deltas<T: EthSpec>(
|
fn get_attestation_deltas<T: EthSpec>(
|
||||||
deltas: &mut Vec<Delta>,
|
|
||||||
state: &BeaconState<T>,
|
state: &BeaconState<T>,
|
||||||
validator_statuses: &ValidatorStatuses,
|
validator_statuses: &ValidatorStatuses,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<Vec<Delta>, Error> {
|
||||||
let finality_delay = (state.previous_epoch() - state.finalized_checkpoint.epoch).as_u64();
|
let finality_delay = (state.previous_epoch() - state.finalized_checkpoint.epoch).as_u64();
|
||||||
|
|
||||||
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
|
let mut deltas = vec![Delta::default(); state.validators.len()];
|
||||||
let base_reward = get_base_reward(
|
|
||||||
state,
|
|
||||||
index,
|
|
||||||
validator_statuses.total_balances.current_epoch(),
|
|
||||||
spec,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let delta = get_attestation_delta::<T>(
|
let total_balances = &validator_statuses.total_balances;
|
||||||
&validator,
|
|
||||||
&validator_statuses.total_balances,
|
|
||||||
base_reward,
|
|
||||||
finality_delay,
|
|
||||||
spec,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
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.
|
fn get_attestation_component_delta(
|
||||||
///
|
index_in_unslashed_attesting_indices: bool,
|
||||||
/// Spec v0.11.1
|
attesting_balance: u64,
|
||||||
fn get_attestation_delta<T: EthSpec>(
|
|
||||||
validator: &ValidatorStatus,
|
|
||||||
total_balances: &TotalBalances,
|
total_balances: &TotalBalances,
|
||||||
base_reward: u64,
|
base_reward: u64,
|
||||||
finality_delay: u64,
|
finality_delay: u64,
|
||||||
@ -146,83 +127,122 @@ fn get_attestation_delta<T: EthSpec>(
|
|||||||
) -> Result<Delta, Error> {
|
) -> Result<Delta, Error> {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
|
|
||||||
// Is this validator eligible to be rewarded or penalized?
|
let total_balance = total_balances.current_epoch();
|
||||||
// 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);
|
|
||||||
|
|
||||||
if !is_eligible {
|
if index_in_unslashed_attesting_indices {
|
||||||
return Ok(delta);
|
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
|
Ok(delta)
|
||||||
// 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)?;
|
|
||||||
|
|
||||||
// Expected FFG source.
|
fn get_source_delta(
|
||||||
// Spec:
|
validator: &ValidatorStatus,
|
||||||
// - validator index in `get_unslashed_attesting_indices(state, matching_source_attestations)`
|
base_reward: u64,
|
||||||
|
total_balances: &TotalBalances,
|
||||||
|
finality_delay: u64,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<Delta, Error> {
|
||||||
|
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<Delta, Error> {
|
||||||
|
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<Delta, Error> {
|
||||||
|
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 {
|
if validator.is_previous_epoch_attester && !validator.is_slashed {
|
||||||
delta.reward(
|
let mut delta = Delta::default();
|
||||||
base_reward
|
let mut proposer_delta = Delta::default();
|
||||||
.safe_mul(total_attesting_balance_ebi)?
|
|
||||||
.safe_div(total_balance_ebi)?,
|
let inclusion_info = validator
|
||||||
)?;
|
|
||||||
// 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
|
|
||||||
.inclusion_info
|
.inclusion_info
|
||||||
.expect("It is a logic error for an attester not to have an inclusion delay.");
|
.ok_or(Error::ValidatorStatusesInconsistent)?;
|
||||||
delta.reward(max_attester_reward.safe_div(inclusion.delay)?)?;
|
|
||||||
} else {
|
|
||||||
delta.penalize(base_reward)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expected FFG target.
|
let proposer_reward = get_proposer_reward(base_reward, spec)?;
|
||||||
// Spec:
|
proposer_delta.reward(proposer_reward)?;
|
||||||
// - 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)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expected head.
|
let max_attester_reward = base_reward.safe_sub(proposer_reward)?;
|
||||||
// Spec:
|
delta.reward(max_attester_reward.safe_div(inclusion_info.delay)?)?;
|
||||||
// - validator index in `get_unslashed_attesting_indices(state, matching_head_attestations)`
|
|
||||||
if validator.is_previous_epoch_head_attester && !validator.is_slashed {
|
let proposer_index = inclusion_info.proposer_index as usize;
|
||||||
delta.reward(
|
Ok((delta, Some((proposer_index, proposer_delta))))
|
||||||
base_reward
|
|
||||||
.safe_mul(matching_head_balance_ebi)?
|
|
||||||
.safe_div(total_balance_ebi)?,
|
|
||||||
)?;
|
|
||||||
} else {
|
} 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<Delta, Error> {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
|
||||||
// Inactivity penalty
|
// Inactivity penalty
|
||||||
if finality_delay > spec.min_epochs_to_inactivity_penalty {
|
if finality_delay > spec.min_epochs_to_inactivity_penalty {
|
||||||
// All eligible validators are penalized
|
// If validator is performing optimally this cancels all rewards for a neutral balance
|
||||||
delta.penalize(spec.base_rewards_per_epoch.safe_mul(base_reward)?)?;
|
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
|
// Additionally, all validators whose FFG target didn't match are penalized extra
|
||||||
// This condition is equivalent to this condition from the spec:
|
// This condition is equivalent to this condition from the spec:
|
||||||
@ -237,10 +257,20 @@ fn get_attestation_delta<T: EthSpec>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
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<u64, Error> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
@ -215,7 +215,7 @@ mod signatures_minimal {
|
|||||||
let mut spec = &mut TestEthSpec::default_spec();
|
let mut spec = &mut TestEthSpec::default_spec();
|
||||||
|
|
||||||
// Allows the test to pass.
|
// Allows the test to pass.
|
||||||
spec.persistent_committee_period = 0;
|
spec.shard_committee_period = 0;
|
||||||
|
|
||||||
test_scenario::<TestEthSpec, _, _>(
|
test_scenario::<TestEthSpec, _, _>(
|
||||||
|mut builder| {
|
|mut builder| {
|
||||||
|
@ -82,7 +82,7 @@ pub struct ChainSpec {
|
|||||||
pub max_seed_lookahead: Epoch,
|
pub max_seed_lookahead: Epoch,
|
||||||
pub min_epochs_to_inactivity_penalty: u64,
|
pub min_epochs_to_inactivity_penalty: u64,
|
||||||
pub min_validator_withdrawability_delay: Epoch,
|
pub min_validator_withdrawability_delay: Epoch,
|
||||||
pub persistent_committee_period: u64,
|
pub shard_committee_period: u64,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reward and penalty quotients
|
* Reward and penalty quotients
|
||||||
@ -292,7 +292,7 @@ impl ChainSpec {
|
|||||||
max_seed_lookahead: Epoch::new(4),
|
max_seed_lookahead: Epoch::new(4),
|
||||||
min_epochs_to_inactivity_penalty: 4,
|
min_epochs_to_inactivity_penalty: 4,
|
||||||
min_validator_withdrawability_delay: Epoch::new(256),
|
min_validator_withdrawability_delay: Epoch::new(256),
|
||||||
persistent_committee_period: 2_048,
|
shard_committee_period: 256,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reward and penalty quotients
|
* Reward and penalty quotients
|
||||||
@ -300,7 +300,7 @@ impl ChainSpec {
|
|||||||
base_reward_factor: 64,
|
base_reward_factor: 64,
|
||||||
whistleblower_reward_quotient: 512,
|
whistleblower_reward_quotient: 512,
|
||||||
proposer_reward_quotient: 8,
|
proposer_reward_quotient: 8,
|
||||||
inactivity_penalty_quotient: 33_554_432,
|
inactivity_penalty_quotient: u64::pow(2, 24),
|
||||||
min_slashing_penalty_quotient: 32,
|
min_slashing_penalty_quotient: 32,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -353,7 +353,7 @@ impl ChainSpec {
|
|||||||
min_genesis_active_validator_count: 64,
|
min_genesis_active_validator_count: 64,
|
||||||
eth1_follow_distance: 16,
|
eth1_follow_distance: 16,
|
||||||
genesis_fork_version: [0x00, 0x00, 0x00, 0x01],
|
genesis_fork_version: [0x00, 0x00, 0x00, 0x01],
|
||||||
persistent_committee_period: 128,
|
shard_committee_period: 64,
|
||||||
min_genesis_delay: 300,
|
min_genesis_delay: 300,
|
||||||
milliseconds_per_slot: 6_000,
|
milliseconds_per_slot: 6_000,
|
||||||
safe_slots_to_update_justified: 2,
|
safe_slots_to_update_justified: 2,
|
||||||
@ -481,7 +481,7 @@ pub struct YamlConfig {
|
|||||||
max_seed_lookahead: u64,
|
max_seed_lookahead: u64,
|
||||||
min_epochs_to_inactivity_penalty: u64,
|
min_epochs_to_inactivity_penalty: u64,
|
||||||
min_validator_withdrawability_delay: u64,
|
min_validator_withdrawability_delay: u64,
|
||||||
persistent_committee_period: u64,
|
shard_committee_period: u64,
|
||||||
base_reward_factor: u64,
|
base_reward_factor: u64,
|
||||||
whistleblower_reward_quotient: u64,
|
whistleblower_reward_quotient: u64,
|
||||||
proposer_reward_quotient: u64,
|
proposer_reward_quotient: u64,
|
||||||
@ -591,7 +591,7 @@ impl YamlConfig {
|
|||||||
min_seed_lookahead: spec.min_seed_lookahead.into(),
|
min_seed_lookahead: spec.min_seed_lookahead.into(),
|
||||||
max_seed_lookahead: spec.max_seed_lookahead.into(),
|
max_seed_lookahead: spec.max_seed_lookahead.into(),
|
||||||
min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay.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,
|
min_epochs_to_inactivity_penalty: spec.min_epochs_to_inactivity_penalty,
|
||||||
base_reward_factor: spec.base_reward_factor,
|
base_reward_factor: spec.base_reward_factor,
|
||||||
whistleblower_reward_quotient: spec.whistleblower_reward_quotient,
|
whistleblower_reward_quotient: spec.whistleblower_reward_quotient,
|
||||||
@ -690,7 +690,7 @@ impl YamlConfig {
|
|||||||
min_validator_withdrawability_delay: Epoch::from(
|
min_validator_withdrawability_delay: Epoch::from(
|
||||||
self.min_validator_withdrawability_delay,
|
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,
|
min_epochs_to_inactivity_penalty: self.min_epochs_to_inactivity_penalty,
|
||||||
base_reward_factor: self.base_reward_factor,
|
base_reward_factor: self.base_reward_factor,
|
||||||
whistleblower_reward_quotient: self.whistleblower_reward_quotient,
|
whistleblower_reward_quotient: self.whistleblower_reward_quotient,
|
||||||
|
@ -3,7 +3,7 @@ use crate::*;
|
|||||||
use safe_arith::SafeArith;
|
use safe_arith::SafeArith;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use ssz_types::typenum::{
|
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,
|
U65536, U8, U8192,
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@ -151,7 +151,7 @@ impl EthSpec for MainnetEthSpec {
|
|||||||
type HistoricalRootsLimit = U16777216;
|
type HistoricalRootsLimit = U16777216;
|
||||||
type ValidatorRegistryLimit = U1099511627776;
|
type ValidatorRegistryLimit = U1099511627776;
|
||||||
type MaxProposerSlashings = U16;
|
type MaxProposerSlashings = U16;
|
||||||
type MaxAttesterSlashings = U1;
|
type MaxAttesterSlashings = U2;
|
||||||
type MaxAttestations = U128;
|
type MaxAttestations = U128;
|
||||||
type MaxDeposits = U16;
|
type MaxDeposits = U16;
|
||||||
type MaxVoluntaryExits = U16;
|
type MaxVoluntaryExits = U16;
|
||||||
@ -178,12 +178,12 @@ pub struct MinimalEthSpec;
|
|||||||
|
|
||||||
impl EthSpec for MinimalEthSpec {
|
impl EthSpec for MinimalEthSpec {
|
||||||
type SlotsPerEpoch = U8;
|
type SlotsPerEpoch = U8;
|
||||||
type EpochsPerEth1VotingPeriod = U2;
|
type EpochsPerEth1VotingPeriod = U4;
|
||||||
type SlotsPerHistoricalRoot = U64;
|
type SlotsPerHistoricalRoot = U64;
|
||||||
type EpochsPerHistoricalVector = U64;
|
type EpochsPerHistoricalVector = U64;
|
||||||
type EpochsPerSlashingsVector = U64;
|
type EpochsPerSlashingsVector = U64;
|
||||||
type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch
|
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 {
|
params_from_eth_spec!(MainnetEthSpec {
|
||||||
JustificationBitsLength,
|
JustificationBitsLength,
|
||||||
|
@ -39,7 +39,7 @@ pub mod signed_aggregate_and_proof;
|
|||||||
pub mod signed_beacon_block;
|
pub mod signed_beacon_block;
|
||||||
pub mod signed_beacon_block_header;
|
pub mod signed_beacon_block_header;
|
||||||
pub mod signed_voluntary_exit;
|
pub mod signed_voluntary_exit;
|
||||||
pub mod signing_root;
|
pub mod signing_data;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod validator;
|
pub mod validator;
|
||||||
pub mod voluntary_exit;
|
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::{SignedBeaconBlock, SignedBeaconBlockHash};
|
||||||
pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader;
|
pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader;
|
||||||
pub use crate::signed_voluntary_exit::SignedVoluntaryExit;
|
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::slot_epoch::{Epoch, Slot};
|
||||||
pub use crate::subnet_id::SubnetId;
|
pub use crate::subnet_id::SubnetId;
|
||||||
pub use crate::validator::Validator;
|
pub use crate::validator::Validator;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
test_utils::TestRandom, BeaconBlock, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey,
|
test_utils::TestRandom, BeaconBlock, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey,
|
||||||
SignedRoot, SigningRoot, Slot,
|
SignedRoot, SigningData, Slot,
|
||||||
};
|
};
|
||||||
use bls::Signature;
|
use bls::Signature;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
@ -69,7 +69,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let message = if let Some(object_root) = object_root_opt {
|
let message = if let Some(object_root) = object_root_opt {
|
||||||
SigningRoot {
|
SigningData {
|
||||||
object_root,
|
object_root,
|
||||||
domain,
|
domain,
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,14 @@ use tree_hash_derive::TreeHash;
|
|||||||
|
|
||||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||||
pub struct SigningRoot {
|
pub struct SigningData {
|
||||||
pub object_root: Hash256,
|
pub object_root: Hash256,
|
||||||
pub domain: Hash256,
|
pub domain: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SignedRoot: TreeHash {
|
pub trait SignedRoot: TreeHash {
|
||||||
fn signing_root(&self, domain: Hash256) -> Hash256 {
|
fn signing_root(&self, domain: Hash256) -> Hash256 {
|
||||||
SigningRoot {
|
SigningData {
|
||||||
object_root: self.tree_hash_root(),
|
object_root: self.tree_hash_root(),
|
||||||
domain,
|
domain,
|
||||||
}
|
}
|
@ -147,7 +147,7 @@ impl<E: EthSpec> EnvironmentBuilder<E> {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setups eth2 config using the CLI arguments.
|
/// Adds a testnet configuration to the environment.
|
||||||
pub fn eth2_testnet_config(
|
pub fn eth2_testnet_config(
|
||||||
mut self,
|
mut self,
|
||||||
eth2_testnet_config: Eth2TestnetConfig<E>,
|
eth2_testnet_config: Eth2TestnetConfig<E>,
|
||||||
@ -170,6 +170,18 @@ impl<E: EthSpec> EnvironmentBuilder<E> {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Optionally adds a testnet configuration to the environment.
|
||||||
|
pub fn optional_eth2_testnet_config(
|
||||||
|
self,
|
||||||
|
optional_config: Option<Eth2TestnetConfig<E>>,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
if let Some(config) = optional_config {
|
||||||
|
self.eth2_testnet_config(config)
|
||||||
|
} else {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes the builder, returning an `Environment`.
|
/// Consumes the builder, returning an `Environment`.
|
||||||
pub fn build(self) -> Result<Environment<E>, String> {
|
pub fn build(self) -> Result<Environment<E>, String> {
|
||||||
Ok(Environment {
|
Ok(Environment {
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* TODO: disabled until hardcoded testnet config is updated for v0.11
|
|
||||||
*
|
|
||||||
*
|
|
||||||
#![cfg(test)]
|
#![cfg(test)]
|
||||||
|
|
||||||
use clap::ArgMatches;
|
|
||||||
use environment::EnvironmentBuilder;
|
use environment::EnvironmentBuilder;
|
||||||
use eth2_testnet_config::Eth2TestnetConfig;
|
use eth2_testnet_config::Eth2TestnetConfig;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -19,75 +13,32 @@ fn builder() -> EnvironmentBuilder<MainnetEthSpec> {
|
|||||||
.expect("should set logger")
|
.expect("should set logger")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dummy_data_dir() -> PathBuf {
|
fn eth2_testnet_config() -> Option<Eth2TestnetConfig<MainnetEthSpec>> {
|
||||||
PathBuf::from("./tests/datadir_that_does_not_exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eth2_testnet_config() -> Eth2TestnetConfig<MainnetEthSpec> {
|
|
||||||
Eth2TestnetConfig::hard_coded().expect("should decode hard_coded params")
|
Eth2TestnetConfig::hard_coded().expect("should decode hard_coded params")
|
||||||
}
|
}
|
||||||
|
|
||||||
mod setup_eth2_config {
|
mod setup_eth2_config {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn update_spec_with_yaml_config() {
|
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(
|
||||||
eth2_testnet_config.yaml_config =
|
YamlConfig::from_file(config_yaml.as_path()).expect("should load yaml config"),
|
||||||
Some(YamlConfig::from_file(config_yaml.as_path()).expect("should load yaml config"));
|
);
|
||||||
|
|
||||||
let environment = builder()
|
let environment = builder()
|
||||||
.setup_eth2_config(
|
.eth2_testnet_config(eth2_testnet_config)
|
||||||
dummy_data_dir(),
|
.expect("should setup eth2_config")
|
||||||
eth2_testnet_config,
|
.build()
|
||||||
&ArgMatches::default(),
|
.expect("should build environment");
|
||||||
)
|
|
||||||
.expect("should setup eth2_config")
|
|
||||||
.build()
|
|
||||||
.expect("should build environment");
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
environment.eth2_config.spec.far_future_epoch,
|
environment.eth2_config.spec.far_future_epoch,
|
||||||
Epoch::new(999) // see testnet_dir/config.yaml
|
Epoch::new(999) // see testnet_dir/config.yaml
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -24,7 +24,7 @@ min_seed_lookahead = 1
|
|||||||
max_seed_lookahead = 4
|
max_seed_lookahead = 4
|
||||||
min_epochs_to_inactivity_penalty = 4
|
min_epochs_to_inactivity_penalty = 4
|
||||||
min_validator_withdrawability_delay = 256
|
min_validator_withdrawability_delay = 256
|
||||||
persistent_committee_period = 2048
|
shard_committee_period = 2048
|
||||||
base_reward_factor = 64
|
base_reward_factor = 64
|
||||||
whistleblower_reward_quotient = 512
|
whistleblower_reward_quotient = 512
|
||||||
proposer_reward_quotient = 8
|
proposer_reward_quotient = 8
|
||||||
|
@ -132,13 +132,14 @@ fn run<E: EthSpec>(
|
|||||||
.ok_or_else(|| "Expected --debug-level flag".to_string())?;
|
.ok_or_else(|| "Expected --debug-level flag".to_string())?;
|
||||||
|
|
||||||
let log_format = matches.value_of("log-format");
|
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")?;
|
clap_utils::parse_testnet_dir_with_hardcoded_default(matches, "testnet-dir")?;
|
||||||
|
|
||||||
let mut environment = environment_builder
|
let mut environment = environment_builder
|
||||||
.async_logger(debug_level, log_format)?
|
.async_logger(debug_level, log_format)?
|
||||||
.multi_threaded_tokio_runtime()?
|
.multi_threaded_tokio_runtime()?
|
||||||
.eth2_testnet_config(eth2_testnet_config)?
|
.optional_eth2_testnet_config(optional_testnet_config)?
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let log = environment.core_context().log;
|
let log = environment.core_context().log;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Bump the test tag here and in .gitlab-ci.yml and CI will take care of updating the cached tarballs
|
# 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
|
TESTS = general minimal mainnet
|
||||||
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
||||||
|
|
||||||
|
@ -25,12 +25,17 @@ impl Case for BlsAggregateSigs {
|
|||||||
aggregate_signature.add(&sig);
|
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..])
|
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());
|
let aggregate_signature = Ok(aggregate_signature.as_bytes().to_vec());
|
||||||
|
|
||||||
compare_result::<Vec<u8>, Vec<u8>>(&aggregate_signature, &output_bytes)
|
compare_result::<Vec<u8>, Vec<u8>>(&aggregate_signature, &Some(output_bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,10 @@ use crate::cases::common::BlsCase;
|
|||||||
use bls::{AggregateSignature, PublicKey};
|
use bls::{AggregateSignature, PublicKey};
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct BlsAggregatePair {
|
|
||||||
pub pubkey: PublicKey,
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct BlsAggregateVerifyInput {
|
pub struct BlsAggregateVerifyInput {
|
||||||
pub pairs: Vec<BlsAggregatePair>,
|
pub pubkeys: Vec<PublicKey>,
|
||||||
|
pub messages: Vec<String>,
|
||||||
pub signature: String,
|
pub signature: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,11 +23,10 @@ impl Case for BlsAggregateVerify {
|
|||||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||||
let messages = self
|
let messages = self
|
||||||
.input
|
.input
|
||||||
.pairs
|
.messages
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pair| {
|
.map(|message| {
|
||||||
hex::decode(&pair.message[2..])
|
hex::decode(&message[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))
|
||||||
.map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))
|
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<Vec<_>>, _>>()?;
|
.collect::<Result<Vec<Vec<_>>, _>>()?;
|
||||||
|
|
||||||
@ -41,12 +35,7 @@ impl Case for BlsAggregateVerify {
|
|||||||
.map(|x| x.as_slice())
|
.map(|x| x.as_slice())
|
||||||
.collect::<Vec<&[u8]>>();
|
.collect::<Vec<&[u8]>>();
|
||||||
|
|
||||||
let pubkey_refs = self
|
let pubkey_refs = self.input.pubkeys.iter().collect::<Vec<_>>();
|
||||||
.input
|
|
||||||
.pairs
|
|
||||||
.iter()
|
|
||||||
.map(|p| &p.pubkey)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let signature_ok = hex::decode(&self.input.signature[2..])
|
let signature_ok = hex::decode(&self.input.signature[2..])
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -27,7 +27,7 @@ struct Metadata {
|
|||||||
pub struct Operations<E: EthSpec, O: Operation<E>> {
|
pub struct Operations<E: EthSpec, O: Operation<E>> {
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
pub pre: BeaconState<E>,
|
pub pre: BeaconState<E>,
|
||||||
pub operation: O,
|
pub operation: Option<O>,
|
||||||
pub post: Option<BeaconState<E>>,
|
pub post: Option<BeaconState<E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,8 +135,16 @@ impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
|
|||||||
} else {
|
} else {
|
||||||
Metadata::default()
|
Metadata::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let pre = ssz_decode_file(&path.join("pre.ssz"))?;
|
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_filename = path.join("post.ssz");
|
||||||
let post = if post_filename.is_file() {
|
let post = if post_filename.is_file() {
|
||||||
Some(ssz_decode_file(&post_filename)?)
|
Some(ssz_decode_file(&post_filename)?)
|
||||||
@ -162,8 +170,6 @@ impl<E: EthSpec, O: Operation<E>> Case for Operations<E, O> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||||
self.metadata.bls_setting.unwrap_or_default().check()?;
|
|
||||||
|
|
||||||
let spec = &E::default_spec();
|
let spec = &E::default_spec();
|
||||||
let mut state = self.pre.clone();
|
let mut state = self.pre.clone();
|
||||||
let mut expected = self.post.clone();
|
let mut expected = self.post.clone();
|
||||||
@ -173,7 +179,12 @@ impl<E: EthSpec, O: Operation<E>> Case for Operations<E, O> {
|
|||||||
.build_all_committee_caches(spec)
|
.build_all_committee_caches(spec)
|
||||||
.expect("committee caches OK");
|
.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)
|
compare_beacon_state_results_without_caches(&mut result, &mut expected)
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,25 @@ fn minimal_config_ok() {
|
|||||||
config_test::<MinimalEthSpec>();
|
config_test::<MinimalEthSpec>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<E: EthSpec>() {
|
||||||
|
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::<MinimalEthSpec>();
|
||||||
|
check_typenum_values::<MainnetEthSpec>();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shuffling() {
|
fn shuffling() {
|
||||||
ShufflingHandler::<MinimalEthSpec>::run();
|
ShufflingHandler::<MinimalEthSpec>::run();
|
||||||
|
@ -7,8 +7,8 @@ use types::{BeaconBlock, BeaconState, Epoch, EthSpec, SignedBeaconBlock};
|
|||||||
|
|
||||||
// Default validator index to exit.
|
// Default validator index to exit.
|
||||||
pub const VALIDATOR_INDEX: u64 = 0;
|
pub const VALIDATOR_INDEX: u64 = 0;
|
||||||
// Epoch that the state will be transitioned to by default, equal to PERSISTENT_COMMITTEE_PERIOD.
|
// Epoch that the state will be transitioned to by default, equal to SHARD_COMMITTEE_PERIOD.
|
||||||
pub const STATE_EPOCH: Epoch = Epoch::new(2048);
|
pub const STATE_EPOCH: Epoch = Epoch::new(256);
|
||||||
|
|
||||||
struct ExitTest {
|
struct ExitTest {
|
||||||
validator_index: u64,
|
validator_index: u64,
|
||||||
@ -62,7 +62,7 @@ impl ExitTest {
|
|||||||
fn run(self) -> BeaconState<E> {
|
fn run(self) -> BeaconState<E> {
|
||||||
let spec = &E::default_spec();
|
let spec = &E::default_spec();
|
||||||
let expected = self.expected.clone();
|
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();
|
let (block, mut state) = self.block_and_pre_state();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user