Merge branch 'dev' into vbuterin-patch-3

This commit is contained in:
Hsiao-Wei Wang 2019-03-15 11:22:15 +08:00
commit 16123685af
No known key found for this signature in database
GPG Key ID: 95B070122902DEA4
4 changed files with 249 additions and 130 deletions

116
LICENSE Normal file
View File

@ -0,0 +1,116 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

View File

@ -61,9 +61,9 @@
- [`is_active_validator`](#is_active_validator) - [`is_active_validator`](#is_active_validator)
- [`get_active_validator_indices`](#get_active_validator_indices) - [`get_active_validator_indices`](#get_active_validator_indices)
- [`get_balance`](#get_balance) - [`get_balance`](#get_balance)
- [`set_balance`](#set_balance) - [`set_balance`](#set_balance)
- [`increase_balance`](#increase_balance) - [`increase_balance`](#increase_balance)
- [`decrease_balance`](#decrease_balance) - [`decrease_balance`](#decrease_balance)
- [`get_permuted_index`](#get_permuted_index) - [`get_permuted_index`](#get_permuted_index)
- [`split`](#split) - [`split`](#split)
- [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_epoch_committee_count`](#get_epoch_committee_count)
@ -119,7 +119,7 @@
- [Helper functions](#helper-functions-1) - [Helper functions](#helper-functions-1)
- [Justification](#justification) - [Justification](#justification)
- [Crosslinks](#crosslinks) - [Crosslinks](#crosslinks)
- [Eth1 data](#eth1-data-1) - [Eth1 data](#eth1-data)
- [Rewards and penalties](#rewards-and-penalties) - [Rewards and penalties](#rewards-and-penalties)
- [Justification and finalization](#justification-and-finalization) - [Justification and finalization](#justification-and-finalization)
- [Crosslinks](#crosslinks-1) - [Crosslinks](#crosslinks-1)
@ -132,7 +132,7 @@
- [Per-block processing](#per-block-processing) - [Per-block processing](#per-block-processing)
- [Block header](#block-header) - [Block header](#block-header)
- [RANDAO](#randao) - [RANDAO](#randao)
- [Eth1 data](#eth1-data) - [Eth1 data](#eth1-data-1)
- [Transactions](#transactions) - [Transactions](#transactions)
- [Proposer slashings](#proposer-slashings) - [Proposer slashings](#proposer-slashings)
- [Attester slashings](#attester-slashings) - [Attester slashings](#attester-slashings)
@ -294,9 +294,9 @@ The types are defined topologically to aid in facilitating an executable version
```python ```python
{ {
# Previous fork version # Previous fork version
'previous_version': 'uint64', 'previous_version': 'bytes4',
# Current fork version # Current fork version
'current_version': 'uint64', 'current_version': 'bytes4',
# Fork epoch number # Fork epoch number
'epoch': 'uint64', 'epoch': 'uint64',
} }
@ -339,22 +339,19 @@ The types are defined topologically to aid in facilitating an executable version
```python ```python
{ {
# Slot number # LMD GHOST vote
'slot': 'uint64', 'slot': 'uint64',
# Shard number
'shard': 'uint64',
# Root of the signed beacon block
'beacon_block_root': 'bytes32', 'beacon_block_root': 'bytes32',
# Root of the ancestor at the epoch boundary
'epoch_boundary_root': 'bytes32', # FFG vote
# Data from the shard since the last attestation 'source_epoch': 'uint64',
'source_root': 'bytes32',
'target_root': 'bytes32',
# Crosslink vote
'shard': 'uint64',
'previous_crosslink': Crosslink,
'crosslink_data_root': 'bytes32', 'crosslink_data_root': 'bytes32',
# Last crosslink
'latest_crosslink': Crosslink,
# Last justified epoch in the beacon state
'justified_epoch': 'uint64',
# Hash of the last justified beacon block
'justified_block_root': 'bytes32',
} }
``` ```
@ -617,9 +614,12 @@ The types are defined topologically to aid in facilitating an executable version
'previous_epoch_attestations': [PendingAttestation], 'previous_epoch_attestations': [PendingAttestation],
'current_epoch_attestations': [PendingAttestation], 'current_epoch_attestations': [PendingAttestation],
'previous_justified_epoch': 'uint64', 'previous_justified_epoch': 'uint64',
'justified_epoch': 'uint64', 'current_justified_epoch': 'uint64',
'previous_justified_root': 'bytes32',
'current_justified_root': 'bytes32',
'justification_bitfield': 'uint64', 'justification_bitfield': 'uint64',
'finalized_epoch': 'uint64', 'finalized_epoch': 'uint64',
'finalized_root': 'bytes32',
# Recent state # Recent state
'latest_crosslinks': [Crosslink, SHARD_COUNT], 'latest_crosslinks': [Crosslink, SHARD_COUNT],
@ -1052,7 +1052,7 @@ def get_beacon_proposer_index(state: BeaconState,
assert previous_epoch <= epoch <= next_epoch assert previous_epoch <= epoch <= next_epoch
first_committee, _ = get_crosslink_committees_at_slot(state, slot, registry_change)[0] first_committee, _ = get_crosslink_committees_at_slot(state, slot, registry_change)[0]
return first_committee[slot % len(first_committee)] return first_committee[epoch % len(first_committee)]
``` ```
### `verify_merkle_branch` ### `verify_merkle_branch`
@ -1143,7 +1143,7 @@ def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> G
```python ```python
def get_fork_version(fork: Fork, def get_fork_version(fork: Fork,
epoch: Epoch) -> int: epoch: Epoch) -> bytes:
""" """
Return the fork version of the given ``epoch``. Return the fork version of the given ``epoch``.
""" """
@ -1162,8 +1162,7 @@ def get_domain(fork: Fork,
""" """
Get the domain number that represents the fork meta and signature domain. Get the domain number that represents the fork meta and signature domain.
""" """
fork_version = get_fork_version(fork, epoch) return bytes_to_int(get_fork_version(fork, epoch) + int_to_bytes4(domain_type))
return fork_version * 2**32 + domain_type
``` ```
### `get_bitfield_bit` ### `get_bitfield_bit`
@ -1260,8 +1259,8 @@ def is_surround_vote(attestation_data_1: AttestationData,
""" """
Check if ``attestation_data_1`` surrounds ``attestation_data_2``. Check if ``attestation_data_1`` surrounds ``attestation_data_2``.
""" """
source_epoch_1 = attestation_data_1.justified_epoch source_epoch_1 = attestation_data_1.source_epoch
source_epoch_2 = attestation_data_2.justified_epoch source_epoch_2 = attestation_data_2.source_epoch
target_epoch_1 = slot_to_epoch(attestation_data_1.slot) target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
target_epoch_2 = slot_to_epoch(attestation_data_2.slot) target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
@ -1342,27 +1341,26 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# object, and we need to be able to skip over it # object, and we need to be able to skip over it
state.deposit_index += 1 state.deposit_index += 1
# Verify the proof of possession
proof_is_valid = bls_verify(
pubkey=deposit_input.pubkey,
message_hash=signed_root(deposit_input),
signature=deposit_input.proof_of_possession,
domain=get_domain(
state.fork,
get_current_epoch(state),
DOMAIN_DEPOSIT,
)
)
if not proof_is_valid:
return
validator_pubkeys = [v.pubkey for v in state.validator_registry] validator_pubkeys = [v.pubkey for v in state.validator_registry]
pubkey = deposit_input.pubkey pubkey = deposit_input.pubkey
amount = deposit.deposit_data.amount amount = deposit.deposit_data.amount
withdrawal_credentials = deposit_input.withdrawal_credentials withdrawal_credentials = deposit_input.withdrawal_credentials
if pubkey not in validator_pubkeys: if pubkey not in validator_pubkeys:
# Verify the proof of possession
proof_is_valid = bls_verify(
pubkey=deposit_input.pubkey,
message_hash=signed_root(deposit_input),
signature=deposit_input.proof_of_possession,
domain=get_domain(
state.fork,
get_current_epoch(state),
DOMAIN_DEPOSIT,
)
)
if not proof_is_valid:
return
# Add new validator # Add new validator
validator = Validator( validator = Validator(
pubkey=pubkey, pubkey=pubkey,
@ -1382,7 +1380,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
else: else:
# Increase balance by deposit amount # Increase balance by deposit amount
index = validator_pubkeys.index(pubkey) index = validator_pubkeys.index(pubkey)
assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials
increase_balance(state, index, amount) increase_balance(state, index, amount)
``` ```
@ -1560,8 +1557,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
slot=GENESIS_SLOT, slot=GENESIS_SLOT,
genesis_time=genesis_time, genesis_time=genesis_time,
fork=Fork( fork=Fork(
previous_version=GENESIS_FORK_VERSION, previous_version=int_to_bytes4(GENESIS_FORK_VERSION),
current_version=GENESIS_FORK_VERSION, current_version=int_to_bytes4(GENESIS_FORK_VERSION),
epoch=GENESIS_EPOCH, epoch=GENESIS_EPOCH,
), ),
@ -1583,9 +1580,12 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
previous_epoch_attestations=[], previous_epoch_attestations=[],
current_epoch_attestations=[], current_epoch_attestations=[],
previous_justified_epoch=GENESIS_EPOCH, previous_justified_epoch=GENESIS_EPOCH,
justified_epoch=GENESIS_EPOCH, current_justified_epoch=GENESIS_EPOCH,
previous_justified_root=ZERO_HASH,
current_justified_root=ZERO_HASH,
justification_bitfield=0, justification_bitfield=0,
finalized_epoch=GENESIS_EPOCH, finalized_epoch=GENESIS_EPOCH,
finalized_root=ZERO_HASH,
# Recent state # Recent state
latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)],
@ -1762,7 +1762,7 @@ def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestat
``` ```
```python ```python
def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> Gwei:
return get_total_balance(state, get_attesting_indices(state, attestations)) return get_total_balance(state, get_attesting_indices(state, attestations))
``` ```
@ -1770,7 +1770,7 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat
def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]:
return [ return [
a for a in state.current_epoch_attestations a for a in state.current_epoch_attestations
if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state))) if a.data.target_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state)))
] ]
``` ```
@ -1778,7 +1778,7 @@ def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingA
def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]:
return [ return [
a for a in state.previous_epoch_attestations a for a in state.previous_epoch_attestations
if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))) if a.data.target_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state)))
] ]
``` ```
@ -1796,7 +1796,7 @@ def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[Pe
def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]: def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]:
all_attestations = state.current_epoch_attestations + state.previous_epoch_attestations all_attestations = state.current_epoch_attestations + state.previous_epoch_attestations
valid_attestations = [ valid_attestations = [
a for a in all_attestations if a.data.latest_crosslink == state.latest_crosslinks[shard] a for a in all_attestations if a.data.previous_crosslink == state.latest_crosslinks[shard]
] ]
all_roots = [a.data.crosslink_data_root for a in valid_attestations] all_roots = [a.data.crosslink_data_root for a in valid_attestations]
@ -1839,7 +1839,9 @@ Run the following function:
```python ```python
def update_justification_and_finalization(state: BeaconState) -> None: def update_justification_and_finalization(state: BeaconState) -> None:
new_justified_epoch = state.justified_epoch new_justified_epoch = state.current_justified_epoch
new_finalized_epoch = state.finalized_epoch
# Rotate the justification bitfield up one epoch to make room for the current epoch # Rotate the justification bitfield up one epoch to make room for the current epoch
state.justification_bitfield <<= 1 state.justification_bitfield <<= 1
# If the previous epoch gets justified, fill the second last bit # If the previous epoch gets justified, fill the second last bit
@ -1858,20 +1860,26 @@ def update_justification_and_finalization(state: BeaconState) -> None:
current_epoch = get_current_epoch(state) current_epoch = get_current_epoch(state)
# The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source # The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source
if (bitfield >> 1) % 8 == 0b111 and state.previous_justified_epoch == current_epoch - 3: if (bitfield >> 1) % 8 == 0b111 and state.previous_justified_epoch == current_epoch - 3:
state.finalized_epoch = state.previous_justified_epoch new_finalized_epoch = state.previous_justified_epoch
# The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source # The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source
if (bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == current_epoch - 2: if (bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == current_epoch - 2:
state.finalized_epoch = state.previous_justified_epoch new_finalized_epoch = state.previous_justified_epoch
# The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source # The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source
if (bitfield >> 0) % 8 == 0b111 and state.justified_epoch == current_epoch - 2: if (bitfield >> 0) % 8 == 0b111 and state.current_justified_epoch == current_epoch - 2:
state.finalized_epoch = state.justified_epoch new_finalized_epoch = state.current_justified_epoch
# The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source # The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source
if (bitfield >> 0) % 4 == 0b11 and state.justified_epoch == current_epoch - 1: if (bitfield >> 0) % 4 == 0b11 and state.current_justified_epoch == current_epoch - 1:
state.finalized_epoch = state.justified_epoch new_finalized_epoch = state.current_justified_epoch
# Rotate justified epochs # Update state jusification/finality fields
state.previous_justified_epoch = state.justified_epoch state.previous_justified_epoch = state.current_justified_epoch
state.justified_epoch = new_justified_epoch state.previous_justified_root = state.current_justified_root
if new_justified_epoch != state.current_justified_epoch:
state.current_justified_epoch = new_justified_epoch
state.current_justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch))
if new_finalized_epoch != state.finalized_epoch:
state.finalized_epoch = new_finalized_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(new_finalized_epoch))
``` ```
#### Crosslinks #### Crosslinks
@ -2346,8 +2354,8 @@ def process_proposer_slashing(state: BeaconState,
Note that this function mutates ``state``. Note that this function mutates ``state``.
""" """
proposer = state.validator_registry[proposer_slashing.proposer_index] proposer = state.validator_registry[proposer_slashing.proposer_index]
# Verify that the slot is the same # Verify that the epoch is the same
assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot)
# But the headers are different # But the headers are different
assert proposer_slashing.header_1 != proposer_slashing.header_2 assert proposer_slashing.header_1 != proposer_slashing.header_2
# Proposer is not yet slashed # Proposer is not yet slashed
@ -2415,20 +2423,19 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
assert state.slot <= attestation.data.slot + SLOTS_PER_EPOCH assert state.slot <= attestation.data.slot + SLOTS_PER_EPOCH
# Can't submit attestations too quickly # Can't submit attestations too quickly
assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot
# Verify that the justified epoch is correct, case 1: current epoch attestations # Verify that the justified epoch and root is correct
if slot_to_epoch(attestation.data.slot) >= get_current_epoch(state): if slot_to_epoch(attestation.data.slot) >= get_current_epoch(state):
assert attestation.data.justified_epoch == state.justified_epoch # Case 1: current epoch attestations
# Case 2: previous epoch attestations assert attestation.data.source_epoch == state.current_justified_epoch
assert attestation.data.source_root == state.current_justified_root
else: else:
assert attestation.data.justified_epoch == state.previous_justified_epoch # Case 2: previous epoch attestations
# Check that the justified block root is correct assert attestation.data.source_epoch == state.previous_justified_epoch
assert attestation.data.justified_block_root == get_block_root( assert attestation.data.source_root == state.previous_justified_root
state, get_epoch_start_slot(attestation.data.justified_epoch)
)
# Check that the crosslink data is valid # Check that the crosslink data is valid
acceptable_crosslink_data = { acceptable_crosslink_data = {
# Case 1: Latest crosslink matches the one in the state # Case 1: Latest crosslink matches the one in the state
attestation.data.latest_crosslink, attestation.data.previous_crosslink,
# Case 2: State has already been updated, state's latest crosslink matches the crosslink # Case 2: State has already been updated, state's latest crosslink matches the crosslink
# the attestation is trying to create # the attestation is trying to create
Crosslink( Crosslink(

View File

@ -556,7 +556,7 @@ def verify_custody_subkey_reveal(pubkey: bytes48,
```python ```python
def verify_signed_challenge_message(message: Any, pubkey: bytes48) -> bool: def verify_signed_challenge_message(message: Any, pubkey: bytes48) -> bool:
return bls_verify( return bls_verify(
message_hash=signed_root(message, 'signature'), message_hash=signed_root(message),
pubkey=pubkey, pubkey=pubkey,
signature=message.signature, signature=message.signature,
domain=get_domain(state, get_current_epoch(state), DOMAIN_CUSTODY_INTERACTIVE) domain=get_domain(state, get_current_epoch(state), DOMAIN_CUSTODY_INTERACTIVE)
@ -607,8 +607,8 @@ Verify that `len(block.body.branch_challenges) <= MAX_BRANCH_CHALLENGES`.
For each `challenge` in `block.body.branch_challenges`, run: For each `challenge` in `block.body.branch_challenges`, run:
```python ```python
def process_branch_challenge(challenge: BranchChallenge, def process_branch_challenge(state: BeaconState,
state: BeaconState): challenge: BranchChallenge) -> None:
# Check that it's not too late to challenge # Check that it's not too late to challenge
assert slot_to_epoch(challenge.attestation.data.slot) >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY assert slot_to_epoch(challenge.attestation.data.slot) >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY
assert state.validator_registry[responder_index].exit_epoch >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY assert state.validator_registry[responder_index].exit_epoch >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY
@ -643,8 +643,8 @@ Verify that `len(block.body.branch_responses) <= MAX_BRANCH_RESPONSES`.
For each `response` in `block.body.branch_responses`, if `response.responding_to_custody_challenge == False`, run: For each `response` in `block.body.branch_responses`, if `response.responding_to_custody_challenge == False`, run:
```python ```python
def process_branch_exploration_response(response: BranchResponse, def process_branch_exploration_response(state: BeaconState,
state: BeaconState): response: BranchResponse) -> None:
challenge = get_branch_challenge_record_by_id(response.challenge_id) challenge = get_branch_challenge_record_by_id(response.challenge_id)
assert verify_merkle_branch( assert verify_merkle_branch(
leaf=response.data, leaf=response.data,
@ -664,8 +664,8 @@ def process_branch_exploration_response(response: BranchResponse,
If `response.responding_to_custody_challenge == True`, run: If `response.responding_to_custody_challenge == True`, run:
```python ```python
def process_branch_custody_response(response: BranchResponse, def process_branch_custody_response(state: BeaconState,
state: BeaconState): response: BranchResponse) -> None:
challenge = get_custody_challenge_record_by_id(response.challenge_id) challenge = get_custody_challenge_record_by_id(response.challenge_id)
responder = state.validator_registry[challenge.responder_index] responder = state.validator_registry[challenge.responder_index]
# Verify we're not too late # Verify we're not too late
@ -718,8 +718,8 @@ Verify that `len(block.body.interactive_custody_challenge_initiations) <= MAX_IN
For each `initiation` in `block.body.interactive_custody_challenge_initiations`, use the following function to process it: For each `initiation` in `block.body.interactive_custody_challenge_initiations`, use the following function to process it:
```python ```python
def process_initiation(initiation: InteractiveCustodyChallengeInitiation, def process_initiation(state: BeaconState,
state: BeaconState): initiation: InteractiveCustodyChallengeInitiation) -> None:
challenger = state.validator_registry[initiation.challenger_index] challenger = state.validator_registry[initiation.challenger_index]
responder = state.validator_registry[initiation.responder_index] responder = state.validator_registry[initiation.responder_index]
# Verify the signature # Verify the signature
@ -771,8 +771,8 @@ Verify that `len(block.body.interactive_custody_challenge_responses) <= MAX_INTE
For each `response` in `block.body.interactive_custody_challenge_responses`, use the following function to process it: For each `response` in `block.body.interactive_custody_challenge_responses`, use the following function to process it:
```python ```python
def process_response(response: InteractiveCustodyChallengeResponse, def process_response(state: BeaconState,
state: State): response: InteractiveCustodyChallengeResponse) -> None:
challenge = get_custody_challenge_record_by_id(state, response.challenge_id) challenge = get_custody_challenge_record_by_id(state, response.challenge_id)
responder = state.validator_registry[challenge.responder_index] responder = state.validator_registry[challenge.responder_index]
# Check that the right number of hashes was provided # Check that the right number of hashes was provided
@ -804,8 +804,8 @@ Verify that `len(block.body.interactive_custody_challenge_continuations) <= MAX_
For each `continuation` in `block.body.interactive_custody_challenge_continuations`, use the following function to process it: For each `continuation` in `block.body.interactive_custody_challenge_continuations`, use the following function to process it:
```python ```python
def process_continuation(continuation: InteractiveCustodyChallengeContinuation, def process_continuation(state: BeaconState,
state: State): continuation: InteractiveCustodyChallengeContinuation) -> None:
challenge = get_custody_challenge_record_by_id(state, continuation.challenge_id) challenge = get_custody_challenge_record_by_id(state, continuation.challenge_id)
challenger = state.validator_registry[challenge.challenger_index] challenger = state.validator_registry[challenge.challenger_index]
responder = state.validator_registry[challenge.responder_index] responder = state.validator_registry[challenge.responder_index]

View File

@ -40,11 +40,11 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers
- [Slot](#slot-1) - [Slot](#slot-1)
- [Shard](#shard) - [Shard](#shard)
- [Beacon block root](#beacon-block-root) - [Beacon block root](#beacon-block-root)
- [Epoch boundary root](#epoch-boundary-root) - [Target root](#target-root)
- [Crosslink data root](#crosslink-data-root) - [Crosslink data root](#crosslink-data-root)
- [Latest crosslink](#latest-crosslink) - [Latest crosslink](#latest-crosslink)
- [Justified epoch](#justified-epoch) - [Source epoch](#source-epoch)
- [Justified block root](#justified-block-root) - [Source root](#source-root)
- [Construct attestation](#construct-attestation) - [Construct attestation](#construct-attestation)
- [Data](#data) - [Data](#data)
- [Aggregation bitfield](#aggregation-bitfield) - [Aggregation bitfield](#aggregation-bitfield)
@ -101,8 +101,7 @@ In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW
To submit a deposit: To submit a deposit:
* Pack the validator's [initialization parameters](#initialization) into `deposit_input`, a [`DepositInput`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#depositinput) SSZ object. * Pack the validator's [initialization parameters](#initialization) into `deposit_input`, a [`DepositInput`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#depositinput) SSZ object.
* Set `deposit_input.proof_of_possession = EMPTY_SIGNATURE`. * Let `proof_of_possession` be the result of `bls_sign` of the `signed_root(deposit_input)` with `domain=DOMAIN_DEPOSIT`.
* Let `proof_of_possession` be the result of `bls_sign` of the `hash_tree_root(deposit_input)` with `domain=DOMAIN_DEPOSIT`.
* Set `deposit_input.proof_of_possession = proof_of_possession`. * Set `deposit_input.proof_of_possession = proof_of_possession`.
* Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`. * Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`.
* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit` along with `serialize(deposit_input)` as the singular `bytes` input along with a deposit `amount` in Gwei. * Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit` along with `serialize(deposit_input)` as the singular `bytes` input along with a deposit `amount` in Gwei.
@ -121,11 +120,12 @@ Once a validator has been processed and added to the beacon state's `validator_r
In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ACTIVATION_EXIT_DELAY` epochs (25.6 minutes). In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ACTIVATION_EXIT_DELAY` epochs (25.6 minutes).
The function [`is_active_validator`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given epoch. Usage is as follows: The function [`is_active_validator`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given shuffling epoch. Note that the `BeaconState` contains a field `current_shuffling_epoch` which dictates from which epoch the current active validators are taken. Usage is as follows:
```python ```python
shuffling_epoch = state.current_shuffling_epoch
validator = state.validator_registry[validator_index] validator = state.validator_registry[validator_index]
is_active = is_active_validator(validator, epoch) is_active = is_active_validator(validator, shuffling_epoch)
``` ```
Once a validator is activated, the validator is assigned [responsibilities](#beacon-chain-responsibilities) until exited. Once a validator is activated, the validator is assigned [responsibilities](#beacon-chain-responsibilities) until exited.
@ -138,7 +138,7 @@ A validator has two primary responsibilities to the beacon chain -- [proposing b
### Block proposal ### Block proposal
A validator is expected to propose a [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function). A validator is expected to propose a [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. 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 is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](https://github.com/ethereum/eth2.0-specs/blob/master/specs/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 (eg. at 312500 validators = 10 million ETH, that's once per ~3 weeks). 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 (eg. at 312500 validators = 10 million ETH, that's once per ~3 weeks).
@ -152,13 +152,13 @@ _Note:_ there might be "skipped" slots between the `parent` and `block`. These s
##### Parent root ##### Parent root
Set `block.parent_root = hash_tree_root(parent)`. Set `block.previous_block_root = hash_tree_root(parent)`.
##### State root ##### State root
Set `block.state_root = hash_tree_root(state)` of the resulting `state` of the `parent -> block` state transition. 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 for this purpose. _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.
##### Randao reveal ##### Randao reveal
@ -166,8 +166,8 @@ Set `block.randao_reveal = epoch_signature` where `epoch_signature` is defined a
```python ```python
epoch_signature = bls_sign( epoch_signature = bls_sign(
privkey=validator.privkey, # privkey store locally, not in state privkey=validator.privkey, # privkey stored locally, not in state
message_hash=int_to_bytes32(slot_to_epoch(block.slot)), message_hash=hash_tree_root(slot_to_epoch(block.slot)),
domain=get_domain( domain=get_domain(
fork=fork, # `fork` is the fork object at the slot `block.slot` fork=fork, # `fork` is the fork object at the slot `block.slot`
epoch=slot_to_epoch(block.slot), epoch=slot_to_epoch(block.slot),
@ -194,23 +194,16 @@ epoch_signature = bls_sign(
##### Signature ##### Signature
Set `block.signature = signed_proposal_data` where `signed_proposal_data` is defined as: Set `block.signature = block_signature` where `block_signature` is defined as:
```python ```python
proposal_data = ProposalSignedData( block_signature = bls_sign(
slot=slot,
shard=BEACON_CHAIN_SHARD_NUMBER,
block_root=hash_tree_root(block), # where `block.sigature == EMPTY_SIGNATURE
)
proposal_root = hash_tree_root(proposal_data)
signed_proposal_data = bls_sign(
privkey=validator.privkey, # privkey store locally, not in state privkey=validator.privkey, # privkey store locally, not in state
message_hash=proposal_root, message_hash=signed_root(block),
domain=get_domain( domain=get_domain(
fork=fork, # `fork` is the fork object at the slot `block.slot` fork=fork, # `fork` is the fork object at the slot `block.slot`
epoch=slot_to_epoch(block.slot), epoch=slot_to_epoch(block.slot),
domain_type=DOMAIN_PROPOSAL, domain_type=DOMAIN_BEACON_BLOCK,
) )
) )
``` ```
@ -227,12 +220,14 @@ Up to `MAX_ATTESTER_SLASHINGS` [`AttesterSlashing`](https://github.com/ethereum/
##### Attestations ##### Attestations
Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1). To maximize profit, the validator should attempt to create aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain. Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1). To maximize profit, the validator should attempt to gather aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain.
##### Deposits ##### Deposits
Up to `MAX_DEPOSITS` [`Deposit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit) objects can be included in the `block`. These deposits are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposits-1). Up to `MAX_DEPOSITS` [`Deposit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit) objects can be included in the `block`. These deposits are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposits-1).
The `proof` for each deposit must be constructed against the deposit root contained in `state.latest_eth1_data` rather than the deposit root at the time the deposit was initially logged from the 1.0 chain. This entails storing a full deposit merkle tree locally and computing updated proofs against the `latest_eth1_data.deposit_root` as needed. See [`minimal_merkle.py`](https://github.com/ethereum/research/blob/master/spec_pythonizer/utils/merkle_minimal.py) for a sample implementation.
##### Voluntary exits ##### Voluntary exits
Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exits-1). Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exits-1).
@ -247,9 +242,12 @@ A validator should create and broadcast the attestation halfway through the `slo
First the validator should construct `attestation_data`, an [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot. First the validator should construct `attestation_data`, an [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot.
* Let `head_block` be the result of running the fork choice during the assigned slot.
* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot.
##### Slot ##### Slot
Set `attestation_data.slot = slot` where `slot` is the current slot of which the validator is a member of a committee. Set `attestation_data.slot = head_state.slot`.
##### Shard ##### Shard
@ -257,15 +255,15 @@ Set `attestation_data.shard = shard` where `shard` is the shard associated with
##### Beacon block root ##### Beacon block root
Set `attestation_data.beacon_block_root = hash_tree_root(head)` where `head` is the validator's view of the `head` block of the beacon chain during `slot`. Set `attestation_data.beacon_block_root = hash_tree_root(head_block)`.
##### Epoch boundary root ##### Target root
Set `attestation_data.epoch_boundary_root = hash_tree_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary in the chain defined by `head` -- i.e. the `BeaconBlock` where `block.slot == get_epoch_start_slot(slot_to_epoch(head.slot))`. Set `attestation_data.target_root = hash_tree_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary.
_Note:_ This can be looked up in the state using: _Note:_ This can be looked up in the state using:
* Let `epoch_start_slot = get_epoch_start_slot(slot_to_epoch(head.slot))`. * Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`.
* Set `epoch_boundary_root = hash_tree_root(head) if epoch_start_slot == head.slot else get_block_root(state, epoch_start_slot)`. * Set `epoch_boundary = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`.
##### Crosslink data root ##### Crosslink data root
@ -275,17 +273,15 @@ _Note:_ This is a stub for phase 0.
##### Latest crosslink ##### Latest crosslink
Set `attestation_data.latest_crosslink = state.latest_crosslinks[shard]` where `state` is the beacon state at `head` and `shard` is the validator's assigned shard. Set `attestation_data.previous_crosslink = head_state.latest_crosslinks[shard]`.
##### Justified epoch ##### Source epoch
Set `attestation_data.justified_epoch = state.justified_epoch` where `state` is the beacon state at `head`. Set `attestation_data.source_epoch = head_state.justified_epoch`.
##### Justified block root ##### Source root
Set `attestation_data.justified_block_root = hash_tree_root(justified_block)` where `justified_block` is the block at the slot `get_epoch_start_slot(state.justified_epoch)` in the chain defined by `head`. Set `attestation_data.source_root = head_state.current_justified_root`.
_Note:_ This can be looked up in the state using `get_block_root(state, get_epoch_start_slot(state.justified_epoch))`.
#### Construct attestation #### Construct attestation
@ -320,11 +316,11 @@ attestation_data_and_custody_bit = AttestationDataAndCustodyBit(
data=attestation.data, data=attestation.data,
custody_bit=0b0, custody_bit=0b0,
) )
attestation_message_to_sign = hash_tree_root(attestation_data_and_custody_bit) attestation_message = hash_tree_root(attestation_data_and_custody_bit)
signed_attestation_data = bls_sign( signed_attestation_data = bls_sign(
privkey=validator.privkey, # privkey store locally, not in state privkey=validator.privkey, # privkey stored locally, not in state
message_hash=attestation_message_to_sign, message_hash=attestation_message,
domain=get_domain( domain=get_domain(
fork=fork, # `fork` is the fork object at the slot, `attestation_data.slot` fork=fork, # `fork` is the fork object at the slot, `attestation_data.slot`
epoch=slot_to_epoch(attestation_data.slot), epoch=slot_to_epoch(attestation_data.slot),
@ -402,12 +398,12 @@ _Note_: Signed data must be within a sequential `Fork` context to conflict. Mess
### Proposer slashing ### Proposer slashing
To avoid "proposer slashings", a validator must not sign two conflicting [`ProposalSignedData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposalsigneddata) where conflicting is defined as having the same `slot` and `shard` but a different `block_root`. In phase 0, proposals are only made for the beacon chain (`shard == BEACON_CHAIN_SHARD_NUMBER`). To avoid "proposer slashings", a validator must not sign two conflicting [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposalsigneddata) where conflicting is defined as two distinct blocks within the same epoch.
_In phase 0, as long as the validator does not sign two different beacon chain proposals for the same slot, the validator is safe against proposer slashings._ _In phase 0, as long as the validator does not sign two different beacon blocks for the same epoch, the validator is safe against proposer slashings._
Specifically, when signing an `BeaconBlock`, a validator should perform the following steps in the following order: Specifically, when signing an `BeaconBlock`, a validator should perform the following steps in the following order:
1. Save a record to hard disk that an beacon block has been signed for the `slot=slot` and `shard=BEACON_CHAIN_SHARD_NUMBER`. 1. Save a record to hard disk that an beacon block has been signed for the `epoch=slot_to_epoch(block.slot)`.
2. Generate and broadcast the block. 2. Generate and broadcast the block.
If the software crashes at some point within this routine, then when the validator comes back online the hard disk has the record of the _potentially_ signed/broadcast block and can effectively avoid slashing. If the software crashes at some point within this routine, then when the validator comes back online the hard disk has the record of the _potentially_ signed/broadcast block and can effectively avoid slashing.
@ -417,7 +413,7 @@ If the software crashes at some point within this routine, then when the validat
To avoid "attester slashings", a validator must not sign two conflicting [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) objects where conflicting is defined as a set of two attestations that satisfy either [`is_double_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_double_vote) or [`is_surround_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_surround_vote). To avoid "attester slashings", a validator must not sign two conflicting [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) objects where conflicting is defined as a set of two attestations that satisfy either [`is_double_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_double_vote) or [`is_surround_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_surround_vote).
Specifically, when signing an `Attestation`, a validator should perform the following steps in the following order: Specifically, when signing an `Attestation`, a validator should perform the following steps in the following order:
1. Save a record to hard disk that an attestation has been signed for source -- `attestation_data.justified_epoch` -- and target -- `slot_to_epoch(attestation_data.slot)`. 1. Save a record to hard disk that an attestation has been signed for source -- `attestation_data.source_epoch` -- and target -- `slot_to_epoch(attestation_data.slot)`.
2. Generate and broadcast attestation. 2. Generate and broadcast attestation.
If the software crashes at some point within this routine, then when the validator comes back online the hard disk has the record of the _potentially_ signed/broadcast attestation and can effectively avoid slashing. If the software crashes at some point within this routine, then when the validator comes back online the hard disk has the record of the _potentially_ signed/broadcast attestation and can effectively avoid slashing.