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

executable light client patch: beacon-chain.md #2141

Merged
merged 27 commits into from
Dec 15, 2020
Merged

Conversation

hwwhww
Copy link
Contributor

@hwwhww hwwhww commented Dec 1, 2020

Patch for #2130

This PR

  • Bump remerkleable to 0.1.18 ae86517 Thanks @protolambda!
  • Use new optional_fast_aggregate_verify f0864b8 /cc @JustinDrake
  • Add LIGHTCLIENT fork to test/context.py
  • Make it pass the most phase0 tests except for the inactivity leak tests (see below)

TODO in this PR:

  • Rename LIGHTCLIENT fork to another code name that is more like a "fork". Does LIGHTCLIENT_PATCH work? /cc @djrtwo

TODOs in other PRs:

  • Fix inactivity leak issues (see Added standalone light client patch #2130 (comment) discussions)
    • failed tests
    FAILED eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py::test_almost_empty_attestations_with_leak
    FAILED eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py::test_random_fill_attestations_with_leak
    FAILED eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py::test_almost_full_attestations_with_leak
    FAILED eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py::test_full_attestation_participation_with_leak
    
  • This PR disabled sync-protocol.md for now. Let's make it executable with other PRs.
  • Add light client patch tests

specs/phase0/validator.md Outdated Show resolved Hide resolved
@hwwhww hwwhww changed the title executable light client patch executable light client patch: beacon-chain.md Dec 3, 2020
@hwwhww
Copy link
Contributor Author

hwwhww commented Dec 7, 2020

Changelog

  • As discussed during the call and with @djrtwo in chat, do not cancel the sync committee rewards when dealing inactivity leak.
  • Rebased.

Now trying to fix the new error after rebasing:

eth2spec/test/phase0/sanity/test_blocks.py:319: in test_empty_epoch_transition
    signed_block = state_transition_and_sign_block(spec, state, block)
eth2spec/test/helpers/state.py:83: in state_transition_and_sign_block
    transition_unsigned_block(spec, state, block)
eth2spec/test/helpers/block.py:62: in transition_unsigned_block
    spec.process_block(state, block)
eth2spec/lightclient_patch/spec.py:1148: in process_block
    process_sync_committee(state, block)
eth2spec/lightclient_patch/spec.py:1825: in process_sync_committee
    committee_pubkeys = state.current_sync_committee.pubkeys
../../../venv/lib/python3.8/site-packages/remerkleable/complex.py:820: in __getattr__
    return super().get(i)
../../../venv/lib/python3.8/site-packages/remerkleable/subtree.py:29: in get
    self.get_backing().getter(to_gindex(i, self.tree_depth())), lambda v: self.set(i, v))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 0x0000000000000000000000000000000000000000000000000000000000000000
target = 2

    def getter(self, target: Gindex) -> Node:
        if target != 1:
>           raise NavigationError
E           remerkleable.tree.NavigationError

../../../venv/lib/python3.8/site-packages/remerkleable/tree.py:292: NavigationError

I still could not reproduce a similar error case in writing remerkleable tests. Investigating if something is wrong on the spec side. 👀

