Skip to content

Commit

Permalink
Merge pull request #708 from ethereum/vbuterin-patch-5
Browse files Browse the repository at this point in the history
Convert transaction descriptions from English to python code
  • Loading branch information
djrtwo committed Mar 2, 2019
2 parents 8df7de1 + fa327a1 commit b1fc54a
Showing 1 changed file with 166 additions and 52 deletions.
218 changes: 166 additions & 52 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -1629,59 +1629,123 @@ Below are the processing steps that happen at every `block`.

Verify that `len(block.body.proposer_slashings) <= MAX_PROPOSER_SLASHINGS`.

For each `proposer_slashing` in `block.body.proposer_slashings`:
For each `proposer_slashing` in `block.body.proposer_slashings`, run the following function:

* Let `proposer = state.validator_registry[proposer_slashing.proposer_index]`.
* Verify that `proposer_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot`.
* Verify that `proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard`.
* Verify that `proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root`.
* Verify that `proposer.slashed is False`.
* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_1, "signature"), signature=proposer_slashing.proposal_1.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_1.slot), DOMAIN_PROPOSAL))`.
* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_2, "signature"), signature=proposer_slashing.proposal_2.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_2.slot), DOMAIN_PROPOSAL))`.
* Run `slash_validator(state, proposer_slashing.proposer_index)`.
```python
def process_proposer_slashing(state: BeaconState,
proposer_slashing: ProposerSlashing) -> None:
"""
Process ``ProposerSlashing`` transaction.
Note that this function mutates ``state``.
"""
proposer = state.validator_registry[proposer_slashing.proposer_index]
# Verify that the slot is the same
assert proposer_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot
# Verify that the shard is the same (or that both proposals are beacon chain proposals)
assert proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard
# But the roots are different!
assert proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root
# Proposer is not yet slashed
assert proposer.slashed is False
# Signatures are valid
for proposal in (proposer_slashing.proposal_1, proposer_slashing.proposal_2):
assert bls_verify(
pubkey=proposer.pubkey,
message_hash=signed_root(proposal, "signature"),
signature=proposal.signature,
domain=get_domain(state.fork, slot_to_epoch(proposal.slot), DOMAIN_PROPOSAL)
)
slash_validator(state, proposer_slashing.proposer_index)
```

##### Attester slashings

Verify that `len(block.body.attester_slashings) <= MAX_ATTESTER_SLASHINGS`.

For each `attester_slashing` in `block.body.attester_slashings`:
For each `attester_slashing` in `block.body.attester_slashings`, run the following function:

* Let `slashable_attestation_1 = attester_slashing.slashable_attestation_1`.
* Let `slashable_attestation_2 = attester_slashing.slashable_attestation_2`.
* Verify that `slashable_attestation_1.data != slashable_attestation_2.data`.
* Verify that `is_double_vote(slashable_attestation_1.data, slashable_attestation_2.data)` or `is_surround_vote(slashable_attestation_1.data, slashable_attestation_2.data)`.
* Verify that `verify_slashable_attestation(state, slashable_attestation_1)`.
* Verify that `verify_slashable_attestation(state, slashable_attestation_2)`.
* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].slashed is False]`.
* Verify that `len(slashable_indices) >= 1`.
* Run `slash_validator(state, index)` for each `index` in `slashable_indices`.
```python
def process_attester_slashing(state: BeaconState,
attester_slashing: AttesterSlashing) -> None:
"""
Process ``AttesterSlashing`` transaction.
Note that this function mutates ``state``.
"""
attestation1 = attester_slashing.slashable_attestation_1
attestation2 = attester_slashing.slashable_attestation_2
# Check that the attestations are conflicting
assert attestation1.data != attestation2.data
assert (
is_double_vote(attestation1.data, attestation2.data) or
is_surround_vote(attestation1.data, attestation2.data)
)
assert verify_slashable_attestation(state, attestation1)
assert verify_slashable_attestation(state, attestation2)
slashable_indices = [
index for index in attestation1.validator_indices
if (
index in attestation2.validator_indices and
state.validator_registry[index].slashed is False
)
]
assert len(slashable_indices) >= 1
for index in slashable_indices:
slash_validator(state, index)
```

##### Attestations

Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`.

