Merge branch 'dev' into vbuterin-patch-3
This commit is contained in:
commit
16123685af
|
@ -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/>
|
|
@ -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,6 +1341,12 @@ 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
|
||||||
|
|
||||||
|
validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
||||||
|
pubkey = deposit_input.pubkey
|
||||||
|
amount = deposit.deposit_data.amount
|
||||||
|
withdrawal_credentials = deposit_input.withdrawal_credentials
|
||||||
|
|
||||||
|
if pubkey not in validator_pubkeys:
|
||||||
# Verify the proof of possession
|
# Verify the proof of possession
|
||||||
proof_is_valid = bls_verify(
|
proof_is_valid = bls_verify(
|
||||||
pubkey=deposit_input.pubkey,
|
pubkey=deposit_input.pubkey,
|
||||||
|
@ -1353,16 +1358,9 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||||
DOMAIN_DEPOSIT,
|
DOMAIN_DEPOSIT,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not proof_is_valid:
|
if not proof_is_valid:
|
||||||
return
|
return
|
||||||
|
|
||||||
validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
|
||||||
pubkey = deposit_input.pubkey
|
|
||||||
amount = deposit.deposit_data.amount
|
|
||||||
withdrawal_credentials = deposit_input.withdrawal_credentials
|
|
||||||
|
|
||||||
if pubkey not in validator_pubkeys:
|
|
||||||
# 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(
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue