From 79cdb88e66b0ec1c0a8e403391cd07428d5e99ee Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 16 Mar 2022 15:41:37 -0600 Subject: [PATCH] wip 00 to 01 cred change --- specs/capella/beacon-chain.md | 98 ++++++++++++++++++- .../test_process_bls_to_execution_change.py | 47 +++++++++ 2 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index e912f608e..bcc35381f 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -20,14 +20,20 @@ to validator withdrawals. Including: ## Custom types -## Constants - We define the following Python custom types for type hinting and readability: | Name | SSZ equivalent | Description | | - | - | - | | `WithdrawalIndex` | `uint64` | an index of a `Withdrawal`| +## Constants + +### Domain types + +| Name | Value | +| - | - | +| `DOMAIN_BLS_TO_EXECUTION_CHANGE` | `DomainType('0x0A000000')` | + ## Preset ### State list lengths @@ -36,6 +42,12 @@ We define the following Python custom types for type hinting and readability: | - | - | :-: | :-: | | `WITHDRAWALS_QUEUE_LIMIT` | `uint64(2**40)` (= 1,099,511,627,776) | withdrawals enqueued in state| +### Max operations per block + +| Name | Value | +| - | - | +| `MAX_BLS_TO_EXECUTION_CHANGES` | `2**4` (= 16) | + ### Execution | Name | Value | Description | @@ -64,6 +76,25 @@ class Validator(Container): withdrawn_epoch: Epoch # [New in Capella] ``` +#### `BeaconBlockBody` + +```python +class BeaconBlockBody(Container): + randao_reveal: BLSSignature + eth1_data: Eth1Data # Eth1 data vote + graffiti: Bytes32 # Arbitrary data + # Operations + proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] + attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS] + attestations: List[Attestation, MAX_ATTESTATIONS] + deposits: List[Deposit, MAX_DEPOSITS] + voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] + bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] + sync_aggregate: SyncAggregate + # Execution + execution_payload: ExecutionPayload +``` + #### `BeaconState` ```python @@ -166,6 +197,23 @@ class Withdrawal(Container): amount: Gwei ``` +#### `BLSToExecutionChange` + +```python +class BLSToExecutionChange(Container): + validator_index: ValidatorIndex + from_bls_pubkey: BLSPubkey + to_execution_address: ExecutionAddress +``` + +#### `SignedBLSToExecutionChange` + +```python +class SignedBLSToExecutionChange(Container): + message: BLSToExecutionChange + signature: BLSSignature +``` + ## Helpers ### Beacon state mutators @@ -297,3 +345,49 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe withdrawals_root=hash_tree_root(payload.withdrawals), ) ``` + +#### Modified `process_operations` + +*Note*: The function `process_operations` is modified to process `BLSToExecutionChange` operations included in the block. + +```python +def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: + # Verify that outstanding deposits are processed up to the maximum number of deposits + assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(body.proposer_slashings, process_proposer_slashing) + for_ops(body.attester_slashings, process_attester_slashing) + for_ops(body.attestations, process_attestation) + for_ops(body.deposits, process_deposit) + for_ops(body.voluntary_exits, process_voluntary_exit) + for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) +``` + +#### New `process_bls_to_execution_change` + +```python +def process_bls_to_execution_change(state: BeaconState, + signed_address_change: SignedBLSToExecutionChange) -> None: + address_change = signed_address_change.message + + assert address_change.validator_index < len(state.validators) + + validator = state.validators[address_change.validator_index] + + assert validator.withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX + assert validator.withdrawal_credentials[1:] == hash(address_change.from_bls_pubkey)[1:] + + domain = get_domain(state, DOMAIN_BLS_TO_EXECUTION_CHANGE) + signing_root = compute_signing_root(address_change, domain) + assert bls.Verify(address_change.from_bls_pubkey, signing_root, signed_address_change.signature) + + validator.withdrawal_credentials = ( + ETH1_ADDRESS_WITHDRAWAL_PREFIX + + 0x00 * 11 + + address_change.to_execution_address + ) +``` diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py new file mode 100644 index 000000000..1a7fbfa81 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py @@ -0,0 +1,47 @@ +from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, +) + +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later + +def run_bls_to_execution_change_processing(spec, state, address_change, valid=True): + """ + Run ``process_bls_to_execution_change``, yielding: + - pre-state ('pre') + - address-change ('address_change') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + # yield pre-state + yield 'pre', state + + yield 'address_change', address_change + + # If the address_change is invalid, processing is aborted, and there is no post-state. + if not valid: + expect_assertion_error(lambda: spec.process_bls_to_execution_change(state, attestation)) + yield 'post', None + return + + # process address change + spec.process_bls_to_execution_change(state, attestation) + + # Make sure the address change has been processed + assert state.withdrawal_credentials[:1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX + assert state.withdrawal_credentials[1:12] == b'\x00' * 11 + assert state.withdrawal_credentials[12:] == address_change.to_execution_address + + # yield post-state + yield 'post', state + + +@with_capella_and_later +@spec_state_test +def test_success(spec, state): + address_change = spec.BLSToExecutionChange( + validator_index=0, + from_bls_pubkey=TEST, + to_execution_address=b'\x42' * 20, + ) + + yield from run_bls_to_execution_change_processing(spec, state, address_change)