For each `attestation` in `block.body.attestations`:

* Verify that `attestation.data.slot >= GENESIS_SLOT`.
* Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`.
* Verify that `state.slot < attestation.data.slot + SLOTS_PER_EPOCH.
* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if slot_to_epoch(attestation.data.slot + 1) >= get_current_epoch(state) else state.previous_justified_epoch`.
* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`.
* Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(crosslink_data_root=attestation.data.crosslink_data_root, epoch=slot_to_epoch(attestation.data.slot))`.
* Verify bitfields and aggregate signature:
For each `attestation` in `block.body.attestations`, run the following function:

```python
assert attestation.custody_bitfield == b'\x00' * len(attestation.custody_bitfield) # [TO BE REMOVED IN PHASE 1]
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
"""
Process ``Attestation`` transaction.
Note that this function mutates ``state``.
"""
# Can't submit attestations that are too far in history (or in prehistory)
assert attestation.data.slot >= GENESIS_SLOT
assert state.slot < attestation.data.slot + SLOTS_PER_EPOCH
# Can't submit attestations too quickly
assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot
# Verify that the justified epoch is correct, case 1: current epoch attestations
if slot_to_epoch(attestation.data.slot + 1) >= get_current_epoch(state):
assert attestation.data.justified_epoch == state.justified_epoch
# Case 2: previous epoch attestations
else:
assert attestation.data.justified_epoch == state.previous_justified_epoch
# Check that the justified block root is correct
assert attestation.data.justified_block_root == get_block_root(
state, get_epoch_start_slot(attestation.data.justified_epoch)
)
# Check that the crosslink data is valid
acceptable_crosslink_data = {
# Case 1: Latest crosslink matches the one in the state
attestation.data.latest_crosslink,
# Case 2: State has already been updated, state's latest crosslink matches the crosslink
# the attestation is trying to create
Crosslink(
crosslink_data_root=attestation.data.crosslink_data_root,
epoch=slot_to_epoch(attestation.data.slot)
)
}
assert state.latest_crosslinks[attestation.data.shard] in acceptable_crosslink_data
# Attestation must be nonempty!
assert attestation.aggregation_bitfield != b'\x00' * len(attestation.aggregation_bitfield)

# Custody must be empty (to be removed in phase 1)
assert attestation.custody_bitfield == b'\x00' * len(attestation.custody_bitfield)
# Get the committee for the specific shard that this attestation is for
crosslink_committee = [
committee for committee, shard in get_crosslink_committees_at_slot(state, attestation.data.slot)
if shard == attestation.data.shard
][0]
# Custody bitfield must be a subset of the attestation bitfield
for i in range(len(crosslink_committee)):
if get_bitfield_bit(attestation.aggregation_bitfield, i) == 0b0:
assert get_bitfield_bit(attestation.custody_bitfield, i) == 0b0

