Skip to content

Commit

Permalink
Merge pull request #1491 from ethereum/rm-signing-root
Browse files Browse the repository at this point in the history
rm signing root (fixes #1487)
  • Loading branch information
djrtwo authored Dec 5, 2019
2 parents e9dc2a1 + afb9a1d commit 7af6945
Show file tree
Hide file tree
Showing 30 changed files with 491 additions and 466 deletions.
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)
```
15 changes: 8 additions & 7 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
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

0 comments on commit 7af6945

Please sign in to comment.