Merge pull request #1491 from ethereum/rm-signing-root
rm signing root (fixes #1487)
This commit is contained in:
commit
7af6945f53
|
@ -19,10 +19,7 @@ from dataclasses import (
|
|||
field,
|
||||
)
|
||||
|
||||
from eth2spec.utils.ssz.ssz_impl import (
|
||||
hash_tree_root,
|
||||
signing_root,
|
||||
)
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_typing import (
|
||||
boolean, Container, List, Vector, uint64,
|
||||
Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
|
||||
|
@ -50,7 +47,6 @@ from dataclasses import (
|
|||
|
||||
from eth2spec.utils.ssz.ssz_impl import (
|
||||
hash_tree_root,
|
||||
signing_root,
|
||||
is_zero,
|
||||
)
|
||||
from eth2spec.utils.ssz.ssz_typing import (
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
- [`PendingAttestation`](#pendingattestation)
|
||||
- [`Eth1Data`](#eth1data)
|
||||
- [`HistoricalBatch`](#historicalbatch)
|
||||
- [`DepositMessage`](#depositmessage)
|
||||
- [`DepositData`](#depositdata)
|
||||
- [`BeaconBlockHeader`](#beaconblockheader)
|
||||
- [Beacon operations](#beacon-operations)
|
||||
|
@ -43,6 +44,10 @@
|
|||
- [`BeaconBlock`](#beaconblock)
|
||||
- [Beacon state](#beacon-state)
|
||||
- [`BeaconState`](#beaconstate)
|
||||
- [Signed envelopes](#signed-envelopes)
|
||||
- [`SignedVoluntaryExit`](#signedvoluntaryexit)
|
||||
- [`SignedBeaconBlock`](#signedbeaconblock)
|
||||
- [`SignedBeaconBlockHeader`](#signedbeaconblockheader)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [Math](#math)
|
||||
- [`integer_squareroot`](#integer_squareroot)
|
||||
|
@ -52,7 +57,6 @@
|
|||
- [Crypto](#crypto)
|
||||
- [`hash`](#hash)
|
||||
- [`hash_tree_root`](#hash_tree_root)
|
||||
- [`signing_root`](#signing_root)
|
||||
- [`bls_verify`](#bls_verify)
|
||||
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
|
||||
- [Predicates](#predicates)
|
||||
|
@ -342,6 +346,15 @@ class HistoricalBatch(Container):
|
|||
state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
|
||||
```
|
||||
|
||||
#### `DepositMessage`
|
||||
|
||||
```python
|
||||
class DepositMessage(Container):
|
||||
pubkey: BLSPubkey
|
||||
withdrawal_credentials: Bytes32
|
||||
amount: Gwei
|
||||
```
|
||||
|
||||
#### `DepositData`
|
||||
|
||||
```python
|
||||
|
@ -349,7 +362,7 @@ class DepositData(Container):
|
|||
pubkey: BLSPubkey
|
||||
withdrawal_credentials: Bytes32
|
||||
amount: Gwei
|
||||
signature: BLSSignature
|
||||
signature: BLSSignature # signing over DepositMessage
|
||||
```
|
||||
|
||||
#### `BeaconBlockHeader`
|
||||
|
@ -360,7 +373,6 @@ class BeaconBlockHeader(Container):
|
|||
parent_root: Root
|
||||
state_root: Root
|
||||
body_root: Root
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
### Beacon operations
|
||||
|
@ -370,8 +382,8 @@ class BeaconBlockHeader(Container):
|
|||
```python
|
||||
class ProposerSlashing(Container):
|
||||
proposer_index: ValidatorIndex
|
||||
header_1: BeaconBlockHeader
|
||||
header_2: BeaconBlockHeader
|
||||
signed_header_1: SignedBeaconBlockHeader
|
||||
signed_header_2: SignedBeaconBlockHeader
|
||||
```
|
||||
|
||||
#### `AttesterSlashing`
|
||||
|
@ -405,7 +417,6 @@ class Deposit(Container):
|
|||
class VoluntaryExit(Container):
|
||||
epoch: Epoch # Earliest epoch when voluntary exit can be processed
|
||||
validator_index: ValidatorIndex
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
### Beacon blocks
|
||||
|
@ -422,7 +433,7 @@ class BeaconBlockBody(Container):
|
|||
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
|
||||
attestations: List[Attestation, MAX_ATTESTATIONS]
|
||||
deposits: List[Deposit, MAX_DEPOSITS]
|
||||
voluntary_exits: List[VoluntaryExit, MAX_VOLUNTARY_EXITS]
|
||||
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
|
||||
```
|
||||
|
||||
#### `BeaconBlock`
|
||||
|
@ -433,7 +444,6 @@ class BeaconBlock(Container):
|
|||
parent_root: Root
|
||||
state_root: Root
|
||||
body: BeaconBlockBody
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
### Beacon state
|
||||
|
@ -472,6 +482,34 @@ class BeaconState(Container):
|
|||
finalized_checkpoint: Checkpoint
|
||||
```
|
||||
|
||||
### Signed envelopes
|
||||
|
||||
Some messages in the protocol are wrapped in an envelop to better facilitate adding/pruning the signature and to `hash_tree_root` the `message` separate from the signature.
|
||||
|
||||
#### `SignedVoluntaryExit`
|
||||
|
||||
```python
|
||||
class SignedVoluntaryExit(Container):
|
||||
message: VoluntaryExit
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
#### `SignedBeaconBlock`
|
||||
|
||||
```python
|
||||
class SignedBeaconBlock(Container):
|
||||
message: BeaconBlock
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
#### `SignedBeaconBlockHeader`
|
||||
|
||||
```python
|
||||
class SignedBeaconBlockHeader(Container):
|
||||
message: BeaconBlockHeader
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
## Helper functions
|
||||
|
||||
*Note*: The definitions below are for specification purposes and are not necessarily optimal implementations.
|
||||
|
@ -533,10 +571,6 @@ def bytes_to_int(data: bytes) -> uint64:
|
|||
|
||||
`def hash_tree_root(object: SSZSerializable) -> Root` is a function for hashing objects into a single root by utilizing a hash tree structure, as defined in the [SSZ spec](../simple-serialize.md#merkleization).
|
||||
|
||||
#### `signing_root`
|
||||
|
||||
`def signing_root(object: Container) -> Root` is a function for computing signing messages, as defined in the [SSZ spec](../simple-serialize.md#self-signed-containers).
|
||||
|
||||
#### `bls_verify`
|
||||
|
||||
`bls_verify` is a function for verifying a BLS signature, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify).
|
||||
|
@ -1048,18 +1082,27 @@ Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
|
|||
The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid.
|
||||
|
||||
```python
|
||||
def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root: bool=False) -> BeaconState:
|
||||
def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> BeaconState:
|
||||
# Process slots (including those with no blocks) since block
|
||||
process_slots(state, block.slot)
|
||||
process_slots(state, signed_block.message.slot)
|
||||
# Verify signature
|
||||
if validate_result:
|
||||
assert verify_block_signature(state, signed_block)
|
||||
# Process block
|
||||
process_block(state, block)
|
||||
# Validate state root (`validate_state_root == True` in production)
|
||||
if validate_state_root:
|
||||
assert block.state_root == hash_tree_root(state)
|
||||
process_block(state, signed_block.message)
|
||||
if validate_result:
|
||||
assert signed_block.message.state_root == hash_tree_root(state) # Validate state root
|
||||
# Return post-state
|
||||
return state
|
||||
```
|
||||
|
||||
```python
|
||||
def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) -> bool:
|
||||
proposer = state.validators[get_beacon_proposer_index(state)]
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER)
|
||||
return bls_verify(proposer.pubkey, hash_tree_root(signed_block.message), signed_block.signature, domain)
|
||||
```
|
||||
|
||||
```python
|
||||
def process_slots(state: BeaconState, slot: Slot) -> None:
|
||||
assert state.slot <= slot
|
||||
|
@ -1080,7 +1123,7 @@ def process_slot(state: BeaconState) -> None:
|
|||
if state.latest_block_header.state_root == Bytes32():
|
||||
state.latest_block_header.state_root = previous_state_root
|
||||
# Cache block root
|
||||
previous_block_root = signing_root(state.latest_block_header)
|
||||
previous_block_root = hash_tree_root(state.latest_block_header)
|
||||
state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root
|
||||
```
|
||||
|
||||
|
@ -1340,20 +1383,17 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
|||
# Verify that the slots match
|
||||
assert block.slot == state.slot
|
||||
# Verify that the parent matches
|
||||
assert block.parent_root == signing_root(state.latest_block_header)
|
||||
assert block.parent_root == hash_tree_root(state.latest_block_header)
|
||||
# Save current block as the new latest block
|
||||
state.latest_block_header = BeaconBlockHeader(
|
||||
slot=block.slot,
|
||||
parent_root=block.parent_root,
|
||||
# `state_root` is zeroed and overwritten in the next `process_slot` call
|
||||
body_root=hash_tree_root(block.body),
|
||||
# `signature` is zeroed
|
||||
)
|
||||
# Verify proposer is not slashed
|
||||
proposer = state.validators[get_beacon_proposer_index(state)]
|
||||
assert not proposer.slashed
|
||||
# Verify proposer signature
|
||||
assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER))
|
||||
```
|
||||
|
||||
#### RANDAO
|
||||
|
@ -1403,15 +1443,15 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|||
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
|
||||
proposer = state.validators[proposer_slashing.proposer_index]
|
||||
# Verify slots match
|
||||
assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot
|
||||
assert proposer_slashing.signed_header_1.message.slot == proposer_slashing.signed_header_2.message.slot
|
||||
# But the headers are different
|
||||
assert proposer_slashing.header_1 != proposer_slashing.header_2
|
||||
assert proposer_slashing.signed_header_1.message != proposer_slashing.signed_header_2.message
|
||||
# Check proposer is slashable
|
||||
assert is_slashable_validator(proposer, get_current_epoch(state))
|
||||
# Signatures are valid
|
||||
for header in (proposer_slashing.header_1, proposer_slashing.header_2):
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
|
||||
assert bls_verify(proposer.pubkey, signing_root(header), header.signature, domain)
|
||||
for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2):
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot))
|
||||
assert bls_verify(proposer.pubkey, hash_tree_root(signed_header.message), signed_header.signature, domain)
|
||||
|
||||
slash_validator(state, proposer_slashing.proposer_index)
|
||||
```
|
||||
|
@ -1489,7 +1529,11 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|||
# Note: The deposit contract does not check signatures.
|
||||
# Note: Deposits are valid across forks, thus the deposit domain is retrieved directly from `compute_domain`.
|
||||
domain = compute_domain(DOMAIN_DEPOSIT)
|
||||
if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature, domain):
|
||||
deposit_message = DepositMessage(
|
||||
pubkey=deposit.data.pubkey,
|
||||
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||
amount=deposit.data.amount)
|
||||
if not bls_verify(pubkey, hash_tree_root(deposit_message), deposit.data.signature, domain):
|
||||
return
|
||||
|
||||
# Add validator and balance entries
|
||||
|
@ -1512,19 +1556,20 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|||
##### Voluntary exits
|
||||
|
||||
```python
|
||||
def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
|
||||
validator = state.validators[exit.validator_index]
|
||||
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
||||
voluntary_exit = signed_voluntary_exit.message
|
||||
validator = state.validators[voluntary_exit.validator_index]
|
||||
# Verify the validator is active
|
||||
assert is_active_validator(validator, get_current_epoch(state))
|
||||
# Verify the validator has not yet exited
|
||||
assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
||||
# Exits must specify an epoch when they become valid; they are not valid before then
|
||||
assert get_current_epoch(state) >= exit.epoch
|
||||
assert get_current_epoch(state) >= voluntary_exit.epoch
|
||||
# Verify the validator has been active long enough
|
||||
assert get_current_epoch(state) >= validator.activation_epoch + PERSISTENT_COMMITTEE_PERIOD
|
||||
# Verify signature
|
||||
domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch)
|
||||
assert bls_verify(validator.pubkey, signing_root(exit), exit.signature, domain)
|
||||
domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||
assert bls_verify(validator.pubkey, hash_tree_root(voluntary_exit), signed_voluntary_exit.signature, domain)
|
||||
# Initiate exit
|
||||
initiate_validator_exit(state, exit.validator_index)
|
||||
initiate_validator_exit(state, voluntary_exit.validator_index)
|
||||
```
|
||||
|
|
|
@ -32,7 +32,7 @@ This document is the beacon chain fork choice spec, part of Ethereum 2.0 Phase 0
|
|||
The head block root associated with a `store` is defined as `get_head(store)`. At genesis, let `store = get_genesis_store(genesis_state)` and update `store` by running:
|
||||
|
||||
- `on_tick(time)` whenever `time > store.time` where `time` is the current Unix time
|
||||
- `on_block(block)` whenever a block `block` is received
|
||||
- `on_block(block)` whenever a block `block: SignedBeaconBlock` is received
|
||||
- `on_attestation(attestation)` whenever an attestation `attestation` is received
|
||||
|
||||
*Notes*:
|
||||
|
@ -81,7 +81,7 @@ class Store(object):
|
|||
```python
|
||||
def get_genesis_store(genesis_state: BeaconState) -> Store:
|
||||
genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))
|
||||
root = signing_root(genesis_block)
|
||||
root = hash_tree_root(genesis_block)
|
||||
justified_checkpoint = Checkpoint(epoch=GENESIS_EPOCH, root=root)
|
||||
finalized_checkpoint = Checkpoint(epoch=GENESIS_EPOCH, root=root)
|
||||
return Store(
|
||||
|
@ -203,25 +203,26 @@ def on_tick(store: Store, time: uint64) -> None:
|
|||
#### `on_block`
|
||||
|
||||
```python
|
||||
def on_block(store: Store, block: BeaconBlock) -> None:
|
||||
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
block = signed_block.message
|
||||
# Make a copy of the state to avoid mutability issues
|
||||
assert block.parent_root in store.block_states
|
||||
pre_state = store.block_states[block.parent_root].copy()
|
||||
# Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
|
||||
assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT
|
||||
# Add new block to the store
|
||||
store.blocks[signing_root(block)] = block
|
||||
store.blocks[hash_tree_root(block)] = block
|
||||
# Check block is a descendant of the finalized block
|
||||
assert (
|
||||
get_ancestor(store, signing_root(block), store.blocks[store.finalized_checkpoint.root].slot) ==
|
||||
get_ancestor(store, hash_tree_root(block), store.blocks[store.finalized_checkpoint.root].slot) ==
|
||||
store.finalized_checkpoint.root
|
||||
)
|
||||
# Check that block is later than the finalized epoch slot
|
||||
assert block.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
||||
# Check the block is valid and compute the post-state
|
||||
state = state_transition(pre_state, block, True)
|
||||
state = state_transition(pre_state, signed_block, True)
|
||||
# Add new state for this block to the store
|
||||
store.block_states[signing_root(block)] = state
|
||||
store.block_states[hash_tree_root(block)] = state
|
||||
|
||||
# Update justified checkpoint
|
||||
if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
|
|
|
@ -595,7 +595,8 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) ->
|
|||
# Verify challenge signature
|
||||
challenger = state.validators[challenge.challenger_index]
|
||||
domain = get_domain(state, DOMAIN_CUSTODY_BIT_CHALLENGE, get_current_epoch(state))
|
||||
assert bls_verify(challenger.pubkey, signing_root(challenge), challenge.signature, domain)
|
||||
# TODO incorrect hash-tree-root, but this changes with phase 1 PR #1483
|
||||
assert bls_verify(challenger.pubkey, hash_tree_root(challenge), challenge.signature, domain)
|
||||
# Verify challenger is slashable
|
||||
assert is_slashable_validator(challenger, get_current_epoch(state))
|
||||
# Verify attestation
|
||||
|
|
|
@ -359,9 +359,9 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat
|
|||
)
|
||||
if beacon_block_header.state_root == Bytes32():
|
||||
beacon_block_header.state_root = hash_tree_root(beacon_state)
|
||||
assert block.beacon_block_root == signing_root(beacon_block_header)
|
||||
assert block.beacon_block_root == hash_tree_root(beacon_block_header)
|
||||
# Verify the parent root
|
||||
assert block.parent_root == signing_root(shard_state.latest_block_header)
|
||||
assert block.parent_root == hash_tree_root(shard_state.latest_block_header)
|
||||
# Save current block as the new latest block
|
||||
shard_state.latest_block_header = ShardBlockHeader(
|
||||
shard=block.shard,
|
||||
|
@ -384,7 +384,7 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat
|
|||
assert not proposer.slashed
|
||||
# Verify proposer signature
|
||||
domain = get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_shard_slot(block.slot))
|
||||
assert bls_verify(proposer.pubkey, signing_root(block), block.signature, domain)
|
||||
assert bls_verify(proposer.pubkey, hash_tree_root(block), block.signature, domain)
|
||||
```
|
||||
|
||||
#### Attestations
|
||||
|
|
|
@ -151,7 +151,7 @@ The payload is carried in the `data` field of a gossipsub message, and varies de
|
|||
|
||||
| Topic | Message Type |
|
||||
|----------------------------------------|-------------------|
|
||||
| beacon_block | BeaconBlock |
|
||||
| beacon_block | SignedBeaconBlock |
|
||||
| beacon_aggregate_and_proof | AggregateAndProof |
|
||||
| beacon_attestation\* | Attestation |
|
||||
| committee_index{subnet_id}\_beacon_attestation | Attestation |
|
||||
|
@ -214,7 +214,7 @@ Topics are post-fixed with an encoding. Encodings define how the payload of a go
|
|||
|
||||
#### Interop
|
||||
|
||||
- `ssz` - All objects are [SSZ-encoded](#ssz-encoding). Example: The beacon block topic string is `/eth2/beacon_block/ssz`, and the data field of a gossipsub message is an ssz-encoded `BeaconBlock`.
|
||||
- `ssz` - All objects are [SSZ-encoded](#ssz-encoding). Example: The beacon block topic string is `/eth2/beacon_block/ssz`, and the data field of a gossipsub message is an ssz-encoded `SignedBeaconBlock`.
|
||||
|
||||
#### Mainnet
|
||||
|
||||
|
@ -331,9 +331,9 @@ The [SimpleSerialize (SSZ) specification](../simple-serialize.md) outlines how o
|
|||
|
||||
All messages that contain only a single field MUST be encoded directly as the type of that field and MUST NOT be encoded as an SSZ container.
|
||||
|
||||
Responses that are SSZ-lists (for example `[]BeaconBlocks`) send their
|
||||
Responses that are SSZ-lists (for example `[]SignedBeaconBlock`) send their
|
||||
constituents individually as `response_chunk`s. For example, the
|
||||
`[]BeaconBlocks` response type sends one or more `response_chunk`s. Each _successful_ `response_chunk` contains a single `BeaconBlock` payload.
|
||||
`[]SignedBeaconBlock` response type sends one or more `response_chunk`s. Each _successful_ `response_chunk` contains a single `SignedBeaconBlock` payload.
|
||||
|
||||
### Messages
|
||||
|
||||
|
@ -413,15 +413,15 @@ Request Content:
|
|||
Response Content:
|
||||
```
|
||||
(
|
||||
[]BeaconBlock
|
||||
[]SignedBeaconBlock
|
||||
)
|
||||
```
|
||||
|
||||
Requests count beacon blocks from the peer starting from `start_slot` on the chain defined by `head_block_root` (= `signing_root(BeaconBlock)`). The response MUST contain no more than count blocks. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …]. A step value of 1 returns all blocks on the range `[start_slot, start_slot + count)`.
|
||||
Requests count beacon blocks from the peer starting from `start_slot` on the chain defined by `head_block_root` (= `hash_tree_root(SignedBeaconBlock.message)`). The response MUST contain no more than count blocks. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …]. A step value of 1 returns all blocks on the range `[start_slot, start_slot + count)`.
|
||||
|
||||
The request MUST be encoded as an SSZ-container.
|
||||
|
||||
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `BeaconBlock` payload.
|
||||
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
|
||||
|
||||
`BeaconBlocksByRange` is primarily used to sync historical blocks.
|
||||
|
||||
|
@ -449,17 +449,17 @@ Response Content:
|
|||
|
||||
```
|
||||
(
|
||||
[]BeaconBlock
|
||||
[]SignedBeaconBlock
|
||||
)
|
||||
```
|
||||
|
||||
Requests blocks by block root (= `signing_root(BeaconBlock)`). The response is a list of `BeaconBlock` whose length is less than or equal to the number of requested blocks. It may be less in the case that the responding peer is missing blocks.
|
||||
Requests blocks by block root (= `hash_tree_root(SignedBeaconBlock.message)`). The response is a list of `SignedBeaconBlock` whose length is less than or equal to the number of requested blocks. It may be less in the case that the responding peer is missing blocks.
|
||||
|
||||
`BeaconBlocksByRoot` is primarily used to recover recent blocks (e.g. when receiving a block or attestation whose parent is unknown).
|
||||
|
||||
The request MUST be encoded as an SSZ-field.
|
||||
|
||||
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `BeaconBlock` payload.
|
||||
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
|
||||
|
||||
Clients MUST support requesting blocks since the latest finalized epoch.
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
- [Vectors, containers, lists, unions](#vectors-containers-lists-unions)
|
||||
- [Deserialization](#deserialization)
|
||||
- [Merkleization](#merkleization)
|
||||
- [Self-signed containers](#self-signed-containers)
|
||||
- [Summaries and expansions](#summaries-and-expansions)
|
||||
- [Implementations](#implementations)
|
||||
|
||||
|
@ -106,7 +105,7 @@ An SSZ object is called zeroed (and thus, `is_zero(object)` returns true) if it
|
|||
|
||||
We recursively define the `serialize` function which consumes an object `value` (of the type specified) and returns a bytestring of type `bytes`.
|
||||
|
||||
*Note*: In the function definitions below (`serialize`, `hash_tree_root`, `signing_root`, `is_variable_size`, etc.) objects implicitly carry their type.
|
||||
*Note*: In the function definitions below (`serialize`, `hash_tree_root`, `is_variable_size`, etc.) objects implicitly carry their type.
|
||||
|
||||
### `uintN`
|
||||
|
||||
|
@ -234,10 +233,6 @@ We now define Merkleization `hash_tree_root(value)` of an object `value` recursi
|
|||
* `mix_in_length(merkleize([hash_tree_root(element) for element in value], limit=chunk_count(type)), len(value))` if `value` is a list of composite objects.
|
||||
* `mix_in_type(merkleize(value.value), value.type_index)` if `value` is of union type.
|
||||
|
||||
## Self-signed containers
|
||||
|
||||
Let `value` be a self-signed container object. The convention is that the signature (e.g. a `"bytes96"` BLS12-381 signature) be the last field of `value`. Further, the signed message for `value` is `signing_root(value) = hash_tree_root(truncate_last(value))` where `truncate_last` truncates the last element of `value`.
|
||||
|
||||
## Summaries and expansions
|
||||
|
||||
Let `A` be an object derived from another object `B` by replacing some of the (possibly nested) values of `B` by their `hash_tree_root`. We say `A` is a "summary" of `B`, and that `B` is an "expansion" of `A`. Notice `hash_tree_root(A) == hash_tree_root(B)`.
|
||||
|
|
|
@ -42,7 +42,6 @@ The expected roots are encoded into the metadata yaml:
|
|||
|
||||
```yaml
|
||||
root: Bytes32 -- Hash-tree-root of the object
|
||||
signing_root: Bytes32 -- Signing-root of the object
|
||||
```
|
||||
|
||||
The `Bytes32` is encoded as a string, hexadecimal encoding, prefixed with `0x`.
|
||||
|
|
|
@ -24,8 +24,6 @@ The output parts are: `roots.yaml`, `serialized.ssz`, `value.yaml`
|
|||
|
||||
```yaml
|
||||
root: bytes32 -- string, hash-tree-root of the value, hex encoded, with prefix 0x
|
||||
signing_root: bytes32 -- string, signing-root of the value, hex encoded, with prefix 0x.
|
||||
*Optional*, present if type is a container and ends with a ``signature`` field.
|
||||
```
|
||||
|
||||
### `serialized.ssz`
|
||||
|
@ -47,11 +45,9 @@ A test-runner can implement the following assertions:
|
|||
- Serialization in 2 steps: deserialize `serialized`, then serialize the result,
|
||||
and verify if the bytes match the original `serialized`.
|
||||
- Hash-tree-root: After parsing the `value` (or deserializing `serialized`), Hash-tree-root it: the output should match `root`
|
||||
- Optionally also check `signing_root`, if present.
|
||||
|
||||
|
||||
## References
|
||||
|
||||
**`serialized`**—[SSZ serialization](../../simple-serialize.md#serialization)
|
||||
**`root`**—[hash_tree_root](../../simple-serialize.md#merkleization) function
|
||||
**`signing_root`**—[signing_root](../../simple-serialize.md#self-signed-containers) function
|
||||
|
|
|
@ -110,8 +110,11 @@ To submit a deposit:
|
|||
|
||||
- Pack the validator's [initialization parameters](#initialization) into `deposit_data`, a [`DepositData`](../core/0_beacon-chain.md#depositdata) SSZ object.
|
||||
- Let `amount` be the amount in Gwei to be deposited by the validator where `amount >= MIN_DEPOSIT_AMOUNT`.
|
||||
- Set `deposit_data.amount = amount`.
|
||||
- Let `signature` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=compute_domain(DOMAIN_DEPOSIT)`. (Deposits are valid regardless of fork version, `compute_domain` will default to zeroes there).
|
||||
- Set `deposit_data.pubkey` to validator's `pubkey`.
|
||||
- Set `deposit_data.withdrawal_credentials` to `withdrawal_credentials`.
|
||||
- Set `deposit_data.amount` to `amount`.
|
||||
- Let `deposit_message` be a `DepositMessage` with all the `DepositData` contents except the `signature`.
|
||||
- Let `signature` be the result of `bls_sign` of the `hash_tree_root(deposit_message)` with `domain=compute_domain(DOMAIN_DEPOSIT)`. (Deposits are valid regardless of fork version, `compute_domain` will default to zeroes there).
|
||||
- Let `deposit_data_root` be `hash_tree_root(deposit_data)`.
|
||||
- Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96], deposit_data_root: bytes32)` along with a deposit of `amount` Gwei.
|
||||
|
||||
|
@ -200,11 +203,13 @@ A validator has two primary responsibilities to the beacon chain: [proposing blo
|
|||
|
||||
### Block proposal
|
||||
|
||||
A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator creates, signs, and broadcasts a `block` that is a child of `parent` that satisfies a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function).
|
||||
A validator is expected to propose a [`SignedBeaconBlock`](../core/0_beacon-chain.md#signedbeaconblock) at the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator creates, signs, and broadcasts a `block` that is a child of `parent` that satisfies a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function).
|
||||
|
||||
There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (e.g. at 312,500 validators = 10 million ETH, that's once per ~6 weeks).
|
||||
|
||||
#### Block header
|
||||
#### Preparing for a `BeaconBlock`
|
||||
|
||||
To construct a `BeaconBlockBody`, a `block` (`BeaconBlock`) is defined with the necessary context for a block proposal:
|
||||
|
||||
##### Slot
|
||||
|
||||
|
@ -214,17 +219,14 @@ Set `block.slot = slot` where `slot` is the current slot at which the validator
|
|||
|
||||
##### Parent root
|
||||
|
||||
Set `block.parent_root = signing_root(parent)`.
|
||||
Set `block.parent_root = hash_tree_root(parent)`.
|
||||
|
||||
##### State root
|
||||
|
||||
Set `block.state_root = hash_tree_root(state)` of the resulting `state` of the `parent -> block` state transition.
|
||||
|
||||
*Note*: To calculate `state_root`, the validator should first run the state transition function on an unsigned `block` containing a stub for the `state_root`. It is useful to be able to run a state transition function that does _not_ validate signatures or state root for this purpose.
|
||||
#### Constructing the `BeaconBlockBody`
|
||||
|
||||
##### Randao reveal
|
||||
|
||||
Set `block.randao_reveal = epoch_signature` where `epoch_signature` is obtained from:
|
||||
Set `block.body.randao_reveal = epoch_signature` where `epoch_signature` is obtained from:
|
||||
|
||||
```python
|
||||
def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> BLSSignature:
|
||||
|
@ -234,11 +236,11 @@ def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) ->
|
|||
|
||||
##### Eth1 Data
|
||||
|
||||
The `block.eth1_data` field is for block proposers to vote on recent Eth1 data. This recent data contains an Eth1 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth1 block. If over half of the block proposers in the current Eth1 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
The `block.body.eth1_data` field is for block proposers to vote on recent Eth1 data. This recent data contains an Eth1 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth1 block. If over half of the block proposers in the current Eth1 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
|
||||
Let `get_eth1_data(distance: uint64) -> Eth1Data` be the (subjective) function that returns the Eth1 data at distance `distance` relative to the Eth1 head at the start of the current Eth1 voting period. Let `previous_eth1_distance` be the distance relative to the Eth1 block corresponding to `eth1_data.block_hash` found in the state at the _start_ of the current Eth1 voting period. Note that `eth1_data` can be updated in the middle of a voting period and thus the starting `eth1_data.block_hash` must be stored separately.
|
||||
|
||||
An honest block proposer sets `block.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where:
|
||||
An honest block proposer sets `block.body.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where:
|
||||
|
||||
```python
|
||||
def get_eth1_vote(state: BeaconState, previous_eth1_distance: uint64) -> Eth1Data:
|
||||
|
@ -260,18 +262,6 @@ def get_eth1_vote(state: BeaconState, previous_eth1_distance: uint64) -> Eth1Dat
|
|||
)
|
||||
```
|
||||
|
||||
##### Signature
|
||||
|
||||
Set `header.signature = block_signature` where `block_signature` is obtained from:
|
||||
|
||||
```python
|
||||
def get_block_signature(state: BeaconState, header: BeaconBlockHeader, privkey: int) -> BLSSignature:
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
|
||||
return bls_sign(privkey, signing_root(header), domain)
|
||||
```
|
||||
|
||||
#### Block body
|
||||
|
||||
##### Proposer slashings
|
||||
|
||||
Up to `MAX_PROPOSER_SLASHINGS`, [`ProposerSlashing`](../core/0_beacon-chain.md#proposerslashing) objects can be included in the `block`. The proposer slashings must satisfy the verification conditions found in [proposer slashings processing](../core/0_beacon-chain.md#proposer-slashings). The validator receives a small "whistleblower" reward for each proposer slashing found and included.
|
||||
|
@ -294,6 +284,33 @@ The `proof` for each deposit must be constructed against the deposit root contai
|
|||
|
||||
Up to `MAX_VOLUNTARY_EXITS`, [`VoluntaryExit`](../core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](../core/0_beacon-chain.md#voluntary-exits).
|
||||
|
||||
|
||||
#### Packaging into a `SignedBeaconBlock`
|
||||
|
||||
##### State root
|
||||
|
||||
Set `block.state_root = hash_tree_root(state)` of the resulting `state` of the `parent -> block` state transition.
|
||||
|
||||
*Note*: To calculate `state_root`, the validator should first run the state transition function on an unsigned `block` containing a stub for the `state_root`.
|
||||
It is useful to be able to run a state transition function (working on a copy of the state) that does _not_ validate signatures or state root for this purpose:
|
||||
|
||||
```python
|
||||
def compute_new_state_root(state: BeaconState, block: BeaconBlock) -> Root:
|
||||
process_slots(state, block.slot)
|
||||
process_block(state, block)
|
||||
return hash_tree_root(state)
|
||||
```
|
||||
|
||||
##### Signature
|
||||
|
||||
`signed_block = SignedBeaconBlock(message=block, signature=block_signature)`, where `block_signature` is obtained from:
|
||||
|
||||
```python
|
||||
def get_block_signature(state: BeaconState, header: BeaconBlockHeader, privkey: int) -> BLSSignature:
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
|
||||
return bls_sign(privkey, hash_tree_root(header), domain)
|
||||
```
|
||||
|
||||
### Attesting
|
||||
|
||||
A validator is expected to create, sign, and broadcast an attestation during each epoch. The `committee`, assigned `index`, and assigned `slot` for which the validator performs this role during an epoch are defined by `get_committee_assignment(state, epoch, validator_index)`.
|
||||
|
@ -316,7 +333,7 @@ First, the validator should construct `attestation_data`, an [`AttestationData`]
|
|||
|
||||
##### LMD GHOST vote
|
||||
|
||||
Set `attestation_data.beacon_block_root = signing_root(head_block)`.
|
||||
Set `attestation_data.beacon_block_root = hash_tree_root(head_block)`.
|
||||
|
||||
##### FFG vote
|
||||
|
||||
|
@ -326,7 +343,7 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`.
|
|||
*Note*: `epoch_boundary_block_root` can be looked up in the state using:
|
||||
|
||||
- Let `start_slot = compute_start_slot_at_epoch(get_current_epoch(head_state))`.
|
||||
- Let `epoch_boundary_block_root = signing_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`.
|
||||
- Let `epoch_boundary_block_root = hash_tree_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`.
|
||||
|
||||
#### Construct attestation
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from eth2spec.utils.ssz.ssz_impl import serialize, hash_tree_root, signing_root
|
||||
from eth2spec.utils.ssz.ssz_impl import serialize, hash_tree_root
|
||||
from eth2spec.debug.encode import encode
|
||||
from eth2spec.utils.ssz.ssz_typing import SSZValue, Container
|
||||
from typing import Callable
|
||||
|
@ -10,8 +10,6 @@ def valid_test_case(value_fn: Callable[[], SSZValue]):
|
|||
yield "value", "data", encode(value)
|
||||
yield "serialized", "ssz", serialize(value)
|
||||
yield "root", "meta", '0x' + hash_tree_root(value).hex()
|
||||
if isinstance(value, Container):
|
||||
yield "signing_root", "meta", '0x' + signing_root(value).hex()
|
||||
return case_fn
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ from eth2spec.phase0 import spec
|
|||
from eth2spec.utils.ssz.ssz_typing import Container
|
||||
from eth2spec.utils.ssz.ssz_impl import (
|
||||
hash_tree_root,
|
||||
signing_root,
|
||||
serialize,
|
||||
)
|
||||
from gen_base import gen_runner, gen_typing
|
||||
|
@ -24,8 +23,6 @@ def create_test_case(rng: Random, typ, mode: random_value.RandomizationMode, cha
|
|||
roots_data = {
|
||||
"root": '0x' + hash_tree_root(value).hex()
|
||||
}
|
||||
if isinstance(value, Container) and hasattr(value, "signature"):
|
||||
roots_data["signing_root"] = '0x' + signing_root(value).hex()
|
||||
yield "roots", "data", roots_data
|
||||
|
||||
|
||||
|
|
|
@ -4,19 +4,19 @@ from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
|||
from eth2spec.test.helpers.state import state_transition_and_sign_block
|
||||
|
||||
|
||||
def add_block_to_store(spec, store, block):
|
||||
pre_state = store.block_states[block.parent_root]
|
||||
block_time = pre_state.genesis_time + block.slot * spec.SECONDS_PER_SLOT
|
||||
def add_block_to_store(spec, store, signed_block):
|
||||
pre_state = store.block_states[signed_block.message.parent_root]
|
||||
block_time = pre_state.genesis_time + signed_block.message.slot * spec.SECONDS_PER_SLOT
|
||||
|
||||
if store.time < block_time:
|
||||
spec.on_tick(store, block_time)
|
||||
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
|
||||
def add_attestation_to_store(spec, store, attestation):
|
||||
parent_block = store.blocks[attestation.data.beacon_block_root]
|
||||
pre_state = store.block_states[spec.signing_root(parent_block)]
|
||||
pre_state = store.block_states[spec.hash_tree_root(parent_block)]
|
||||
block_time = pre_state.genesis_time + parent_block.slot * spec.SECONDS_PER_SLOT
|
||||
next_epoch_time = block_time + spec.SLOTS_PER_EPOCH * spec.SECONDS_PER_SLOT
|
||||
|
||||
|
@ -32,7 +32,7 @@ def test_genesis(spec, state):
|
|||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -41,19 +41,19 @@ def test_chain_no_attestations(spec, state):
|
|||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
|
||||
|
||||
# On receiving a block of `GENESIS_SLOT + 1` slot
|
||||
block_1 = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block_1)
|
||||
add_block_to_store(spec, store, block_1)
|
||||
signed_block_1 = state_transition_and_sign_block(spec, state, block_1)
|
||||
add_block_to_store(spec, store, signed_block_1)
|
||||
|
||||
# On receiving a block of next epoch
|
||||
block_2 = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block_2)
|
||||
add_block_to_store(spec, store, block_2)
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state, block_2)
|
||||
add_block_to_store(spec, store, signed_block_2)
|
||||
|
||||
assert spec.get_head(store) == spec.signing_root(block_2)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(block_2)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -64,22 +64,22 @@ def test_split_tie_breaker_no_attestations(spec, state):
|
|||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
|
||||
|
||||
# block at slot 1
|
||||
block_1_state = genesis_state.copy()
|
||||
block_1 = build_empty_block_for_next_slot(spec, block_1_state)
|
||||
state_transition_and_sign_block(spec, block_1_state, block_1)
|
||||
add_block_to_store(spec, store, block_1)
|
||||
signed_block_1 = state_transition_and_sign_block(spec, block_1_state, block_1)
|
||||
add_block_to_store(spec, store, signed_block_1)
|
||||
|
||||
# additional block at slot 1
|
||||
block_2_state = genesis_state.copy()
|
||||
block_2 = build_empty_block_for_next_slot(spec, block_2_state)
|
||||
block_2.body.graffiti = b'\x42' * 32
|
||||
state_transition_and_sign_block(spec, block_2_state, block_2)
|
||||
add_block_to_store(spec, store, block_2)
|
||||
signed_block_2 = state_transition_and_sign_block(spec, block_2_state, block_2)
|
||||
add_block_to_store(spec, store, signed_block_2)
|
||||
|
||||
highest_root = max(spec.signing_root(block_1), spec.signing_root(block_2))
|
||||
highest_root = max(spec.hash_tree_root(block_1), spec.hash_tree_root(block_2))
|
||||
|
||||
assert spec.get_head(store) == highest_root
|
||||
|
||||
|
@ -92,23 +92,23 @@ def test_shorter_chain_but_heavier_weight(spec, state):
|
|||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
|
||||
|
||||
# build longer tree
|
||||
long_state = genesis_state.copy()
|
||||
for i in range(3):
|
||||
long_block = build_empty_block_for_next_slot(spec, long_state)
|
||||
state_transition_and_sign_block(spec, long_state, long_block)
|
||||
add_block_to_store(spec, store, long_block)
|
||||
signed_long_block = state_transition_and_sign_block(spec, long_state, long_block)
|
||||
add_block_to_store(spec, store, signed_long_block)
|
||||
|
||||
# build short tree
|
||||
short_state = genesis_state.copy()
|
||||
short_block = build_empty_block_for_next_slot(spec, short_state)
|
||||
short_block.body.graffiti = b'\x42' * 32
|
||||
state_transition_and_sign_block(spec, short_state, short_block)
|
||||
add_block_to_store(spec, store, short_block)
|
||||
signed_short_block = state_transition_and_sign_block(spec, short_state, short_block)
|
||||
add_block_to_store(spec, store, signed_short_block)
|
||||
|
||||
short_attestation = get_valid_attestation(spec, short_state, short_block.slot, signed=True)
|
||||
add_attestation_to_store(spec, store, short_attestation)
|
||||
|
||||
assert spec.get_head(store) == spec.signing_root(short_block)
|
||||
assert spec.get_head(store) == spec.hash_tree_root(short_block)
|
||||
|
|
|
@ -31,12 +31,12 @@ def test_on_attestation_current_epoch(spec, state):
|
|||
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * 2)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
# store block in store
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
|
||||
assert attestation.data.target.epoch == spec.GENESIS_EPOCH
|
||||
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH
|
||||
|
||||
|
@ -50,12 +50,12 @@ def test_on_attestation_previous_epoch(spec, state):
|
|||
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
# store block in store
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
|
||||
assert attestation.data.target.epoch == spec.GENESIS_EPOCH
|
||||
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 1
|
||||
|
||||
|
@ -73,11 +73,11 @@ def test_on_attestation_past_epoch(spec, state):
|
|||
|
||||
# create and store block from 3 epochs ago
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
spec.on_block(store, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
# create attestation for past block
|
||||
attestation = get_valid_attestation(spec, state, slot=state.slot)
|
||||
attestation = get_valid_attestation(spec, state, slot=state.slot, signed=True)
|
||||
assert attestation.data.target.epoch == spec.GENESIS_EPOCH
|
||||
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 2
|
||||
|
||||
|
@ -99,8 +99,8 @@ def test_on_attestation_target_not_in_store(spec, state):
|
|||
|
||||
# do not add target block to store
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=target_block.slot)
|
||||
assert attestation.data.target.root == target_block.signing_root()
|
||||
attestation = get_valid_attestation(spec, state, slot=target_block.slot, signed=True)
|
||||
assert attestation.data.target.root == target_block.hash_tree_root()
|
||||
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
@ -116,19 +116,19 @@ def test_on_attestation_beacon_block_not_in_store(spec, state):
|
|||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
|
||||
|
||||
target_block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, target_block)
|
||||
signed_target_block = state_transition_and_sign_block(spec, state, target_block)
|
||||
|
||||
# store target in store
|
||||
spec.on_block(store, target_block)
|
||||
spec.on_block(store, signed_target_block)
|
||||
|
||||
head_block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, head_block)
|
||||
|
||||
# do not add head block to store
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=head_block.slot)
|
||||
assert attestation.data.target.root == target_block.signing_root()
|
||||
assert attestation.data.beacon_block_root == head_block.signing_root()
|
||||
attestation = get_valid_attestation(spec, state, slot=head_block.slot, signed=True)
|
||||
assert attestation.data.target.root == target_block.hash_tree_root()
|
||||
assert attestation.data.beacon_block_root == head_block.hash_tree_root()
|
||||
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
@ -141,15 +141,15 @@ def test_on_attestation_future_epoch(spec, state):
|
|||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
# store block in store
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
# move state forward but not store
|
||||
state.slot = block.slot + spec.SLOTS_PER_EPOCH
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=state.slot)
|
||||
attestation = get_valid_attestation(spec, state, slot=state.slot, signed=True)
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
||||
|
@ -161,13 +161,13 @@ def test_on_attestation_future_block(spec, state):
|
|||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
# attestation for slot immediately prior to the block being attested to
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot - 1, signed=False)
|
||||
attestation.data.beacon_block_root = block.signing_root()
|
||||
attestation.data.beacon_block_root = block.hash_tree_root()
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
@ -181,11 +181,11 @@ def test_on_attestation_same_slot(spec, state):
|
|||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
||||
|
@ -197,11 +197,11 @@ def test_on_attestation_invalid_attestation(spec, state):
|
|||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
|
||||
# make invalid by using an invalid committee index
|
||||
attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
from copy import deepcopy
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block, transition_unsigned_block, \
|
||||
build_empty_block
|
||||
from eth2spec.test.helpers.state import next_epoch, next_epoch_with_attestations, state_transition_and_sign_block
|
||||
|
||||
|
||||
def run_on_block(spec, store, block, valid=True):
|
||||
def run_on_block(spec, store, signed_block, valid=True):
|
||||
if not valid:
|
||||
try:
|
||||
spec.on_block(store, block)
|
||||
spec.on_block(store, signed_block)
|
||||
except AssertionError:
|
||||
return
|
||||
else:
|
||||
assert False
|
||||
|
||||
spec.on_block(store, block)
|
||||
assert store.blocks[signing_root(block)] == block
|
||||
spec.on_block(store, signed_block)
|
||||
assert store.blocks[hash_tree_root(signed_block.message)] == signed_block.message
|
||||
|
||||
|
||||
def apply_next_epoch_with_attestations(spec, state, store):
|
||||
_, new_blocks, post_state = next_epoch_with_attestations(spec, state, True, False)
|
||||
for block in new_blocks:
|
||||
block_root = signing_root(block)
|
||||
_, new_signed_blocks, post_state = next_epoch_with_attestations(spec, state, True, False)
|
||||
for signed_block in new_signed_blocks:
|
||||
block = signed_block.message
|
||||
block_root = hash_tree_root(block)
|
||||
store.blocks[block_root] = block
|
||||
store.block_states[block_root] = post_state
|
||||
last_block = block
|
||||
last_signed_block = signed_block
|
||||
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
|
||||
return post_state, store, last_block
|
||||
return post_state, store, last_signed_block
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -41,16 +43,15 @@ def test_basic(spec, state):
|
|||
|
||||
# On receiving a block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
run_on_block(spec, store, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
run_on_block(spec, store, signed_block)
|
||||
|
||||
# On receiving a block of next epoch
|
||||
store.time = time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot += spec.SLOTS_PER_EPOCH
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
run_on_block(spec, store, block)
|
||||
run_on_block(spec, store, signed_block)
|
||||
|
||||
# TODO: add tests for justified_root and finalized_root
|
||||
|
||||
|
@ -65,10 +66,10 @@ def test_on_block_checkpoints(spec, state):
|
|||
|
||||
next_epoch(spec, state)
|
||||
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
|
||||
state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
|
||||
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
|
||||
next_epoch(spec, state)
|
||||
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
|
||||
last_block_root = signing_root(last_block)
|
||||
last_block_root = hash_tree_root(last_signed_block.message)
|
||||
|
||||
# Mock the finalized_checkpoint
|
||||
fin_state = store.block_states[last_block_root]
|
||||
|
@ -77,8 +78,8 @@ def test_on_block_checkpoints(spec, state):
|
|||
)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, fin_state)
|
||||
state_transition_and_sign_block(spec, deepcopy(fin_state), block)
|
||||
run_on_block(spec, store, block)
|
||||
signed_block = state_transition_and_sign_block(spec, deepcopy(fin_state), block)
|
||||
run_on_block(spec, store, signed_block)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -91,8 +92,8 @@ def test_on_block_future_block(spec, state):
|
|||
|
||||
# Fail receiving block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
run_on_block(spec, store, block, False)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
run_on_block(spec, store, signed_block, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -105,14 +106,14 @@ def test_on_block_bad_parent_root(spec, state):
|
|||
|
||||
# Fail receiving block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
spec.state_transition(state, block)
|
||||
transition_unsigned_block(spec, state, block)
|
||||
block.state_root = state.hash_tree_root()
|
||||
|
||||
block.parent_root = b'\x45' * 32
|
||||
|
||||
sign_block(spec, state, block)
|
||||
signed_block = sign_block(spec, state, block)
|
||||
|
||||
run_on_block(spec, store, block, False)
|
||||
run_on_block(spec, store, signed_block, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -130,8 +131,8 @@ def test_on_block_before_finalized(spec, state):
|
|||
|
||||
# Fail receiving block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
run_on_block(spec, store, block, False)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
run_on_block(spec, store, signed_block, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -144,10 +145,10 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
|
|||
|
||||
next_epoch(spec, state)
|
||||
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
|
||||
state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
|
||||
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
|
||||
next_epoch(spec, state)
|
||||
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
|
||||
last_block_root = signing_root(last_block)
|
||||
last_block_root = hash_tree_root(last_signed_block.message)
|
||||
|
||||
# Mock the justified checkpoint
|
||||
just_state = store.block_states[last_block_root]
|
||||
|
@ -158,9 +159,9 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
|
|||
just_state.current_justified_checkpoint = new_justified
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, just_state)
|
||||
state_transition_and_sign_block(spec, deepcopy(just_state), block)
|
||||
signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
|
||||
assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH < spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED
|
||||
run_on_block(spec, store, block)
|
||||
run_on_block(spec, store, signed_block)
|
||||
|
||||
assert store.justified_checkpoint == new_justified
|
||||
|
||||
|
@ -175,10 +176,10 @@ def test_on_block_outside_safe_slots_and_old_block(spec, state):
|
|||
|
||||
next_epoch(spec, state)
|
||||
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
|
||||
state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
|
||||
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
|
||||
next_epoch(spec, state)
|
||||
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
|
||||
last_block_root = signing_root(last_block)
|
||||
last_block_root = hash_tree_root(last_signed_block.message)
|
||||
|
||||
# Mock justified block in store
|
||||
just_block = build_empty_block_for_next_slot(spec, state)
|
||||
|
@ -195,11 +196,11 @@ def test_on_block_outside_safe_slots_and_old_block(spec, state):
|
|||
just_state.current_justified_checkpoint = new_justified
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, just_state)
|
||||
state_transition_and_sign_block(spec, deepcopy(just_state), block)
|
||||
signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
|
||||
|
||||
spec.on_tick(store, store.time + spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED * spec.SECONDS_PER_SLOT)
|
||||
assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED
|
||||
run_on_block(spec, store, block)
|
||||
run_on_block(spec, store, signed_block)
|
||||
|
||||
assert store.justified_checkpoint != new_justified
|
||||
assert store.best_justified_checkpoint == new_justified
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import List
|
||||
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, transition_unsigned_block, \
|
||||
build_empty_block
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures
|
||||
from eth2spec.utils.ssz.ssz_typing import Bitlist
|
||||
|
@ -122,10 +123,8 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False):
|
|||
|
||||
|
||||
def add_attestations_to_state(spec, state, attestations, slot):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot = slot
|
||||
block = build_empty_block(spec, state, slot)
|
||||
for attestation in attestations:
|
||||
block.body.attestations.append(attestation)
|
||||
spec.process_slots(state, block.slot)
|
||||
sign_block(spec, state, block)
|
||||
spec.state_transition(state, block)
|
||||
transition_unsigned_block(spec, state, block)
|
||||
|
|
|
@ -2,26 +2,30 @@ from copy import deepcopy
|
|||
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils.bls import bls_sign, only_with_bls
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
|
||||
# Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
|
||||
@only_with_bls()
|
||||
def sign_block(spec, state, block, proposer_index=None):
|
||||
assert state.slot <= block.slot
|
||||
|
||||
def get_proposer_index_maybe(spec, state, slot, proposer_index=None):
|
||||
if proposer_index is None:
|
||||
if block.slot == state.slot:
|
||||
assert state.slot <= slot
|
||||
if slot == state.slot:
|
||||
proposer_index = spec.get_beacon_proposer_index(state)
|
||||
else:
|
||||
if spec.compute_epoch_at_slot(state.slot) + 1 > spec.compute_epoch_at_slot(block.slot):
|
||||
if spec.compute_epoch_at_slot(state.slot) + 1 > spec.compute_epoch_at_slot(slot):
|
||||
print("warning: block slot far away, and no proposer index manually given."
|
||||
" Signing block is slow due to transition for proposer index calculation.")
|
||||
# use stub state to get proposer index of future slot
|
||||
stub_state = deepcopy(state)
|
||||
spec.process_slots(stub_state, block.slot)
|
||||
spec.process_slots(stub_state, slot)
|
||||
proposer_index = spec.get_beacon_proposer_index(stub_state)
|
||||
return proposer_index
|
||||
|
||||
|
||||
@only_with_bls()
|
||||
def apply_randao_reveal(spec, state, block, proposer_index=None):
|
||||
assert state.slot <= block.slot
|
||||
|
||||
proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index)
|
||||
privkey = privkeys[proposer_index]
|
||||
|
||||
block.body.randao_reveal = bls_sign(
|
||||
|
@ -33,8 +37,18 @@ def sign_block(spec, state, block, proposer_index=None):
|
|||
domain_type=spec.DOMAIN_RANDAO,
|
||||
)
|
||||
)
|
||||
block.signature = bls_sign(
|
||||
message_hash=signing_root(block),
|
||||
|
||||
|
||||
# Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
|
||||
@only_with_bls()
|
||||
def apply_sig(spec, state, signed_block, proposer_index=None):
|
||||
block = signed_block.message
|
||||
|
||||
proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index)
|
||||
privkey = privkeys[proposer_index]
|
||||
|
||||
signed_block.signature = bls_sign(
|
||||
message_hash=hash_tree_root(block),
|
||||
privkey=privkey,
|
||||
domain=spec.get_domain(
|
||||
state,
|
||||
|
@ -42,17 +56,26 @@ def sign_block(spec, state, block, proposer_index=None):
|
|||
spec.compute_epoch_at_slot(block.slot)))
|
||||
|
||||
|
||||
def sign_block(spec, state, block, proposer_index=None):
|
||||
signed_block = spec.SignedBeaconBlock(message=block)
|
||||
apply_sig(spec, state, signed_block, proposer_index)
|
||||
return signed_block
|
||||
|
||||
|
||||
def transition_unsigned_block(spec, state, block):
|
||||
spec.process_slots(state, block.slot)
|
||||
spec.process_block(state, block)
|
||||
|
||||
|
||||
def apply_empty_block(spec, state):
|
||||
"""
|
||||
Transition via an empty block (on current slot, assuming no block has been applied yet).
|
||||
:return: the empty block that triggered the transition.
|
||||
"""
|
||||
block = build_empty_block(spec, state, signed=True)
|
||||
spec.state_transition(state, block)
|
||||
return block
|
||||
block = build_empty_block(spec, state)
|
||||
transition_unsigned_block(spec, state, block)
|
||||
|
||||
|
||||
def build_empty_block(spec, state, slot=None, signed=False):
|
||||
def build_empty_block(spec, state, slot=None):
|
||||
if slot is None:
|
||||
slot = state.slot
|
||||
empty_block = spec.BeaconBlock()
|
||||
|
@ -61,13 +84,10 @@ def build_empty_block(spec, state, slot=None, signed=False):
|
|||
previous_block_header = deepcopy(state.latest_block_header)
|
||||
if previous_block_header.state_root == spec.Root():
|
||||
previous_block_header.state_root = state.hash_tree_root()
|
||||
empty_block.parent_root = signing_root(previous_block_header)
|
||||
|
||||
if signed:
|
||||
sign_block(spec, state, empty_block)
|
||||
|
||||
empty_block.parent_root = hash_tree_root(previous_block_header)
|
||||
apply_randao_reveal(spec, state, empty_block)
|
||||
return empty_block
|
||||
|
||||
|
||||
def build_empty_block_for_next_slot(spec, state, signed=False):
|
||||
return build_empty_block(spec, state, state.slot + 1, signed=signed)
|
||||
def build_empty_block_for_next_slot(spec, state):
|
||||
return build_empty_block(spec, state, state.slot + 1)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.utils.bls import bls_sign
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
|
||||
def sign_block_header(spec, state, header, privkey):
|
||||
|
@ -7,8 +7,8 @@ def sign_block_header(spec, state, header, privkey):
|
|||
state=state,
|
||||
domain_type=spec.DOMAIN_BEACON_PROPOSER,
|
||||
)
|
||||
header.signature = bls_sign(
|
||||
message_hash=signing_root(header),
|
||||
return spec.SignedBeaconBlockHeader(message=header, signature=bls_sign(
|
||||
message_hash=hash_tree_root(header),
|
||||
privkey=privkey,
|
||||
domain=domain,
|
||||
)
|
||||
))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from eth2spec.test.helpers.keys import pubkeys, privkeys
|
||||
from eth2spec.utils.bls import bls_sign
|
||||
from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_typing import List
|
||||
|
||||
|
||||
|
@ -26,8 +26,12 @@ def sign_deposit_data(spec, deposit_data, privkey, state=None):
|
|||
spec.DOMAIN_DEPOSIT,
|
||||
)
|
||||
|
||||
deposit_message = spec.DepositMessage(
|
||||
pubkey=deposit_data.pubkey,
|
||||
withdrawal_credentials=deposit_data.withdrawal_credentials,
|
||||
amount=deposit_data.amount)
|
||||
signature = bls_sign(
|
||||
message_hash=signing_root(deposit_data),
|
||||
message_hash=hash_tree_root(deposit_message),
|
||||
privkey=privkey,
|
||||
domain=domain,
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ from eth2spec.utils.bls import (
|
|||
only_with_bls,
|
||||
)
|
||||
from eth2spec.utils.ssz.ssz_impl import (
|
||||
signing_root,
|
||||
hash_tree_root,
|
||||
)
|
||||
|
||||
from .attestations import (
|
||||
|
@ -22,7 +22,7 @@ def sign_shard_block(spec, beacon_state, shard_state, block, proposer_index=None
|
|||
privkey = privkeys[proposer_index]
|
||||
|
||||
block.signature = bls_sign(
|
||||
message_hash=signing_root(block),
|
||||
message_hash=hash_tree_root(block),
|
||||
privkey=privkey,
|
||||
domain=spec.get_domain(
|
||||
beacon_state,
|
||||
|
@ -44,12 +44,12 @@ def build_empty_shard_block(spec,
|
|||
previous_beacon_header = deepcopy(beacon_state.latest_block_header)
|
||||
if previous_beacon_header.state_root == spec.Bytes32():
|
||||
previous_beacon_header.state_root = beacon_state.hash_tree_root()
|
||||
beacon_block_root = spec.signing_root(previous_beacon_header)
|
||||
beacon_block_root = hash_tree_root(previous_beacon_header)
|
||||
|
||||
previous_block_header = deepcopy(shard_state.latest_block_header)
|
||||
if previous_block_header.state_root == spec.Bytes32():
|
||||
previous_block_header.state_root = shard_state.hash_tree_root()
|
||||
parent_root = signing_root(previous_block_header)
|
||||
parent_root = hash_tree_root(previous_block_header)
|
||||
|
||||
block = spec.ShardBlock(
|
||||
shard=shard_state.shard,
|
||||
|
|
|
@ -20,12 +20,16 @@ def get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False):
|
|||
header_2.parent_root = b'\x99' * 32
|
||||
|
||||
if signed_1:
|
||||
sign_block_header(spec, state, header_1, privkey)
|
||||
signed_header_1 = sign_block_header(spec, state, header_1, privkey)
|
||||
else:
|
||||
signed_header_1 = spec.SignedBeaconBlockHeader(message=header_1)
|
||||
if signed_2:
|
||||
sign_block_header(spec, state, header_2, privkey)
|
||||
signed_header_2 = sign_block_header(spec, state, header_2, privkey)
|
||||
else:
|
||||
signed_header_2 = spec.SignedBeaconBlockHeader(message=header_2)
|
||||
|
||||
return spec.ProposerSlashing(
|
||||
proposer_index=validator_index,
|
||||
header_1=header_1,
|
||||
header_2=header_2,
|
||||
signed_header_1=signed_header_1,
|
||||
signed_header_2=signed_header_2,
|
||||
)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from copy import deepcopy
|
||||
|
||||
from eth2spec.test.context import expect_assertion_error
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation
|
||||
from eth2spec.test.helpers.block import sign_block, build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.block import sign_block, build_empty_block_for_next_slot, transition_unsigned_block
|
||||
|
||||
|
||||
def get_balance(state, index):
|
||||
|
@ -40,14 +42,17 @@ def get_state_root(spec, state, slot) -> bytes:
|
|||
return state.state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT]
|
||||
|
||||
|
||||
def state_transition_and_sign_block(spec, state, block):
|
||||
def state_transition_and_sign_block(spec, state, block, expect_fail=False):
|
||||
"""
|
||||
State transition via the provided ``block``
|
||||
then package the block with the state root and signature.
|
||||
then package the block with the correct state root and signature.
|
||||
"""
|
||||
spec.state_transition(state, block)
|
||||
if expect_fail:
|
||||
expect_assertion_error(lambda: transition_unsigned_block(spec, state, block))
|
||||
else:
|
||||
transition_unsigned_block(spec, state, block)
|
||||
block.state_root = state.hash_tree_root()
|
||||
sign_block(spec, state, block)
|
||||
return sign_block(spec, state, block)
|
||||
|
||||
|
||||
def next_epoch_with_attestations(spec,
|
||||
|
@ -57,7 +62,7 @@ def next_epoch_with_attestations(spec,
|
|||
assert state.slot % spec.SLOTS_PER_EPOCH == 0
|
||||
|
||||
post_state = deepcopy(state)
|
||||
blocks = []
|
||||
signed_blocks = []
|
||||
for _ in range(spec.SLOTS_PER_EPOCH):
|
||||
block = build_empty_block_for_next_slot(spec, post_state)
|
||||
if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
|
||||
|
@ -65,17 +70,17 @@ def next_epoch_with_attestations(spec,
|
|||
committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
|
||||
if slot_to_attest >= spec.compute_start_slot_at_epoch(spec.get_current_epoch(post_state)):
|
||||
for index in range(committees_per_slot):
|
||||
cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index)
|
||||
cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
|
||||
block.body.attestations.append(cur_attestation)
|
||||
|
||||
if fill_prev_epoch:
|
||||
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
|
||||
committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
|
||||
for index in range(committees_per_slot):
|
||||
prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index)
|
||||
prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
|
||||
block.body.attestations.append(prev_attestation)
|
||||
|
||||
state_transition_and_sign_block(spec, post_state, block)
|
||||
blocks.append(block)
|
||||
signed_block = state_transition_and_sign_block(spec, post_state, block)
|
||||
signed_blocks.append(signed_block)
|
||||
|
||||
return state, blocks, post_state
|
||||
return state, signed_blocks, post_state
|
||||
|
|
|
@ -1,24 +1,17 @@
|
|||
from eth2spec.utils.bls import bls_sign
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||
|
||||
|
||||
def build_voluntary_exit(spec, state, epoch, validator_index, privkey, signed=False):
|
||||
voluntary_exit = spec.VoluntaryExit(
|
||||
epoch=epoch,
|
||||
validator_index=validator_index,
|
||||
)
|
||||
if signed:
|
||||
sign_voluntary_exit(spec, state, voluntary_exit, privkey)
|
||||
return voluntary_exit
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
|
||||
def sign_voluntary_exit(spec, state, voluntary_exit, privkey):
|
||||
voluntary_exit.signature = bls_sign(
|
||||
message_hash=signing_root(voluntary_exit),
|
||||
privkey=privkey,
|
||||
domain=spec.get_domain(
|
||||
state=state,
|
||||
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
|
||||
message_epoch=voluntary_exit.epoch,
|
||||
return spec.SignedVoluntaryExit(
|
||||
message=voluntary_exit,
|
||||
signature=bls_sign(
|
||||
message_hash=hash_tree_root(voluntary_exit),
|
||||
privkey=privkey,
|
||||
domain=spec.get_domain(
|
||||
state=state,
|
||||
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
|
||||
message_epoch=voluntary_exit.epoch,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
from copy import deepcopy
|
||||
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
sign_block
|
||||
)
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_all_phases
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.state import next_slot
|
||||
|
||||
|
||||
|
@ -37,16 +34,8 @@ def run_block_header_processing(spec, state, block, valid=True):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_success_block_header(spec, state):
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
yield from run_block_header_processing(spec, state, block)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_invalid_sig_block_header(spec, state):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
yield from run_block_header_processing(spec, state, block, valid=False)
|
||||
yield from run_block_header_processing(spec, state, block)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -54,7 +43,6 @@ def test_invalid_sig_block_header(spec, state):
|
|||
def test_invalid_slot_block_header(spec, state):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot = state.slot + 2 # invalid slot
|
||||
sign_block(spec, state, block)
|
||||
|
||||
yield from run_block_header_processing(spec, state, block, valid=False)
|
||||
|
||||
|
@ -64,7 +52,6 @@ def test_invalid_slot_block_header(spec, state):
|
|||
def test_invalid_parent_root(spec, state):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.parent_root = b'\12' * 32 # invalid prev root
|
||||
sign_block(spec, state, block)
|
||||
|
||||
yield from run_block_header_processing(spec, state, block, valid=False)
|
||||
|
||||
|
@ -80,6 +67,6 @@ def test_proposer_slashed(spec, state):
|
|||
# set proposer to slashed
|
||||
state.validators[proposer_index].slashed = True
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
yield from run_block_header_processing(spec, state, block, valid=False)
|
||||
|
|
|
@ -88,8 +88,10 @@ def test_epochs_are_different(spec, state):
|
|||
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
||||
# set slots to be in different epochs
|
||||
proposer_slashing.header_2.slot += spec.SLOTS_PER_EPOCH
|
||||
sign_block_header(spec, state, proposer_slashing.header_2, privkeys[proposer_slashing.proposer_index])
|
||||
header_2 = proposer_slashing.signed_header_2.message
|
||||
header_2.slot += spec.SLOTS_PER_EPOCH
|
||||
proposer_slashing.signed_header_2 = sign_block_header(
|
||||
spec, state, header_2, privkeys[proposer_slashing.proposer_index])
|
||||
|
||||
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
|
||||
|
||||
|
@ -100,7 +102,7 @@ def test_headers_are_same(spec, state):
|
|||
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
||||
# set headers to be the same
|
||||
proposer_slashing.header_2 = proposer_slashing.header_1
|
||||
proposer_slashing.signed_header_2 = proposer_slashing.signed_header_1
|
||||
|
||||
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
|
||||
from eth2spec.test.helpers.keys import pubkey_to_privkey
|
||||
from eth2spec.test.helpers.voluntary_exits import build_voluntary_exit, sign_voluntary_exit
|
||||
from eth2spec.test.helpers.voluntary_exits import sign_voluntary_exit
|
||||
|
||||
|
||||
def run_voluntary_exit_processing(spec, state, voluntary_exit, valid=True):
|
||||
def run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=True):
|
||||
"""
|
||||
Run ``process_voluntary_exit``, yielding:
|
||||
- pre-state ('pre')
|
||||
|
@ -11,19 +11,19 @@ def run_voluntary_exit_processing(spec, state, voluntary_exit, valid=True):
|
|||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
validator_index = voluntary_exit.validator_index
|
||||
validator_index = signed_voluntary_exit.message.validator_index
|
||||
|
||||
yield 'pre', state
|
||||
yield 'voluntary_exit', voluntary_exit
|
||||
yield 'voluntary_exit', signed_voluntary_exit
|
||||
|
||||
if not valid:
|
||||
expect_assertion_error(lambda: spec.process_voluntary_exit(state, voluntary_exit))
|
||||
expect_assertion_error(lambda: spec.process_voluntary_exit(state, signed_voluntary_exit))
|
||||
yield 'post', None
|
||||
return
|
||||
|
||||
pre_exit_epoch = state.validators[validator_index].exit_epoch
|
||||
|
||||
spec.process_voluntary_exit(state, voluntary_exit)
|
||||
spec.process_voluntary_exit(state, signed_voluntary_exit)
|
||||
|
||||
yield 'post', state
|
||||
|
||||
|
@ -41,9 +41,10 @@ def test_success(spec, state):
|
|||
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
|
||||
|
||||
voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey, signed=True)
|
||||
signed_voluntary_exit = sign_voluntary_exit(
|
||||
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
|
||||
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -55,11 +56,14 @@ def test_invalid_signature(spec, state):
|
|||
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
|
||||
|
||||
voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey)
|
||||
voluntary_exit = spec.VoluntaryExit(
|
||||
epoch=current_epoch,
|
||||
validator_index=validator_index,
|
||||
)
|
||||
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, 12345)
|
||||
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -77,14 +81,11 @@ def test_success_exit_queue(spec, state):
|
|||
exit_queue = []
|
||||
for index in initial_indices:
|
||||
privkey = pubkey_to_privkey[state.validators[index].pubkey]
|
||||
exit_queue.append(build_voluntary_exit(
|
||||
spec,
|
||||
state,
|
||||
current_epoch,
|
||||
index,
|
||||
privkey,
|
||||
signed=True,
|
||||
))
|
||||
|
||||
signed_voluntary_exit = sign_voluntary_exit(
|
||||
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=index), privkey)
|
||||
|
||||
exit_queue.append(signed_voluntary_exit)
|
||||
|
||||
# Now run all the exits
|
||||
for voluntary_exit in exit_queue:
|
||||
|
@ -95,18 +96,13 @@ def test_success_exit_queue(spec, state):
|
|||
# exit an additional validator
|
||||
validator_index = spec.get_active_validator_indices(state, current_epoch)[-1]
|
||||
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
|
||||
voluntary_exit = build_voluntary_exit(
|
||||
spec,
|
||||
state,
|
||||
current_epoch,
|
||||
validator_index,
|
||||
privkey,
|
||||
signed=True,
|
||||
)
|
||||
|
||||
signed_voluntary_exit = sign_voluntary_exit(
|
||||
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
|
||||
|
||||
# This is the interesting part of the test: on a pre-state with a full exit queue,
|
||||
# when processing an additional exit, it results in an exit in a later epoch
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
|
||||
|
||||
assert (
|
||||
state.validators[validator_index].exit_epoch ==
|
||||
|
@ -124,18 +120,13 @@ def test_validator_exit_in_future(spec, state):
|
|||
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
|
||||
|
||||
voluntary_exit = build_voluntary_exit(
|
||||
spec,
|
||||
state,
|
||||
current_epoch,
|
||||
validator_index,
|
||||
privkey,
|
||||
signed=False,
|
||||
voluntary_exit = spec.VoluntaryExit(
|
||||
epoch=current_epoch + 1,
|
||||
validator_index=validator_index,
|
||||
)
|
||||
voluntary_exit.epoch += 1
|
||||
sign_voluntary_exit(spec, state, voluntary_exit, privkey)
|
||||
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
|
||||
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -148,18 +139,13 @@ def test_validator_invalid_validator_index(spec, state):
|
|||
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
|
||||
|
||||
voluntary_exit = build_voluntary_exit(
|
||||
spec,
|
||||
state,
|
||||
current_epoch,
|
||||
validator_index,
|
||||
privkey,
|
||||
signed=False,
|
||||
voluntary_exit = spec.VoluntaryExit(
|
||||
epoch=current_epoch,
|
||||
validator_index=len(state.validators),
|
||||
)
|
||||
voluntary_exit.validator_index = len(state.validators)
|
||||
sign_voluntary_exit(spec, state, voluntary_exit, privkey)
|
||||
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
|
||||
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -171,17 +157,10 @@ def test_validator_not_active(spec, state):
|
|||
|
||||
state.validators[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
# build and test voluntary exit
|
||||
voluntary_exit = build_voluntary_exit(
|
||||
spec,
|
||||
state,
|
||||
current_epoch,
|
||||
validator_index,
|
||||
privkey,
|
||||
signed=True,
|
||||
)
|
||||
signed_voluntary_exit = sign_voluntary_exit(
|
||||
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
|
||||
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -197,16 +176,10 @@ def test_validator_already_exited(spec, state):
|
|||
# but validator already has exited
|
||||
state.validators[validator_index].exit_epoch = current_epoch + 2
|
||||
|
||||
voluntary_exit = build_voluntary_exit(
|
||||
spec,
|
||||
state,
|
||||
current_epoch,
|
||||
validator_index,
|
||||
privkey,
|
||||
signed=True,
|
||||
)
|
||||
signed_voluntary_exit = sign_voluntary_exit(
|
||||
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
|
||||
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -216,18 +189,12 @@ def test_validator_not_active_long_enough(spec, state):
|
|||
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
|
||||
|
||||
voluntary_exit = build_voluntary_exit(
|
||||
spec,
|
||||
state,
|
||||
current_epoch,
|
||||
validator_index,
|
||||
privkey,
|
||||
signed=True,
|
||||
)
|
||||
signed_voluntary_exit = sign_voluntary_exit(
|
||||
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
|
||||
|
||||
assert (
|
||||
current_epoch - state.validators[validator_index].activation_epoch <
|
||||
spec.PERSISTENT_COMMITTEE_PERIOD
|
||||
)
|
||||
|
||||
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
|
||||
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
|
||||
|
|
|
@ -140,7 +140,7 @@ def test_skipped_slots(spec, state):
|
|||
assert shard_state.slot == block.slot
|
||||
latest_block_header = deepcopy(shard_state.latest_block_header)
|
||||
latest_block_header.state_root = shard_state.hash_tree_root()
|
||||
assert latest_block_header.signing_root() == block.signing_root()
|
||||
assert latest_block_header.hash_tree_root() == block.hash_tree_root()
|
||||
|
||||
|
||||
@with_all_phases_except(['phase0'])
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
from copy import deepcopy
|
||||
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.bls import bls_sign
|
||||
|
||||
from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block, \
|
||||
transition_unsigned_block
|
||||
from eth2spec.test.helpers.keys import privkeys, pubkeys
|
||||
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
|
||||
from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation
|
||||
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
|
||||
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error, always_bls
|
||||
|
||||
|
||||
@with_all_phases
|
||||
|
@ -20,13 +21,18 @@ def test_prev_slot_block_transition(spec, state):
|
|||
# Go to clean slot
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
# Make a block for it
|
||||
block = build_empty_block(spec, state, slot=state.slot, signed=True)
|
||||
block = build_empty_block(spec, state, slot=state.slot)
|
||||
proposer_index = spec.get_beacon_proposer_index(state)
|
||||
# Transition to next slot, above block will not be invalid on top of new state.
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
yield 'pre', state
|
||||
expect_assertion_error(lambda: state_transition_and_sign_block(spec, state, block))
|
||||
yield 'blocks', [block]
|
||||
# State is beyond block slot, but the block can still be realistic when invalid.
|
||||
# Try the transition, and update the state root to where it is halted. Then sign with the supposed proposer.
|
||||
expect_assertion_error(lambda: transition_unsigned_block(spec, state, block))
|
||||
block.state_root = state.hash_tree_root()
|
||||
signed_block = sign_block(spec, state, block, proposer_index=proposer_index)
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
|
@ -36,13 +42,13 @@ def test_same_slot_block_transition(spec, state):
|
|||
# Same slot on top of pre-state, but move out of slot 0 first.
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
block = build_empty_block(spec, state, slot=state.slot, signed=True)
|
||||
block = build_empty_block(spec, state, slot=state.slot)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
|
||||
|
@ -54,15 +60,15 @@ def test_empty_block_transition(spec, state):
|
|||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert len(state.eth1_data_votes) == pre_eth1_votes + 1
|
||||
assert spec.get_block_root_at_slot(state, pre_slot) == block.parent_root
|
||||
assert spec.get_block_root_at_slot(state, pre_slot) == signed_block.message.parent_root
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Bytes32()
|
||||
|
||||
|
||||
|
@ -73,28 +79,58 @@ def test_invalid_state_root(spec, state):
|
|||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.state_root = b"\xaa" * 32
|
||||
sign_block(spec, state, block)
|
||||
signed_block = sign_block(spec, state, block)
|
||||
|
||||
expect_assertion_error(
|
||||
lambda: spec.state_transition(state, block, validate_state_root=True))
|
||||
expect_assertion_error(lambda: spec.state_transition(state, signed_block))
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_zero_block_sig(spec, state):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
invalid_signed_block = spec.SignedBeaconBlock(message=block)
|
||||
expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block))
|
||||
|
||||
yield 'blocks', [invalid_signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_invalid_block_sig(spec, state):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
invalid_signed_block = spec.SignedBeaconBlock(
|
||||
message=block,
|
||||
signature=bls_sign(
|
||||
message_hash=hash_tree_root(block),
|
||||
privkey=123456,
|
||||
domain=spec.get_domain(
|
||||
state,
|
||||
spec.DOMAIN_BEACON_PROPOSER,
|
||||
spec.compute_epoch_at_slot(block.slot)))
|
||||
)
|
||||
expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block))
|
||||
|
||||
yield 'blocks', [invalid_signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_skipped_slots(spec, state):
|
||||
pre_slot = state.slot
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot += 3
|
||||
sign_block(spec, state, block)
|
||||
block = build_empty_block(spec, state, state.slot + 4)
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.slot == block.slot
|
||||
|
@ -109,13 +145,11 @@ def test_empty_epoch_transition(spec, state):
|
|||
pre_slot = state.slot
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot += spec.SLOTS_PER_EPOCH
|
||||
sign_block(spec, state, block)
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.slot == block.slot
|
||||
|
@ -136,10 +170,10 @@ def test_empty_epoch_transition_not_finalizing(spec, state):
|
|||
yield 'pre', state
|
||||
|
||||
spec.process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH * 5))
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.slot == block.slot
|
||||
|
@ -165,11 +199,10 @@ def test_proposer_slashing(spec, state):
|
|||
#
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.proposer_slashings.append(proposer_slashing)
|
||||
sign_block(spec, state, block)
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
# check if slashed
|
||||
|
@ -199,11 +232,10 @@ def test_attester_slashing(spec, state):
|
|||
#
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.attester_slashings.append(attester_slashing)
|
||||
sign_block(spec, state, block)
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
slashed_validator = state.validators[validator_index]
|
||||
|
@ -229,17 +261,9 @@ def test_expected_deposit_in_block(spec, state):
|
|||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
sign_block(spec, state, block)
|
||||
bad = False
|
||||
try:
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
bad = True
|
||||
except AssertionError:
|
||||
pass
|
||||
if bad:
|
||||
raise AssertionError("expected deposit was not enforced")
|
||||
signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
|
@ -257,11 +281,9 @@ def test_deposit_in_block(spec, state):
|
|||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.deposits.append(deposit)
|
||||
sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert len(state.validators) == initial_registry_len + 1
|
||||
|
@ -285,11 +307,10 @@ def test_deposit_top_up(spec, state):
|
|||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.deposits.append(deposit)
|
||||
sign_block(spec, state, block)
|
||||
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert len(state.validators) == initial_registry_len
|
||||
|
@ -308,23 +329,19 @@ def test_attestation(spec, state):
|
|||
|
||||
# Add to state via block transition
|
||||
pre_current_attestations_len = len(state.current_epoch_attestations)
|
||||
attestation_block = build_empty_block_for_next_slot(spec, state)
|
||||
attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
attestation_block = build_empty_block(spec, state, state.slot + 1 + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
||||
attestation_block.body.attestations.append(attestation)
|
||||
sign_block(spec, state, attestation_block)
|
||||
state_transition_and_sign_block(spec, state, attestation_block)
|
||||
signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block)
|
||||
|
||||
assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1
|
||||
|
||||
# Epoch transition should move to previous_epoch_attestations
|
||||
pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations)
|
||||
|
||||
epoch_block = build_empty_block_for_next_slot(spec, state)
|
||||
epoch_block.slot += spec.SLOTS_PER_EPOCH
|
||||
sign_block(spec, state, epoch_block)
|
||||
state_transition_and_sign_block(spec, state, epoch_block)
|
||||
epoch_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_epoch_block = state_transition_and_sign_block(spec, state, epoch_block)
|
||||
|
||||
yield 'blocks', [attestation_block, epoch_block]
|
||||
yield 'blocks', [signed_attestation_block, signed_epoch_block]
|
||||
yield 'post', state
|
||||
|
||||
assert len(state.current_epoch_attestations) == 0
|
||||
|
@ -348,30 +365,30 @@ def test_voluntary_exit(spec, state):
|
|||
epoch=spec.get_current_epoch(state),
|
||||
validator_index=validator_index,
|
||||
)
|
||||
voluntary_exit.signature = bls_sign(
|
||||
message_hash=signing_root(voluntary_exit),
|
||||
privkey=privkeys[validator_index],
|
||||
domain=spec.get_domain(
|
||||
state=state,
|
||||
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
|
||||
signed_voluntary_exit = spec.SignedVoluntaryExit(
|
||||
message=voluntary_exit,
|
||||
signature=bls_sign(
|
||||
message_hash=hash_tree_root(voluntary_exit),
|
||||
privkey=privkeys[validator_index],
|
||||
domain=spec.get_domain(
|
||||
state=state,
|
||||
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Add to state via block transition
|
||||
initiate_exit_block = build_empty_block_for_next_slot(spec, state)
|
||||
initiate_exit_block.body.voluntary_exits.append(voluntary_exit)
|
||||
sign_block(spec, state, initiate_exit_block)
|
||||
state_transition_and_sign_block(spec, state, initiate_exit_block)
|
||||
initiate_exit_block.body.voluntary_exits.append(signed_voluntary_exit)
|
||||
signed_initiate_exit_block = state_transition_and_sign_block(spec, state, initiate_exit_block)
|
||||
|
||||
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
# Process within epoch transition
|
||||
exit_block = build_empty_block_for_next_slot(spec, state)
|
||||
exit_block.slot += spec.SLOTS_PER_EPOCH
|
||||
sign_block(spec, state, exit_block)
|
||||
state_transition_and_sign_block(spec, state, exit_block)
|
||||
exit_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_exit_block = state_transition_and_sign_block(spec, state, exit_block)
|
||||
|
||||
yield 'blocks', [initiate_exit_block, exit_block]
|
||||
yield 'blocks', [signed_initiate_exit_block, signed_exit_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
@ -391,12 +408,10 @@ def test_balance_driven_status_transitions(spec, state):
|
|||
yield 'pre', state
|
||||
|
||||
# trigger epoch transition
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot += spec.SLOTS_PER_EPOCH
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
@ -410,11 +425,10 @@ def test_historical_batch(spec, state):
|
|||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.slot == block.slot
|
||||
|
@ -430,7 +444,6 @@ def test_eth1_data_votes_consensus(spec, state):
|
|||
return
|
||||
|
||||
offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
|
||||
sign_block(spec, state, offset_block)
|
||||
state_transition_and_sign_block(spec, state, offset_block)
|
||||
yield 'pre', state
|
||||
|
||||
|
@ -444,9 +457,8 @@ def test_eth1_data_votes_consensus(spec, state):
|
|||
block = build_empty_block_for_next_slot(spec, state)
|
||||
# wait for over 50% for A, then start voting B
|
||||
block.body.eth1_data.block_hash = b if i * 2 > spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(signed_block)
|
||||
|
||||
assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
|
||||
assert state.eth1_data.block_hash == a
|
||||
|
@ -454,9 +466,8 @@ def test_eth1_data_votes_consensus(spec, state):
|
|||
# transition to next eth1 voting period
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.eth1_data.block_hash = c
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(signed_block)
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', state
|
||||
|
@ -477,7 +488,6 @@ def test_eth1_data_votes_no_consensus(spec, state):
|
|||
pre_eth1_hash = state.eth1_data.block_hash
|
||||
|
||||
offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
|
||||
sign_block(spec, state, offset_block)
|
||||
state_transition_and_sign_block(spec, state, offset_block)
|
||||
yield 'pre', state
|
||||
|
||||
|
@ -490,9 +500,8 @@ def test_eth1_data_votes_no_consensus(spec, state):
|
|||
block = build_empty_block_for_next_slot(spec, state)
|
||||
# wait for precisely 50% for A, then start voting B for other 50%
|
||||
block.body.eth1_data.block_hash = b if i * 2 >= spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
|
||||
sign_block(spec, state, block)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(block)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(signed_block)
|
||||
|
||||
assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
|
||||
assert state.eth1_data.block_hash == pre_eth1_hash
|
||||
|
|
|
@ -155,10 +155,3 @@ def hash_tree_root(obj: SSZValue):
|
|||
return mix_in_length(merkleize_chunks(leaves, limit=chunk_count(obj.type())), len(obj))
|
||||
else:
|
||||
return merkleize_chunks(leaves)
|
||||
|
||||
|
||||
def signing_root(obj: Container):
|
||||
# ignore last field
|
||||
fields = [field for field in obj][:-1]
|
||||
leaves = [hash_tree_root(f) for f in fields]
|
||||
return merkleize_chunks(chunkify(b''.join(leaves)))
|
||||
|
|
|
@ -156,10 +156,6 @@ class Container(Series, metaclass=SSZType):
|
|||
from .ssz_impl import hash_tree_root
|
||||
return hash_tree_root(self)
|
||||
|
||||
def signing_root(self):
|
||||
from .ssz_impl import signing_root
|
||||
return signing_root(self)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name not in self.__class__.__annotations__:
|
||||
raise AttributeError("Cannot change non-existing SSZ-container attribute")
|
||||
|
|
Loading…
Reference in New Issue