# Verify aggregate signature
participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
custody_bit_1_participants = get_attestation_participants(state, attestation.data, attestation.custody_bitfield)
custody_bit_0_participants = [i in participants for i not in custody_bit_1_participants]
Expand All @@ -1698,11 +1762,17 @@ For each `attestation` in `block.body.attestations`:
signature=attestation.aggregate_signature,
domain=get_domain(state.fork, slot_to_epoch(attestation.data.slot), DOMAIN_ATTESTATION),
)
# Crosslink data root is zero (to be removed in phase 1)
assert attestation.data.crosslink_data_root == ZERO_HASH
# Apply the attestation
state.latest_attestations.append(PendingAttestation(
data=attestation.data,
aggregation_bitfield=attestation.aggregation_bitfield,
custody_bitfield=attestation.custody_bitfield,
inclusion_slot=state.slot)
)
```

* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.crosslink_data_root == ZERO_HASH`.
* Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, inclusion_slot=state.slot)` to `state.latest_attestations`.

##### Deposits

Verify that `len(block.body.deposits) <= MAX_DEPOSITS`.
Expand Down Expand Up @@ -1741,32 +1811,76 @@ process_deposit(state, deposit)

Verify that `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`.

For each `exit` in `block.body.voluntary_exits`:
For each `exit` in `block.body.voluntary_exits`, run the following function:

* Let `validator = state.validator_registry[exit.validator_index]`.
* Verify that `validator.exit_epoch > get_delayed_activation_exit_epoch(get_current_epoch(state))`.
* Verify that `get_current_epoch(state) >= exit.epoch`.
* Verify that `bls_verify(pubkey=validator.pubkey, message_hash=signed_root(exit, "signature"), signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`.
* Run `initiate_validator_exit(state, exit.validator_index)`.
```python
def process_exit(state: BeaconState, exit: VoluntaryExit) -> None:
"""
Process ``VoluntaryExit`` transaction.
Note that this function mutates ``state``.
"""
validator = state.validator_registry[exit.validator_index]
# Verify the validator has not yet exited
assert validator.exit_epoch > get_delayed_activation_exit_epoch(get_current_epoch(state))
# Exits must specify an epoch when they become valid; they are not valid before then
assert get_current_epoch(state) >= exit.epoch
# Verify signature
assert bls_verify(
pubkey=validator.pubkey,
message_hash=signed_root(exit, "signature"),
signature=exit.signature,
domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT)
)
# Run the exit
initiate_validator_exit(state, exit.validator_index)
```

##### Transfers

Note: Transfers are a temporary functionality for phases 0 and 1, to be removed in phase 2.

Verify that `len(block.body.transfers) <= MAX_TRANSFERS` and that all transfers are distinct.

For each `transfer` in `block.body.transfers`:

* Verify that `state.validator_balances[transfer.from] >= transfer.amount`.
* Verify that `state.validator_balances[transfer.from] >= transfer.fee`.
* Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`.
* Verify that `state.slot == transfer.slot`.
* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch` or `state.validator_registry[transfer.from].activation_epoch == FAR_FUTURE_EPOCH`.
* Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`.
* Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=signed_root(transfer, "signature"), signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`.
* Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`.
* Set `state.validator_balances[transfer.to] += transfer.amount`.
* Set `state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee`.
For each `transfer` in `block.body.transfers`, run the following function:

```python
def process_transfer(state: BeaconState, transfer: Transfer) -> None:
"""
Process ``Transfer`` transaction.
Note that this function mutates ``state``.
"""
# Verify the amount and fee aren't individually too big (for anti-overflow purposes)
assert state.validator_balances[transfer.from] >= max(transfer.amount, transfer.fee)
# Verify that we have enough ETH to send, and that after the transfer the balance will be either
# exactly zero or at least MIN_DEPOSIT_AMOUNT
assert (
state.validator_balances[transfer.from] == transfer.amount + transfer.fee or
state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
)
# A transfer is valid in only one slot
assert state.slot == transfer.slot
# Only withdrawn or not-yet-deposited accounts can transfer
assert (
get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch or
state.validator_registry[transfer.from].activation_epoch == FAR_FUTURE_EPOCH
)
# Verify that the pubkey is valid
assert (
state.validator_registry[transfer.from].withdrawal_credentials ==
BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]
)
# Verify that the signature is valid
assert bls_verify(
pubkey=transfer.pubkey,
message_hash=signed_root(transfer, "signature"),
signature=transfer.signature,
domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER)
)
# Process the transfer
state.validator_balances[transfer.from] -= transfer.amount + transfer.fee
state.validator_balances[transfer.to] += transfer.amount
state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee
```

### Per-epoch processing

Expand Down

0 comments on commit b1fc54a

Please sign in to comment.