Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport V09x #1505

Merged
merged 9 commits into from
Dec 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deposit_contract/contracts/validator_registration.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions deposit_contract/contracts/validator_registration.v.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Vyper target 0.1.0b12
# Vyper target 0.1.0b13
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
MAX_DEPOSIT_COUNT: constant(uint256) = 4294967295 # 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1
Expand Down Expand Up @@ -75,7 +75,7 @@ def deposit(pubkey: bytes[PUBKEY_LENGTH],
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
assert deposit_amount >= MIN_DEPOSIT_AMOUNT

# Length checks to facilitate formal verification (see https://github.com/ethereum/eth2.0-specs/pull/1362/files#r320361859)
# Length checks for safety
assert len(pubkey) == PUBKEY_LENGTH
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
assert len(signature) == SIGNATURE_LENGTH
Expand Down
2 changes: 1 addition & 1 deletion deposit_contract/requirements-testing.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
eth-tester[py-evm]==0.1.0b39
vyper==0.1.0b12
vyper==0.1.0b13
web3==5.0.0b2
pytest==3.6.1
../test_libs/pyspec
6 changes: 1 addition & 5 deletions scripts/build_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@
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,
Expand Down Expand Up @@ -50,7 +47,6 @@

from eth2spec.utils.ssz.ssz_impl import (
hash_tree_root,
signing_root,
is_zero,
)
from eth2spec.utils.ssz.ssz_typing import (
Expand Down
115 changes: 80 additions & 35 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
- [`PendingAttestation`](#pendingattestation)
- [`Eth1Data`](#eth1data)
- [`HistoricalBatch`](#historicalbatch)
- [`DepositMessage`](#depositmessage)
- [`DepositData`](#depositdata)
- [`BeaconBlockHeader`](#beaconblockheader)
- [Beacon operations](#beacon-operations)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -342,14 +346,23 @@ 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
class DepositData(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
signature: BLSSignature
signature: BLSSignature # signing over DepositMessage
```

#### `BeaconBlockHeader`
Expand All @@ -360,7 +373,6 @@ class BeaconBlockHeader(Container):
parent_root: Root
state_root: Root
body_root: Root
signature: BLSSignature
```

### Beacon operations
Expand All @@ -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`
Expand Down Expand Up @@ -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
Expand All @@ -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`
Expand All @@ -433,7 +444,6 @@ class BeaconBlock(Container):
parent_root: Root
state_root: Root
body: BeaconBlockBody
signature: BLSSignature
```

### Beacon state
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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
Expand All @@ -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
```

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
```
Expand Down Expand Up @@ -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
Expand All @@ -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)
```
17 changes: 8 additions & 9 deletions specs/core/0_fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*:
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -251,8 +252,6 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
# Use GENESIS_EPOCH for previous when genesis to avoid underflow
previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH
assert target.epoch in [current_epoch, previous_epoch]
# Cannot calculate the current shuffling if have not seen the target
assert target.root in store.blocks

# Attestations target be for a known block. If target block is unknown, delay consideration until the block is found
assert target.root in store.blocks
Expand Down
3 changes: 2 additions & 1 deletion specs/core/1_custody-game.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions specs/core/1_shard-data-chains.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
Loading