Skip to content
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

Merge test vectors: enable phase0 tests for Merge + start on new testing #2380

Merged
merged 17 commits into from
May 10, 2021

Conversation

protolambda
Copy link
Contributor

@protolambda protolambda commented May 4, 2021

Changes:

  • Add merge to forks list with enabled testing and test-vector outputs
  • Update test generators to include merge:
    • SSZ static tests now respect TESTGEN_FORKS again
    • epoch processing (inherits phase0 tests)
    • finality (inherits phase0 tests)
    • operations (inherits phase0 tests, added execution payload testing)
    • rewards (inherits phase0 tests)
    • sanity (inherits phase0 tests, added block tests)
  • Helper method to create empty execution payload on top of the latest state
  • Update beacon-block construction to add in a valid empty execution payload when the merge spec is detected.
    • TODO: may need to be a zeroed execution payload for the time during the merge transition. For next PR.
  • Refactor merge spec (no breaking consensus changes):
    • Isolate the execution payload input for execution payload processing. There is no good reason to include the full block body. The old version makes forking the Merge code with code-reuse much more difficult.
    • The if statement for processing simply moves to outside the processing function. So the processing is conditional, instead of a possible no-op.
  • Implement execution-payload tests
    • Scaffold edge cases to test
      • Note: left 1 minimal test case. Further testing in next PR.
    • Define test format
  • Implement block tests
    • Just a minimal scaffold, needs more work in later PR

TLDR: Port phase0 testing for Merge, enable Merge test generators.

Comment on lines 172 to 173
if is_transition_completed(state) or is_transition_block(state, block):
process_execution_payload(state, block.body.execution_payload) # [New in Merge]
Copy link
Contributor

@hwwhww hwwhww May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues:

  1. Looking at tests/formats/operations/README.md and I just realized that we put sync_aggregate as part of "operation" tests but process_sync_committee is not part of the process_operations function like execution_payload.
    1. IMO it's good to consider sync_aggregate as an operation.
    2. If we want to simplify it, I think execution_payload could also be considered as an operation since it's essentially "transactions".
  2. I can see why it looks good to extract the is_transition_completed(state) or is_transition_block(state, block) condition checks from process_execution_payload, but it makes it a bit unclear where/how to test these conditions in test cases.

What do you think about:

  1. Move process_sync_committee and process_execution_payload calls into process_operations.
  2. Revert the if is_transition_completed(state) or is_transition_block(state, block) checks extraction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Regarding process_sync_committee: sure, but let's keep that separate from merge-testing for now. Moving the process_execution_payload to process_operations would be good too. I'm honestly surprised we didn't do the same for the Randao and Eth1Data functionality.
  2. The if-statement was purposefully extracted from the process_execution_payload function: this way the function input can just be an ExecutionPayload, and not a complete beacon block body. This helps with future compatibility and reduces test complexity (We don't have to set up a complete beacon block for every single test case). We can test that one if-statement as part of the regular full beacon block testing. And in future phases after the merge, we may even remove the if-statement entirely, since the merge-transition check will always be true.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For (2), it might make sense to put it behind a function is_execution_enabled(state, block). This is semantically cleaner for any chain/setting that doesn't have the transition. They can just override the function to return True and as you mentioned, in the future we can just remove it.

@protolambda protolambda changed the title Merge test vectors (work in progress) Merge test vectors: enable phase0 tests for Merge + start on new testing May 5, 2021
@protolambda protolambda marked this pull request as ready for review May 5, 2021 15:08
@protolambda protolambda requested a review from ralexstokes May 5, 2021 19:35
Copy link
Contributor

@djrtwo djrtwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

I think there's a bug in build_empty_block_for_next_slot and I'm not certain if we are actually hitting process_execution_payload. Didn't do any testing. Check out my comments. Hit me up. happy to discuss in DM too.

Comment on lines 172 to 173
if is_transition_completed(state) or is_transition_block(state, block):
process_execution_payload(state, block.body.execution_payload) # [New in Merge]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For (2), it might make sense to put it behind a function is_execution_enabled(state, block). This is semantically cleaner for any chain/setting that doesn't have the transition. They can just override the function to return True and as you mentioned, in the future we can just remove it.

tests/core/pyspec/eth2spec/test/context.py Show resolved Hide resolved
tests/core/pyspec/eth2spec/test/context.py Outdated Show resolved Hide resolved
block_hash=spec.Hash32(),
parent_hash=latest.block_hash,
coinbase=spec.Bytes20(),
state_root=latest.state_root, # no changes to the state
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this certain to be the case?

Does the consensus not write anything to state automatically? The recent block_roots being included in state is something that comes to mind, like EIP 210. I know 210 isn't deployed but I'm unsure if there isn't anything else like that in place

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be something yes, although I'm not entirely sure either. The account tree definitely does not change (no block rewards on eth1 state anymore), and the storage/values/code/nonce of accounts only changes with transactions. Can't find anything in the yellow paper that would otherwise touch the eth1 world state.

I'll open an issue to investigate this more. Maybe this new possibility of a duplicate state-root in eth1 blocks is untested/dangerous?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be. at least it might not be expected

I think we've touched on this a tiny bit. Peter mentioned that it isn't the case today because of issuance/coinbase but that it might change in this context. I think they can handle it but it's something to highlight as a design consideration/edge-case for execution engines

transactions=empty_txs,
)
# TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however.
payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The htr would not be used in the correct functionality, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, it's just a short-cut to make a hash that is unique to the payload. Encoding and flat-hashing would work to. I think we may just want to do the real RLP thing though. The current thing works for now, but it's nice if it's correct, especially if we get eth1-eth2 combination clients that run these tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to decouple this shortcut and use it as a helper.

@spec_state_test
def test_success_first_payload(spec, state):
next_slot(spec, state)
assert not spec.is_transition_completed(state)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe assert that it is a transition block too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no block processing here, just the execution payload, so as-is it cannot be asserted.

Maybe we should start using the new run_block_processing_to helper to simulate the block behavior up to the execution-payload processing, I can discuss this with Mikhail for the next PR, but I prefer to keep this PR simple.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test case supposed to test the transition?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process_execution_payload has been refactored to not care about the transition logic. So yes, it tests the empty header to a real header but it doesn't test that the transition go/no-go logic works

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nit: then we likely should not use transition spec helpers to check the pre-state.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, but we want to test a range of tests/pre-states/inputs directly on this function. One of which when state has an empty header which maps to not spec.is_transition_completed. Even though process_execution_payload does not check this, we do want to ensure we have a test cast that is in this pre-condition.

If we don't use that helper, we'd want to check state.latest_execution_header == ExecutionHeader(). Or directly just set it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see. This is more likely to be done in a separate PR anyway. Though, I would rather set test case preliminaries in the test case itself than expect them from the outside.

Copy link
Contributor

@djrtwo djrtwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great start! happy to get this merged pretty soon.

Copy link
Contributor

@mkalinin mkalinin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work so far! Let's merge and keep work on execution payload tests in #2385

@djrtwo djrtwo merged commit 2539d4e into dev May 10, 2021
@djrtwo djrtwo deleted the merge-test-vectors branch May 10, 2021 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants