-
Notifications
You must be signed in to change notification settings - Fork 808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - Alternative (to BeaconChainHarness) BeaconChain testing API #1380
Conversation
5144364
to
6bcfff3
Compare
6bcfff3
to
c5c4c45
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, and definitely like an improvement, but I'm a little hesitant about maintaining two very similar harnesses in-tree. Do you think it would be feasible to rewrite the existing harness tests in terms of the yoke? Maybe even with compatibility shims that defer to the yoke's functions. We don't have to do it all at once, and I'd be happy to help with the effort.
Having just one harness would solve the naming issue too 😅 I mistook yoke for yolk when I first saw it and had to look up the definition... I guess we could also choose another name? Driver? Runner?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arr, I wrote up a response to this but it's not here. I must have forgot to submit :(
Agree with @michaelsproul about maintaining two harnesses and also the "yoke" name (perhaps I'm not just familiar with the word). Suggestion: BeaconChainTestRig
(not in love with it.. this thing is hard name lol).
I think it would be ideal to have "one beacon chain harness to rule them all", but whether or not we do that in this PR is the question at hand, I assume. I'm happy to see this merged with two harnesses, with the intention of eventually have one.
I noticed that this new harness rewinds the slot clock. I think we might eventually run into issues if we wind back too much, especially around the "seen" caches (that check for already-seen blocks, attestations). If we come across these I think we should fix them, since we should ideally support an arbitrary slot clock.
Approved pending Michael's comments are addressed :)
85c0fd1
to
c60d3ba
Compare
9b0911b
to
d9e63db
Compare
d9e63db
to
c29c4da
Compare
96a9cf8
to
29ee2d1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry it's taken me so long to re-review this. I still can't sign off on it with two almost identical harnesses present, as this is adding technical debt rather than alleviating it. I'd like to see:
- One harness type in
test_utils.rs
, it may as well be calledBeaconChainHarness
- The ability to run with and without a disk DB (two harness types)
- All tests currently in
beacon_chain/tests
passing, and straight-forward to verify their meaning hasn't changed (refactoring all the tests is not desirable, as it would be very time consuming to do and review)
I think the best way to achieve this would be to fuse the new parts of your testing rig into the BeaconChainHarness
, while preferring the original function names (to avoid too much more refactoring). AFAICT the main new things are:
add_block_at_slot
does the trick with the graffiti and the slot clock- There are some new useful utility methods like
get_finalized_checkpoints
andcold_state_exists
- The harness holds an RNG and some validator counts (straight-forward)
I realise that's a lot of work, in which case we could expedite the block pruning fix and the get_state
change in a separate smaller PR.
Extracted from #1380 because merging #1380 proves to be contentious. Co-authored-by: Michael Sproul <[email protected]>
e98e42d
to
da3ae3e
Compare
ad83aeb
to
465ad9d
Compare
7536e88
to
d94aacf
Compare
## Issue Addressed Closes #1488 ## Proposed Changes * Prevent the pruning algorithm from over-eagerly deleting states at skipped slots when they are shared with the canonical chain. * Add `debug` logging to the pruning algorithm so we have so better chance of debugging future issues from logs. * Modify the handling of the "finalized state" in the beacon chain, so that it's always the state at the first slot of the finalized epoch (previously it was the state at the finalized block). This gives database pruning a clearer and cleaner view of things, and will marginally impact the pruning of the op pool, observed proposers, etc (in ways that are safe as far as I can tell). * Remove duplicated `RevertedFinalizedEpoch` check from `after_finalization` * Delete useless and unused `max_finality_distance` * Add tests that exercise pruning with shared states at skip slots * Delete unnecessary `block_strategy` argument from `add_blocks` and friends in the test harness (will likely conflict with #1380 slightly, sorry @adaszko -- but we can fix that) * Bonus: add a `BeaconChain::with_head` method. I didn't end up needing it, but it turned out quite nice, so I figured we could keep it? ## Additional Info Any users who have experienced pruning errors on Medalla will need to resync after upgrading to a release including this change. This should end unbounded `chain_db` growth! 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is so close. I've merged master back in to it and fixed up the pruning tests that were added as part of #1564
loop { | ||
let epoch = slot.epoch(E::slots_per_epoch()); | ||
let epoch_start_slot: u64 = epoch.start_slot(E::slots_per_epoch()).into(); | ||
let epoch_end_slot: u64 = epoch.end_slot(E::slots_per_epoch()).into(); | ||
let this_epoch_slots: HashSet<Slot> = | ||
(epoch_start_slot..epoch_end_slot).map(Into::into).collect(); | ||
|
||
let this_epoch_fork1_slots: Vec<Slot> = fork1_slots | ||
.iter() | ||
.filter(|slot| this_epoch_slots.contains(slot)) | ||
.copied() | ||
.collect(); | ||
let this_epoch_fork2_slots: Vec<Slot> = fork2_slots | ||
.iter() | ||
.filter(|slot| this_epoch_slots.contains(slot)) | ||
.copied() | ||
.collect(); | ||
|
||
if this_epoch_fork1_slots.is_empty() && this_epoch_fork2_slots.is_empty() { | ||
break; | ||
} | ||
|
||
let (_, _, honest_head_, fork1_state_) = harness.add_attested_blocks_at_slots( | ||
fork1_state, | ||
&this_epoch_fork1_slots, | ||
&honest_validators, | ||
); | ||
honest_head = honest_head_.into(); | ||
fork1_state = fork1_state_; | ||
|
||
let (_, _, faulty_head_, fork2_state_) = harness.add_attested_blocks_at_slots( | ||
fork2_state, | ||
&this_epoch_fork2_slots, | ||
&faulty_validators, | ||
); | ||
faulty_head = faulty_head_.into(); | ||
fork2_state = fork2_state_; | ||
|
||
slot = epoch.end_slot(E::slots_per_epoch()) + 1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a go at abstracting this pattern (or something similar to it) into add_blocks_on_multiple_chains
, which you can see an example of in the updated pruning_test
. Would you mind refactoring this block in terms of the new primitive, and then we can merge?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of abstracting it that way too. Just wanted to have something working first, then clean up. Notice how much easier it is to abstract things with the new harness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did appreciate that! I was a bit dubious of whether it would work, but it worked almost as soon as I ran it :O
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's been a hard slog, thanks for persevering and taking my suggestions on board. I think we've ended up with a really good test harness that will be useful for many future tests! Definitely a good legacy
Without further ado...
bors r+
🤯 🎉 ✨
The PR: * Adds the ability to generate a crucial test scenario that isn't possible with `BeaconChainHarness` (i.e. two blocks occupying the same slot; previously forks necessitated skipping slots):  * New testing API: Instead of repeatedly calling add_block(), you generate a sorted `Vec<Slot>` and leave it up to the framework to generate blocks at those slots. * Jumping backwards to an earlier epoch is a hard error, so that tests necessarily generate blocks in a epoch-by-epoch manner. * Configures the test logger so that output is printed on the console in case a test fails. The logger also plays well with `--nocapture`, contrary to the existing testing framework * Rewrites existing fork pruning tests to use the new API * Adds a tests that triggers finalization at a non epoch boundary slot * Renamed `BeaconChainYoke` to `BeaconChainTestingRig` because the former has been too confusing * Fixed multiple tests (e.g. `block_production_different_shuffling_long`, `delete_blocks_and_states`, `shuffling_compatible_simple_fork`) that relied on a weird (and accidental) feature of the old `BeaconChainHarness` that attestations aren't produced for epochs earlier than the current one, thus masking potential bugs in test cases. Co-authored-by: Michael Sproul <[email protected]>
Pull request successfully merged into master. Build succeeded: |
The PR:
BeaconChainHarness
(i.e. two blocks occupying the same slot; previously forks necessitated skipping slots):Vec<Slot>
and leave it up to the framework to generate blocks at those slots.--nocapture
, contrary to the existing testing frameworkBeaconChainYoke
toBeaconChainTestingRig
because the former has been too confusingblock_production_different_shuffling_long
,delete_blocks_and_states
,shuffling_compatible_simple_fork
) that relied on a weird (and accidental) feature of the oldBeaconChainHarness
that attestations aren't produced for epochs earlier than the current one, thus masking potential bugs in test cases.