/cc @protolambda do you have any hints of why this error happened? I tried to comment out raise NavigationError, then some tests passed (haven't run all the tests locally with this version yet).

@@ -165,30 +186,59 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
```python
def process_sync_committee(state: BeaconState, block: BeaconBlock) -> None:
# Verify sync committee aggregate signature signing over the previous slot block root
previous_slot = Slot(max(state.slot, 1) - 1)
previous_slot = Slot(max(int(state.slot), 1) - 1)
Copy link
Contributor Author

@hwwhww hwwhww Dec 7, 2020

Choose a reason for hiding this comment

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

@djrtwo

This line was previous_slot = max(state.slot, Slot(1)) - Slot(1) earlier.

But since max needs to compare the same type, either we cast to Slot or cast to int.

@protolambda
Copy link
Collaborator

protolambda commented Dec 7, 2020

@hwwhww it looks like it was accessing a field in a container, recognized the wrong count of container fields (probably related to new extended types), created a generalized-index that wasn't deep enough (e.g. 9 fields instead of 8 = subtree twice as deep) and then failed to retrieve the node at the generalized index that was too shallow. Interesting! I'll run through this and see if it's a usage problem or a new bug due to OOP changes in remerkleable. Edit: that could have been it, but it wasn't: the tree was not far enough expanded in width to cover all new fields, since it was a phase 0 state.

Comment on lines 198 to +209
# Reward sync committee participants
participant_rewards = Gwei(0)
proposer_reward = Gwei(0)
active_validator_count = uint64(len(get_active_validator_indices(state, get_current_epoch(state))))
for participant_index in participant_indices:
base_reward = get_base_reward(state, participant_index)
reward = Gwei(base_reward * active_validator_count // len(committee_indices) // SLOTS_PER_EPOCH)
max_participant_reward = base_reward - base_reward // PROPOSER_REWARD_QUOTIENT
reward = Gwei(max_participant_reward * active_validator_count // len(committee_indices) // SLOTS_PER_EPOCH)
increase_balance(state, participant_index, reward)
participant_rewards += reward
proposer_reward += base_reward // PROPOSER_REWARD_QUOTIENT

# Reward beacon proposer
increase_balance(state, get_beacon_proposer_index(state), Gwei(participant_rewards // PROPOSER_REWARD_QUOTIENT))
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@protolambda
Copy link
Collaborator

Looked deeper into that navigation error: the state is backed by a tree that does not contain any nodes for current_sync_committee or next_sync_committee. For the first one, it happens to maintain a padding node to make it an even number (but the padding node has no child nodes, so you got a navigation error on access). The 2nd field doesn't have a node at all and will straight away result in a navigation error.

TLDR: need to look at state initialization now, see if it properly builds a tree suitable for use in the lighthclient-patch.

@protolambda
Copy link
Collaborator

Update: in the context.py (literally creates context for tests) it creates the test state. As part of testing, it can upgrade states to later phases. It is not doing this for the light-client-patch, and just plugs the old incomplete state tree into the expected beacon-state-type.
This typing step is an optimization: whenever it can, test preparation re-uses the same immutable state data (based on fork name, fork version, and initialization arguments).
I'll see if I can make it do that upgrade step to get a proper state.

@protolambda
Copy link
Collaborator

protolambda commented Dec 7, 2020

Attempt to fix it: the test state preparation now runs a new function upgrade_to_lightclient_patch, which essentially takes a phase0 state, and outputs a lightclient-patch state. Similar to how a phase1 state is built.

The upgrade_to_lightclient_patch is not pretty, since remerkleable does not support dict-like item retrieval yet. It could have been BeaconState(**pre.items()), or I can try make FooBar(Foo(example=123), more=42) -> FooBar {example: 123, more: 42} a thing for upgrades. The current BeaconState.fields() returns a dict of keys and field types, not values. Style suggestions welcome, we have a working placeholder now, so we have time to discuss.
Or we are more explicit about state upgrades (which IMHO is not a bad thing), and write it all out like with phase 1.

Edit: tests run mostly now, but there are some tests that fail for regular reasons, very close to executable spec though, nice work!

@hwwhww
Copy link
Contributor Author

hwwhww commented Dec 8, 2020

@protolambda

Thanks a lot for the help!!! That makes perfect sense.

I'm okay with BeaconState(**{k: getattr(pre, k) for k in pre.fields().keys()}) style, but I realized that we need to update the state.fork and state.latest_block_header explicitly so I added lightclient-fork.md as phase 1.

Still have some known issues that our tests didn't find, but finally see the green lights for all CI jobs! 🍏

epoch=epoch,
),
# History
latest_block_header=pre.latest_block_header,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@djrtwo @vbuterin

Since sync_committee_bits and sync_committee_signature fields are added to block header instead of block body in 3b7c025, it would be more tricky to handle the SSZ objects at the fork boundary.

  1. The latest_block_header field here should be transformed from phase0.BeaconBlockBody to lightclient_patch.BeaconBlockBody.
  2. However, the next slot (the first slot after the light client patch) will have a problem when caching the previous_block_root = hash_tree_root(state.latest_block_header) in process_slot function because the previous root should have been computed with phase 0 form at phase 0.

I think it makes more sense to have sync_committee_bits and sync_committee_signature in BeaconBlockBody although they are not "operations".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 2c86b43


- [Introduction](#introduction)
- [Configuration](#configuration)
- [Fork to Light-client patch](#fork-to-light-client-patch)
Copy link
Collaborator

Choose a reason for hiding this comment

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

We write "light client" in three different ways 😂

  1. light client
  2. lightclient
  3. light-client

I guess we want to be consistent. (Small preference for "light client" on my side.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 on "light client".

Once we have a better hard fork name, we can replace the folder name lightclient with it.
We can scan it again after merging this PR to avoid conflicts.

1) Add a summary note for every function that is changed.
2) Avoid changing `process_block` (instead only change `process_block_header`).
3) Rename `G2_INFINITY_POINT_SIG` to `G2_POINT_AT_INFINITY` to avoid `SIG` contraction.
4) Misc cleanups
Co-authored-by: Marin Petrunić <[email protected]>
@hwwhww
Copy link
Contributor Author

hwwhww commented Dec 15, 2020

@vbuterin @djrtwo @JustinDrake @protolambda

upgrade_to_lightclient_patch may still have some typing issues but it's not substantive. We can fix it later.

This PR is ready to be merged into upstream #2130.

@hwwhww hwwhww merged commit acfe49e into vbuterin-patch-2 Dec 15, 2020
@hwwhww hwwhww deleted the lightclient-exe branch February 21, 2023 06:14
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.

5 participants