Add progress on op pool test fixes
This commit is contained in:
parent
2a938f2fd5
commit
77c4b6eafe
@ -718,13 +718,16 @@ mod tests {
|
|||||||
|
|
||||||
/// Create a signed attestation for use in tests.
|
/// Create a signed attestation for use in tests.
|
||||||
/// Signed by all validators in `committee[signing_range]` and `committee[extra_signer]`.
|
/// Signed by all validators in `committee[signing_range]` and `committee[extra_signer]`.
|
||||||
#[cfg(not(debug_assertions))]
|
// #[cfg(not(debug_assertions))]
|
||||||
fn signed_attestation<R: std::slice::SliceIndex<[usize], Output = [usize]>>(
|
fn signed_attestation<
|
||||||
|
R: std::slice::SliceIndex<[usize], Output = [usize]>,
|
||||||
|
B: BeaconStateTypes,
|
||||||
|
>(
|
||||||
committee: &CrosslinkCommittee,
|
committee: &CrosslinkCommittee,
|
||||||
keypairs: &[Keypair],
|
keypairs: &[Keypair],
|
||||||
signing_range: R,
|
signing_range: R,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
state: &BeaconState,
|
state: &BeaconState<B>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
extra_signer: Option<usize>,
|
extra_signer: Option<usize>,
|
||||||
) -> Attestation {
|
) -> Attestation {
|
||||||
@ -750,259 +753,270 @@ mod tests {
|
|||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test state for attestation-related tests.
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
fn attestation_test_state(
|
mod release_tests {
|
||||||
spec: &ChainSpec,
|
use super::*;
|
||||||
num_committees: usize,
|
|
||||||
) -> (BeaconState, Vec<Keypair>) {
|
|
||||||
let num_validators =
|
|
||||||
num_committees * (spec.slots_per_epoch * spec.target_committee_size) as usize;
|
|
||||||
let mut state_builder =
|
|
||||||
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(num_validators, spec);
|
|
||||||
let slot_offset = 1000 * spec.slots_per_epoch + spec.slots_per_epoch / 2;
|
|
||||||
let slot = spec.genesis_slot + slot_offset;
|
|
||||||
state_builder.teleport_to_slot(slot, spec);
|
|
||||||
state_builder.build_caches(spec).unwrap();
|
|
||||||
state_builder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the latest crosslink in the state to match the attestation.
|
/// Test state for attestation-related tests.
|
||||||
#[cfg(not(debug_assertions))]
|
fn attestation_test_state<B: BeaconStateTypes>(
|
||||||
fn fake_latest_crosslink(att: &Attestation, state: &mut BeaconState, spec: &ChainSpec) {
|
num_committees: usize,
|
||||||
state.latest_crosslinks[att.data.shard as usize] = Crosslink {
|
) -> (BeaconState<B>, Vec<Keypair>, ChainSpec) {
|
||||||
crosslink_data_root: att.data.crosslink_data_root,
|
let spec = B::spec();
|
||||||
epoch: att.data.slot.epoch(spec.slots_per_epoch),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
let num_validators =
|
||||||
#[cfg(not(debug_assertions))]
|
num_committees * (spec.slots_per_epoch * spec.target_committee_size) as usize;
|
||||||
fn test_attestation_score() {
|
let mut state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(
|
||||||
let spec = &ChainSpec::foundation();
|
num_validators,
|
||||||
let (ref mut state, ref keypairs) = attestation_test_state(spec, 1);
|
&spec,
|
||||||
let slot = state.slot - 1;
|
|
||||||
let committees = state
|
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
for committee in committees {
|
|
||||||
let att1 = signed_attestation(&committee, keypairs, ..2, slot, state, spec, None);
|
|
||||||
let att2 = signed_attestation(&committee, keypairs, .., slot, state, spec, None);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
att1.aggregation_bitfield.num_set_bits(),
|
|
||||||
attestation_score(&att1, state, spec)
|
|
||||||
);
|
);
|
||||||
|
let slot_offset = 1000 * spec.slots_per_epoch + spec.slots_per_epoch / 2;
|
||||||
|
let slot = spec.genesis_slot + slot_offset;
|
||||||
|
state_builder.teleport_to_slot(slot, &spec);
|
||||||
|
state_builder.build_caches(&spec).unwrap();
|
||||||
|
let (state, keypairs) = state_builder.build();
|
||||||
|
|
||||||
state
|
(state, keypairs, FoundationStateTypes::spec())
|
||||||
.current_epoch_attestations
|
|
||||||
.push(PendingAttestation::from_attestation(&att1, state.slot));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
committee.committee.len() - 2,
|
|
||||||
attestation_score(&att2, state, spec)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// End-to-end test of basic attestation handling.
|
/// Set the latest crosslink in the state to match the attestation.
|
||||||
#[test]
|
fn fake_latest_crosslink<T: BeaconStateTypes>(
|
||||||
#[cfg(not(debug_assertions))]
|
att: &Attestation,
|
||||||
fn attestation_aggregation_insert_get_prune() {
|
state: &mut BeaconState<B>,
|
||||||
let spec = &ChainSpec::foundation();
|
spec: &ChainSpec,
|
||||||
let (ref mut state, ref keypairs) = attestation_test_state(spec, 1);
|
) {
|
||||||
let op_pool = OperationPool::new();
|
state.latest_crosslinks[att.data.shard as usize] = Crosslink {
|
||||||
|
crosslink_data_root: att.data.crosslink_data_root,
|
||||||
|
epoch: att.data.slot.epoch(spec.slots_per_epoch),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let slot = state.slot - 1;
|
#[test]
|
||||||
let committees = state
|
fn test_attestation_score() {
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
let (ref mut state, ref keypairs, ref spec) =
|
||||||
.unwrap()
|
attestation_test_state::<FoundationStateTypes>(1);
|
||||||
.clone();
|
|
||||||
|
|
||||||
assert_eq!(
|
let slot = state.slot - 1;
|
||||||
committees.len(),
|
let committees = state
|
||||||
1,
|
.get_crosslink_committees_at_slot(slot, spec)
|
||||||
"we expect just one committee with this many validators"
|
.unwrap()
|
||||||
);
|
.clone();
|
||||||
|
|
||||||
|
for committee in committees {
|
||||||
|
let att1 = signed_attestation(&committee, keypairs, ..2, slot, state, spec, None);
|
||||||
|
let att2 = signed_attestation(&committee, keypairs, .., slot, state, spec, None);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
att1.aggregation_bitfield.num_set_bits(),
|
||||||
|
attestation_score(&att1, state, spec)
|
||||||
|
);
|
||||||
|
|
||||||
|
state
|
||||||
|
.current_epoch_attestations
|
||||||
|
.push(PendingAttestation::from_attestation(&att1, state.slot));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
committee.committee.len() - 2,
|
||||||
|
attestation_score(&att2, state, spec)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// End-to-end test of basic attestation handling.
|
||||||
|
#[test]
|
||||||
|
fn attestation_aggregation_insert_get_prune() {
|
||||||
|
let (ref mut state, ref keypairs, ref spec) =
|
||||||
|
attestation_test_state::<FoundationStateTypes>(1);
|
||||||
|
|
||||||
|
let op_pool = OperationPool::new();
|
||||||
|
|
||||||
|
let slot = state.slot - 1;
|
||||||
|
let committees = state
|
||||||
|
.get_crosslink_committees_at_slot(slot, spec)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
committees.len(),
|
||||||
|
1,
|
||||||
|
"we expect just one committee with this many validators"
|
||||||
|
);
|
||||||
|
|
||||||
|
for committee in &committees {
|
||||||
|
let step_size = 2;
|
||||||
|
for i in (0..committee.committee.len()).step_by(step_size) {
|
||||||
|
let att = signed_attestation(
|
||||||
|
committee,
|
||||||
|
keypairs,
|
||||||
|
i..i + step_size,
|
||||||
|
slot,
|
||||||
|
state,
|
||||||
|
spec,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
fake_latest_crosslink(&att, state, spec);
|
||||||
|
op_pool.insert_attestation(att, state, spec).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(op_pool.attestations.read().len(), committees.len());
|
||||||
|
assert_eq!(op_pool.num_attestations(), committees.len());
|
||||||
|
|
||||||
|
// Before the min attestation inclusion delay, get_attestations shouldn't return anything.
|
||||||
|
assert_eq!(op_pool.get_attestations(state, spec).len(), 0);
|
||||||
|
|
||||||
|
// Then once the delay has elapsed, we should get a single aggregated attestation.
|
||||||
|
state.slot += spec.min_attestation_inclusion_delay;
|
||||||
|
|
||||||
|
let block_attestations = op_pool.get_attestations(state, spec);
|
||||||
|
assert_eq!(block_attestations.len(), committees.len());
|
||||||
|
|
||||||
|
let agg_att = &block_attestations[0];
|
||||||
|
assert_eq!(
|
||||||
|
agg_att.aggregation_bitfield.num_set_bits(),
|
||||||
|
spec.target_committee_size as usize
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prune attestations shouldn't do anything at this point.
|
||||||
|
op_pool.prune_attestations(state, spec);
|
||||||
|
assert_eq!(op_pool.num_attestations(), committees.len());
|
||||||
|
|
||||||
|
// But once we advance to an epoch after the attestation, it should prune it out of
|
||||||
|
// existence.
|
||||||
|
state.slot = slot + spec.slots_per_epoch;
|
||||||
|
op_pool.prune_attestations(state, spec);
|
||||||
|
assert_eq!(op_pool.num_attestations(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adding an attestation already in the pool should not increase the size of the pool.
|
||||||
|
#[test]
|
||||||
|
fn attestation_duplicate() {
|
||||||
|
let (ref mut state, ref keypairs, ref spec) =
|
||||||
|
attestation_test_state::<FoundationStateTypes>(1);
|
||||||
|
|
||||||
|
let op_pool = OperationPool::new();
|
||||||
|
|
||||||
|
let slot = state.slot - 1;
|
||||||
|
let committees = state
|
||||||
|
.get_crosslink_committees_at_slot(slot, spec)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
for committee in &committees {
|
||||||
|
let att = signed_attestation(committee, keypairs, .., slot, state, spec, None);
|
||||||
|
fake_latest_crosslink(&att, state, spec);
|
||||||
|
op_pool
|
||||||
|
.insert_attestation(att.clone(), state, spec)
|
||||||
|
.unwrap();
|
||||||
|
op_pool.insert_attestation(att, state, spec).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(op_pool.num_attestations(), committees.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adding lots of attestations that only intersect pairwise should lead to two aggregate
|
||||||
|
/// attestations.
|
||||||
|
#[test]
|
||||||
|
fn attestation_pairwise_overlapping() {
|
||||||
|
let (ref mut state, ref keypairs, ref spec) =
|
||||||
|
attestation_test_state::<FoundationStateTypes>(1);
|
||||||
|
|
||||||
|
let op_pool = OperationPool::new();
|
||||||
|
|
||||||
|
let slot = state.slot - 1;
|
||||||
|
let committees = state
|
||||||
|
.get_crosslink_committees_at_slot(slot, spec)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
for committee in &committees {
|
|
||||||
let step_size = 2;
|
let step_size = 2;
|
||||||
for i in (0..committee.committee.len()).step_by(step_size) {
|
for committee in &committees {
|
||||||
let att = signed_attestation(
|
// Create attestations that overlap on `step_size` validators, like:
|
||||||
committee,
|
// {0,1,2,3}, {2,3,4,5}, {4,5,6,7}, ...
|
||||||
keypairs,
|
for i in (0..committee.committee.len() - step_size).step_by(step_size) {
|
||||||
i..i + step_size,
|
let att = signed_attestation(
|
||||||
slot,
|
committee,
|
||||||
state,
|
keypairs,
|
||||||
spec,
|
i..i + 2 * step_size,
|
||||||
None,
|
slot,
|
||||||
);
|
state,
|
||||||
fake_latest_crosslink(&att, state, spec);
|
spec,
|
||||||
op_pool.insert_attestation(att, state, spec).unwrap();
|
None,
|
||||||
|
);
|
||||||
|
fake_latest_crosslink(&att, state, spec);
|
||||||
|
op_pool.insert_attestation(att, state, spec).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The attestations should get aggregated into two attestations that comprise all
|
||||||
|
// validators.
|
||||||
|
assert_eq!(op_pool.attestations.read().len(), committees.len());
|
||||||
|
assert_eq!(op_pool.num_attestations(), 2 * committees.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(op_pool.attestations.read().len(), committees.len());
|
/// Create a bunch of attestations signed by a small number of validators, and another
|
||||||
assert_eq!(op_pool.num_attestations(), committees.len());
|
/// bunch signed by a larger number, such that there are at least `max_attestations`
|
||||||
|
/// signed by the larger number. Then, check that `get_attestations` only returns the
|
||||||
|
/// high-quality attestations. To ensure that no aggregation occurs, ALL attestations
|
||||||
|
/// are also signed by the 0th member of the committee.
|
||||||
|
#[test]
|
||||||
|
fn attestation_get_max() {
|
||||||
|
let spec = &ChainSpec::foundation();
|
||||||
|
let small_step_size = 2;
|
||||||
|
let big_step_size = 4;
|
||||||
|
let (ref mut state, ref keypairs) = attestation_test_state(spec, big_step_size);
|
||||||
|
let op_pool = OperationPool::new();
|
||||||
|
|
||||||
// Before the min attestation inclusion delay, get_attestations shouldn't return anything.
|
let slot = state.slot - 1;
|
||||||
assert_eq!(op_pool.get_attestations(state, spec).len(), 0);
|
let committees = state
|
||||||
|
.get_crosslink_committees_at_slot(slot, spec)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
// Then once the delay has elapsed, we should get a single aggregated attestation.
|
let max_attestations = spec.max_attestations as usize;
|
||||||
state.slot += spec.min_attestation_inclusion_delay;
|
let target_committee_size = spec.target_committee_size as usize;
|
||||||
|
|
||||||
let block_attestations = op_pool.get_attestations(state, spec);
|
let mut insert_attestations = |committee, step_size| {
|
||||||
assert_eq!(block_attestations.len(), committees.len());
|
for i in (0..target_committee_size).step_by(step_size) {
|
||||||
|
let att = signed_attestation(
|
||||||
|
committee,
|
||||||
|
keypairs,
|
||||||
|
i..i + step_size,
|
||||||
|
slot,
|
||||||
|
state,
|
||||||
|
spec,
|
||||||
|
if i == 0 { None } else { Some(0) },
|
||||||
|
);
|
||||||
|
fake_latest_crosslink(&att, state, spec);
|
||||||
|
op_pool.insert_attestation(att, state, spec).unwrap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let agg_att = &block_attestations[0];
|
for committee in &committees {
|
||||||
assert_eq!(
|
assert_eq!(committee.committee.len(), target_committee_size);
|
||||||
agg_att.aggregation_bitfield.num_set_bits(),
|
// Attestations signed by only 2-3 validators
|
||||||
spec.target_committee_size as usize
|
insert_attestations(committee, small_step_size);
|
||||||
);
|
// Attestations signed by 4+ validators
|
||||||
|
insert_attestations(committee, big_step_size);
|
||||||
// Prune attestations shouldn't do anything at this point.
|
|
||||||
op_pool.prune_attestations(state, spec);
|
|
||||||
assert_eq!(op_pool.num_attestations(), committees.len());
|
|
||||||
|
|
||||||
// But once we advance to an epoch after the attestation, it should prune it out of
|
|
||||||
// existence.
|
|
||||||
state.slot = slot + spec.slots_per_epoch;
|
|
||||||
op_pool.prune_attestations(state, spec);
|
|
||||||
assert_eq!(op_pool.num_attestations(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adding an attestation already in the pool should not increase the size of the pool.
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
fn attestation_duplicate() {
|
|
||||||
let spec = &ChainSpec::foundation();
|
|
||||||
let (ref mut state, ref keypairs) = attestation_test_state(spec, 1);
|
|
||||||
let op_pool = OperationPool::new();
|
|
||||||
|
|
||||||
let slot = state.slot - 1;
|
|
||||||
let committees = state
|
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
for committee in &committees {
|
|
||||||
let att = signed_attestation(committee, keypairs, .., slot, state, spec, None);
|
|
||||||
fake_latest_crosslink(&att, state, spec);
|
|
||||||
op_pool
|
|
||||||
.insert_attestation(att.clone(), state, spec)
|
|
||||||
.unwrap();
|
|
||||||
op_pool.insert_attestation(att, state, spec).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(op_pool.num_attestations(), committees.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adding lots of attestations that only intersect pairwise should lead to two aggregate
|
|
||||||
/// attestations.
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
fn attestation_pairwise_overlapping() {
|
|
||||||
let spec = &ChainSpec::foundation();
|
|
||||||
let (ref mut state, ref keypairs) = attestation_test_state(spec, 1);
|
|
||||||
let op_pool = OperationPool::new();
|
|
||||||
|
|
||||||
let slot = state.slot - 1;
|
|
||||||
let committees = state
|
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let step_size = 2;
|
|
||||||
for committee in &committees {
|
|
||||||
// Create attestations that overlap on `step_size` validators, like:
|
|
||||||
// {0,1,2,3}, {2,3,4,5}, {4,5,6,7}, ...
|
|
||||||
for i in (0..committee.committee.len() - step_size).step_by(step_size) {
|
|
||||||
let att = signed_attestation(
|
|
||||||
committee,
|
|
||||||
keypairs,
|
|
||||||
i..i + 2 * step_size,
|
|
||||||
slot,
|
|
||||||
state,
|
|
||||||
spec,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
fake_latest_crosslink(&att, state, spec);
|
|
||||||
op_pool.insert_attestation(att, state, spec).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// The attestations should get aggregated into two attestations that comprise all
|
let num_small = target_committee_size / small_step_size;
|
||||||
// validators.
|
let num_big = target_committee_size / big_step_size;
|
||||||
assert_eq!(op_pool.attestations.read().len(), committees.len());
|
|
||||||
assert_eq!(op_pool.num_attestations(), 2 * committees.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a bunch of attestations signed by a small number of validators, and another
|
assert_eq!(op_pool.attestations.read().len(), committees.len());
|
||||||
/// bunch signed by a larger number, such that there are at least `max_attestations`
|
assert_eq!(
|
||||||
/// signed by the larger number. Then, check that `get_attestations` only returns the
|
op_pool.num_attestations(),
|
||||||
/// high-quality attestations. To ensure that no aggregation occurs, ALL attestations
|
(num_small + num_big) * committees.len()
|
||||||
/// are also signed by the 0th member of the committee.
|
);
|
||||||
#[test]
|
assert!(op_pool.num_attestations() > max_attestations);
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
fn attestation_get_max() {
|
|
||||||
let spec = &ChainSpec::foundation();
|
|
||||||
let small_step_size = 2;
|
|
||||||
let big_step_size = 4;
|
|
||||||
let (ref mut state, ref keypairs) = attestation_test_state(spec, big_step_size);
|
|
||||||
let op_pool = OperationPool::new();
|
|
||||||
|
|
||||||
let slot = state.slot - 1;
|
state.slot += spec.min_attestation_inclusion_delay;
|
||||||
let committees = state
|
let best_attestations = op_pool.get_attestations(state, spec);
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
assert_eq!(best_attestations.len(), max_attestations);
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let max_attestations = spec.max_attestations as usize;
|
// All the best attestations should be signed by at least `big_step_size` (4) validators.
|
||||||
let target_committee_size = spec.target_committee_size as usize;
|
for att in &best_attestations {
|
||||||
|
assert!(att.aggregation_bitfield.num_set_bits() >= big_step_size);
|
||||||
let mut insert_attestations = |committee, step_size| {
|
|
||||||
for i in (0..target_committee_size).step_by(step_size) {
|
|
||||||
let att = signed_attestation(
|
|
||||||
committee,
|
|
||||||
keypairs,
|
|
||||||
i..i + step_size,
|
|
||||||
slot,
|
|
||||||
state,
|
|
||||||
spec,
|
|
||||||
if i == 0 { None } else { Some(0) },
|
|
||||||
);
|
|
||||||
fake_latest_crosslink(&att, state, spec);
|
|
||||||
op_pool.insert_attestation(att, state, spec).unwrap();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
for committee in &committees {
|
|
||||||
assert_eq!(committee.committee.len(), target_committee_size);
|
|
||||||
// Attestations signed by only 2-3 validators
|
|
||||||
insert_attestations(committee, small_step_size);
|
|
||||||
// Attestations signed by 4+ validators
|
|
||||||
insert_attestations(committee, big_step_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_small = target_committee_size / small_step_size;
|
|
||||||
let num_big = target_committee_size / big_step_size;
|
|
||||||
|
|
||||||
assert_eq!(op_pool.attestations.read().len(), committees.len());
|
|
||||||
assert_eq!(
|
|
||||||
op_pool.num_attestations(),
|
|
||||||
(num_small + num_big) * committees.len()
|
|
||||||
);
|
|
||||||
assert!(op_pool.num_attestations() > max_attestations);
|
|
||||||
|
|
||||||
state.slot += spec.min_attestation_inclusion_delay;
|
|
||||||
let best_attestations = op_pool.get_attestations(state, spec);
|
|
||||||
assert_eq!(best_attestations.len(), max_attestations);
|
|
||||||
|
|
||||||
// All the best attestations should be signed by at least `big_step_size` (4) validators.
|
|
||||||
for att in &best_attestations {
|
|
||||||
assert!(att.aggregation_bitfield.num_set_bits() >= big_step_size);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ fn verify_deposit_merkle_proof<T: BeaconStateTypes>(
|
|||||||
let leaf = hash(&get_serialized_deposit_data(deposit));
|
let leaf = hash(&get_serialized_deposit_data(deposit));
|
||||||
verify_merkle_proof(
|
verify_merkle_proof(
|
||||||
Hash256::from_slice(&leaf),
|
Hash256::from_slice(&leaf),
|
||||||
&deposit.proof,
|
&deposit.proof[..],
|
||||||
spec.deposit_contract_tree_depth as usize,
|
spec.deposit_contract_tree_depth as usize,
|
||||||
deposit.index as usize,
|
deposit.index as usize,
|
||||||
state.latest_eth1_data.deposit_root,
|
state.latest_eth1_data.deposit_root,
|
||||||
|
@ -1,153 +0,0 @@
|
|||||||
#![cfg(not(debug_assertions))]
|
|
||||||
|
|
||||||
use serde_derive::Deserialize;
|
|
||||||
use serde_yaml;
|
|
||||||
use state_processing::{per_block_processing, per_slot_processing};
|
|
||||||
use std::{fs::File, io::prelude::*, path::PathBuf};
|
|
||||||
use types::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct ExpectedState {
|
|
||||||
pub slot: Option<Slot>,
|
|
||||||
pub genesis_time: Option<u64>,
|
|
||||||
pub fork: Option<Fork>,
|
|
||||||
pub validator_registry: Option<Vec<Validator>>,
|
|
||||||
pub validator_balances: Option<Vec<u64>>,
|
|
||||||
pub previous_epoch_attestations: Option<Vec<PendingAttestation>>,
|
|
||||||
pub current_epoch_attestations: Option<Vec<PendingAttestation>>,
|
|
||||||
pub historical_roots: Option<Vec<Hash256>>,
|
|
||||||
pub finalized_epoch: Option<Epoch>,
|
|
||||||
pub latest_block_roots: Option<TreeHashVector<Hash256>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExpectedState {
|
|
||||||
// Return a list of fields that differ, and a string representation of the beacon state's field.
|
|
||||||
fn check(&self, state: &BeaconState) -> Vec<(&str, String)> {
|
|
||||||
// Check field equality
|
|
||||||
macro_rules! cfe {
|
|
||||||
($field_name:ident) => {
|
|
||||||
if self.$field_name.as_ref().map_or(true, |$field_name| {
|
|
||||||
println!(" > Checking {}", stringify!($field_name));
|
|
||||||
$field_name == &state.$field_name
|
|
||||||
}) {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
vec![(stringify!($field_name), format!("{:#?}", state.$field_name))]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vec![
|
|
||||||
cfe!(slot),
|
|
||||||
cfe!(genesis_time),
|
|
||||||
cfe!(fork),
|
|
||||||
cfe!(validator_registry),
|
|
||||||
cfe!(validator_balances),
|
|
||||||
cfe!(previous_epoch_attestations),
|
|
||||||
cfe!(current_epoch_attestations),
|
|
||||||
cfe!(historical_roots),
|
|
||||||
cfe!(finalized_epoch),
|
|
||||||
cfe!(latest_block_roots),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|x| x)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct TestCase {
|
|
||||||
pub name: String,
|
|
||||||
pub config: ChainSpec,
|
|
||||||
pub verify_signatures: bool,
|
|
||||||
pub initial_state: BeaconState,
|
|
||||||
pub blocks: Vec<BeaconBlock>,
|
|
||||||
pub expected_state: ExpectedState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct TestDoc {
|
|
||||||
pub title: String,
|
|
||||||
pub summary: String,
|
|
||||||
pub fork: String,
|
|
||||||
pub test_cases: Vec<TestCase>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_test_case(test_name: &str) -> TestDoc {
|
|
||||||
let mut file = {
|
|
||||||
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
||||||
file_path_buf.push(format!("yaml_utils/specs/{}", test_name));
|
|
||||||
|
|
||||||
File::open(file_path_buf).unwrap()
|
|
||||||
};
|
|
||||||
let mut yaml_str = String::new();
|
|
||||||
file.read_to_string(&mut yaml_str).unwrap();
|
|
||||||
yaml_str = yaml_str.to_lowercase();
|
|
||||||
|
|
||||||
serde_yaml::from_str(&yaml_str.as_str()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_state_transition_test(test_name: &str) {
|
|
||||||
let doc = load_test_case(test_name);
|
|
||||||
|
|
||||||
// Run Tests
|
|
||||||
let mut ok = true;
|
|
||||||
for (i, test_case) in doc.test_cases.iter().enumerate() {
|
|
||||||
let fake_crypto = cfg!(feature = "fake_crypto");
|
|
||||||
if !test_case.verify_signatures == fake_crypto {
|
|
||||||
println!("Running {}", test_case.name);
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"Skipping {} (fake_crypto: {}, need fake: {})",
|
|
||||||
test_case.name, fake_crypto, !test_case.verify_signatures
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut state = test_case.initial_state.clone();
|
|
||||||
for (j, block) in test_case.blocks.iter().enumerate() {
|
|
||||||
while block.slot > state.slot {
|
|
||||||
per_slot_processing(&mut state, &test_case.config).unwrap();
|
|
||||||
}
|
|
||||||
let res = per_block_processing(&mut state, &block, &test_case.config);
|
|
||||||
if res.is_err() {
|
|
||||||
println!("Error in {} (#{}), on block {}", test_case.name, i, j);
|
|
||||||
println!("{:?}", res);
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mismatched_fields = test_case.expected_state.check(&state);
|
|
||||||
if !mismatched_fields.is_empty() {
|
|
||||||
println!(
|
|
||||||
"Error in expected state, these fields didn't match: {:?}",
|
|
||||||
mismatched_fields.iter().map(|(f, _)| f).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
for (field_name, state_val) in mismatched_fields {
|
|
||||||
println!("state.{} was: {}", field_name, state_val);
|
|
||||||
}
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(ok, "one or more tests failed, see above");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
fn test_read_yaml() {
|
|
||||||
load_test_case("sanity-check_small-config_32-vals.yaml");
|
|
||||||
load_test_case("sanity-check_default-config_100-vals.yaml");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
fn run_state_transition_tests_small() {
|
|
||||||
run_state_transition_test("sanity-check_small-config_32-vals.yaml");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run with --ignored to run this test
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn run_state_transition_tests_large() {
|
|
||||||
run_state_transition_test("sanity-check_default-config_100-vals.yaml");
|
|
||||||
}
|
|
@ -1,8 +1,11 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use fixed_len_vec::typenum::{Unsigned, U1024, U8, U8192};
|
use fixed_len_vec::typenum::{Unsigned, U1024, U8, U8192};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub trait BeaconStateTypes: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
pub trait BeaconStateTypes:
|
||||||
|
'static + Default + Sync + Send + Clone + Debug + PartialEq + serde::de::DeserializeOwned
|
||||||
|
{
|
||||||
type ShardCount: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
type ShardCount: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||||
type SlotsPerHistoricalRoot: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
type SlotsPerHistoricalRoot: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||||
type LatestRandaoMixesLength: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
type LatestRandaoMixesLength: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||||
@ -13,7 +16,7 @@ pub trait BeaconStateTypes: 'static + Default + Sync + Send + Clone + Debug + Pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ethereum Foundation specifications.
|
/// Ethereum Foundation specifications.
|
||||||
#[derive(Clone, PartialEq, Debug, Default)]
|
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct FoundationStateTypes;
|
pub struct FoundationStateTypes;
|
||||||
|
|
||||||
impl BeaconStateTypes for FoundationStateTypes {
|
impl BeaconStateTypes for FoundationStateTypes {
|
||||||
@ -30,7 +33,7 @@ impl BeaconStateTypes for FoundationStateTypes {
|
|||||||
|
|
||||||
pub type FoundationBeaconState = BeaconState<FoundationStateTypes>;
|
pub type FoundationBeaconState = BeaconState<FoundationStateTypes>;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Default)]
|
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct FewValidatorsStateTypes;
|
pub struct FewValidatorsStateTypes;
|
||||||
|
|
||||||
impl BeaconStateTypes for FewValidatorsStateTypes {
|
impl BeaconStateTypes for FewValidatorsStateTypes {
|
||||||
@ -47,7 +50,7 @@ impl BeaconStateTypes for FewValidatorsStateTypes {
|
|||||||
|
|
||||||
pub type FewValidatorsBeaconState = BeaconState<FewValidatorsStateTypes>;
|
pub type FewValidatorsBeaconState = BeaconState<FewValidatorsStateTypes>;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Default)]
|
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct LighthouseTestnetStateTypes;
|
pub struct LighthouseTestnetStateTypes;
|
||||||
|
|
||||||
impl BeaconStateTypes for LighthouseTestnetStateTypes {
|
impl BeaconStateTypes for LighthouseTestnetStateTypes {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use super::{DepositData, Hash256, TreeHashVector};
|
|
||||||
use crate::test_utils::TestRandom;
|
use crate::test_utils::TestRandom;
|
||||||
|
use crate::*;
|
||||||
|
use fixed_len_vec::typenum::U32;
|
||||||
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
@ -22,7 +23,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
|||||||
TestRandom,
|
TestRandom,
|
||||||
)]
|
)]
|
||||||
pub struct Deposit {
|
pub struct Deposit {
|
||||||
pub proof: TreeHashVector<Hash256>,
|
pub proof: FixedLenVec<Hash256, U32>,
|
||||||
pub index: u64,
|
pub index: u64,
|
||||||
pub deposit_data: DepositData,
|
pub deposit_data: DepositData,
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ pub mod pending_attestation;
|
|||||||
pub mod proposer_slashing;
|
pub mod proposer_slashing;
|
||||||
pub mod slashable_attestation;
|
pub mod slashable_attestation;
|
||||||
pub mod transfer;
|
pub mod transfer;
|
||||||
pub mod tree_hash_vector;
|
// pub mod tree_hash_vector;
|
||||||
pub mod voluntary_exit;
|
pub mod voluntary_exit;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod slot_epoch_macros;
|
pub mod slot_epoch_macros;
|
||||||
@ -66,7 +66,7 @@ pub use crate::slashable_attestation::SlashableAttestation;
|
|||||||
pub use crate::slot_epoch::{Epoch, Slot};
|
pub use crate::slot_epoch::{Epoch, Slot};
|
||||||
pub use crate::slot_height::SlotHeight;
|
pub use crate::slot_height::SlotHeight;
|
||||||
pub use crate::transfer::Transfer;
|
pub use crate::transfer::Transfer;
|
||||||
pub use crate::tree_hash_vector::TreeHashVector;
|
// pub use crate::tree_hash_vector::TreeHashVector;
|
||||||
pub use crate::validator::Validator;
|
pub use crate::validator::Validator;
|
||||||
pub use crate::voluntary_exit::VoluntaryExit;
|
pub use crate::voluntary_exit::VoluntaryExit;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user