Merge pull request #488 from ethereum/JustinDrake-patch-1
Reduce and revamp custody placeholder logic
This commit is contained in:
commit
d78fb66aac
|
@ -26,7 +26,7 @@
|
||||||
- [`ProposerSlashing`](#proposerslashing)
|
- [`ProposerSlashing`](#proposerslashing)
|
||||||
- [Casper slashings](#casper-slashings)
|
- [Casper slashings](#casper-slashings)
|
||||||
- [`CasperSlashing`](#casperslashing)
|
- [`CasperSlashing`](#casperslashing)
|
||||||
- [`SlashableVoteData`](#slashablevotedata)
|
- [`SlashableVote`](#slashablevote)
|
||||||
- [Attestations](#attestations)
|
- [Attestations](#attestations)
|
||||||
- [`Attestation`](#attestation)
|
- [`Attestation`](#attestation)
|
||||||
- [`AttestationData`](#attestationdata)
|
- [`AttestationData`](#attestationdata)
|
||||||
|
@ -85,7 +85,9 @@
|
||||||
- [`get_effective_balance`](#get_effective_balance)
|
- [`get_effective_balance`](#get_effective_balance)
|
||||||
- [`get_fork_version`](#get_fork_version)
|
- [`get_fork_version`](#get_fork_version)
|
||||||
- [`get_domain`](#get_domain)
|
- [`get_domain`](#get_domain)
|
||||||
- [`verify_slashable_vote_data`](#verify_slashable_vote_data)
|
- [`get_bitfield_bit`](#get_bitfield_bit)
|
||||||
|
- [`verify_bitfield`](#verify_bitfield)
|
||||||
|
- [`verify_slashable_vote`](#verify_slashable_vote)
|
||||||
- [`is_double_vote`](#is_double_vote)
|
- [`is_double_vote`](#is_double_vote)
|
||||||
- [`is_surround_vote`](#is_surround_vote)
|
- [`is_surround_vote`](#is_surround_vote)
|
||||||
- [`integer_squareroot`](#integer_squareroot)
|
- [`integer_squareroot`](#integer_squareroot)
|
||||||
|
@ -110,7 +112,6 @@
|
||||||
- [Attestations](#attestations-1)
|
- [Attestations](#attestations-1)
|
||||||
- [Deposits](#deposits-1)
|
- [Deposits](#deposits-1)
|
||||||
- [Exits](#exits-1)
|
- [Exits](#exits-1)
|
||||||
- [Custody](#custody)
|
|
||||||
- [Per-epoch processing](#per-epoch-processing)
|
- [Per-epoch processing](#per-epoch-processing)
|
||||||
- [Helpers](#helpers)
|
- [Helpers](#helpers)
|
||||||
- [Eth1 data](#eth1-data-1)
|
- [Eth1 data](#eth1-data-1)
|
||||||
|
@ -171,7 +172,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
|
||||||
| `EJECTION_BALANCE` | `2**4 * 1e9` (= 16,000,000,000) | Gwei |
|
| `EJECTION_BALANCE` | `2**4 * 1e9` (= 16,000,000,000) | Gwei |
|
||||||
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - |
|
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - |
|
||||||
| `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - |
|
| `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - |
|
||||||
| `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes |
|
| `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) | votes |
|
||||||
| `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals |
|
| `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals |
|
||||||
|
|
||||||
* For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
* For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||||
|
@ -245,7 +246,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
|
||||||
| Name | Value |
|
| Name | Value |
|
||||||
| - | - |
|
| - | - |
|
||||||
| `MAX_PROPOSER_SLASHINGS` | `2**4` (= 16) |
|
| `MAX_PROPOSER_SLASHINGS` | `2**4` (= 16) |
|
||||||
| `MAX_CASPER_SLASHINGS` | `2**4` (= 16) |
|
| `MAX_CASPER_SLASHINGS` | `2**0` (= 1) |
|
||||||
| `MAX_ATTESTATIONS` | `2**7` (= 128) |
|
| `MAX_ATTESTATIONS` | `2**7` (= 128) |
|
||||||
| `MAX_DEPOSITS` | `2**4` (= 16) |
|
| `MAX_DEPOSITS` | `2**4` (= 16) |
|
||||||
| `MAX_EXITS` | `2**4` (= 16) |
|
| `MAX_EXITS` | `2**4` (= 16) |
|
||||||
|
@ -273,7 +274,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# Proposer index
|
# Proposer index
|
||||||
'proposer_index': 'uint24',
|
'proposer_index': 'uint64',
|
||||||
# First proposal data
|
# First proposal data
|
||||||
'proposal_data_1': ProposalSignedData,
|
'proposal_data_1': ProposalSignedData,
|
||||||
# First proposal signature
|
# First proposal signature
|
||||||
|
@ -292,20 +293,20 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# First batch of votes
|
# First batch of votes
|
||||||
'slashable_vote_data_1': SlashableVoteData,
|
'slashable_vote_1': SlashableVote,
|
||||||
# Second batch of votes
|
# Second batch of votes
|
||||||
'slashable_vote_data_2': SlashableVoteData,
|
'slashable_vote_2': SlashableVote,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### `SlashableVoteData`
|
##### `SlashableVote`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# Validator indices with custody bit equal to 0
|
# Validator indices
|
||||||
'custody_bit_0_indices': ['uint24'],
|
'validator_indices': '[uint64]',
|
||||||
# Validator indices with custody bit equal to 1
|
# Custody bitfield
|
||||||
'custody_bit_1_indices': ['uint24'],
|
'custody_bitfield': 'bytes',
|
||||||
# Attestation data
|
# Attestation data
|
||||||
'data': AttestationData,
|
'data': AttestationData,
|
||||||
# Aggregate signature
|
# Aggregate signature
|
||||||
|
@ -358,9 +359,9 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# Attestation data
|
# Attestation data
|
||||||
data: AttestationData,
|
'data': AttestationData,
|
||||||
# Custody bit
|
# Custody bit
|
||||||
custody_bit: bool,
|
'custody_bit': 'bool',
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -414,7 +415,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
# Minimum epoch for processing exit
|
# Minimum epoch for processing exit
|
||||||
'epoch': 'uint64',
|
'epoch': 'uint64',
|
||||||
# Index of the exiting validator
|
# Index of the exiting validator
|
||||||
'validator_index': 'uint24',
|
'validator_index': 'uint64',
|
||||||
# Validator signature
|
# Validator signature
|
||||||
'signature': 'bytes96',
|
'signature': 'bytes96',
|
||||||
}
|
}
|
||||||
|
@ -446,16 +447,11 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
'proposer_slashings': [ProposerSlashing],
|
'proposer_slashings': [ProposerSlashing],
|
||||||
'casper_slashings': [CasperSlashing],
|
'casper_slashings': [CasperSlashing],
|
||||||
'attestations': [Attestation],
|
'attestations': [Attestation],
|
||||||
'custody_reseeds': [CustodyReseed],
|
|
||||||
'custody_challenges': [CustodyChallenge],
|
|
||||||
'custody_responses': [CustodyResponse],
|
|
||||||
'deposits': [Deposit],
|
'deposits': [Deposit],
|
||||||
'exits': [Exit],
|
'exits': [Exit],
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`CustodyReseed`, `CustodyChallenge`, and `CustodyResponse` will be defined in phase 1; for now, put dummy classes as these lists will remain empty throughout phase 0.
|
|
||||||
|
|
||||||
#### `ProposalSignedData`
|
#### `ProposalSignedData`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -488,7 +484,6 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
|
|
||||||
# Randomness and committees
|
# Randomness and committees
|
||||||
'latest_randao_mixes': ['bytes32'],
|
'latest_randao_mixes': ['bytes32'],
|
||||||
'latest_vdf_outputs': ['bytes32'],
|
|
||||||
'previous_epoch_start_shard': 'uint64',
|
'previous_epoch_start_shard': 'uint64',
|
||||||
'current_epoch_start_shard': 'uint64',
|
'current_epoch_start_shard': 'uint64',
|
||||||
'previous_calculation_epoch': 'uint64',
|
'previous_calculation_epoch': 'uint64',
|
||||||
|
@ -496,9 +491,6 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
'previous_epoch_seed': 'bytes32',
|
'previous_epoch_seed': 'bytes32',
|
||||||
'current_epoch_seed': 'bytes32',
|
'current_epoch_seed': 'bytes32',
|
||||||
|
|
||||||
# Custody challenges
|
|
||||||
'custody_challenges': [CustodyChallenge],
|
|
||||||
|
|
||||||
# Finality
|
# Finality
|
||||||
'previous_justified_epoch': 'uint64',
|
'previous_justified_epoch': 'uint64',
|
||||||
'justified_epoch': 'uint64',
|
'justified_epoch': 'uint64',
|
||||||
|
@ -539,10 +531,6 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
'exit_count': 'uint64',
|
'exit_count': 'uint64',
|
||||||
# Status flags
|
# Status flags
|
||||||
'status_flags': 'uint64',
|
'status_flags': 'uint64',
|
||||||
# Slot of latest custody reseed
|
|
||||||
'latest_custody_reseed_slot': 'uint64',
|
|
||||||
# Slot of second-latest custody reseed
|
|
||||||
'penultimate_custody_reseed_slot': 'uint64',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -611,16 +599,16 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
|
|
||||||
We define the following Python custom types for type hinting and readability:
|
We define the following Python custom types for type hinting and readability:
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | SSZ equivalent | Description |
|
||||||
| - | - | - |
|
| - | - | - |
|
||||||
| `SlotNumber` | unsigned 64-bit integer | the number of a slot |
|
| `SlotNumber` | `uint64` | a slot number |
|
||||||
| `EpochNumber` | unsigned 64-bit integer | the number of an epoch |
|
| `EpochNumber` | `uint64` | an epoch number |
|
||||||
| `ShardNumber` | unsigned 64-bit integer | the number of a shard |
|
| `ShardNumber` | `uint64` | a shard number |
|
||||||
| `ValidatorIndex` | unsigned 24-bit integer | the index number of a validator in the registry |
|
| `ValidatorIndex` | `uint64` | an index in the validator registry |
|
||||||
| `Gwei` | unsigned 64-bit integer | an amount in Gwei |
|
| `Gwei` | `uint64` | an amount in Gwei |
|
||||||
| `Bytes32` | 32-byte data | binary data with 32-byte length |
|
| `Bytes32` | `bytes32` | 32 bytes of binary data |
|
||||||
| `BLSPubkey` | 48-byte data | a public key in BLS signature scheme |
|
| `BLSPubkey` | `bytes48` | a BLS public key |
|
||||||
| `BLSSignature` | 96-byte data | a signature in BLS signature scheme |
|
| `BLSSignature` | `bytes96` | a BLS signature |
|
||||||
|
|
||||||
## Ethereum 1.0 deposit contract
|
## Ethereum 1.0 deposit contract
|
||||||
|
|
||||||
|
@ -1101,23 +1089,23 @@ def merkle_root(values: List[Bytes32]) -> Bytes32:
|
||||||
```python
|
```python
|
||||||
def get_attestation_participants(state: BeaconState,
|
def get_attestation_participants(state: BeaconState,
|
||||||
attestation_data: AttestationData,
|
attestation_data: AttestationData,
|
||||||
aggregation_bitfield: bytes) -> List[ValidatorIndex]:
|
bitfield: bytes) -> List[ValidatorIndex]:
|
||||||
"""
|
"""
|
||||||
Returns the participant indices at for the ``attestation_data`` and ``aggregation_bitfield``.
|
Returns the participant indices at for the ``attestation_data`` and ``bitfield``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Find the committee in the list with the desired shard
|
# Find the committee in the list with the desired shard
|
||||||
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
|
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
|
||||||
|
|
||||||
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
|
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
|
||||||
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
|
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
|
||||||
assert len(aggregation_bitfield) == (len(crosslink_committee) + 7) // 8
|
|
||||||
|
assert verify_bitfield(bitfield, len(crosslink_committee))
|
||||||
|
|
||||||
# Find the participating attesters in the committee
|
# Find the participating attesters in the committee
|
||||||
participants = []
|
participants = []
|
||||||
for i, validator_index in enumerate(crosslink_committee):
|
for i, validator_index in enumerate(crosslink_committee):
|
||||||
aggregation_bit = (aggregation_bitfield[i // 8] >> (7 - (i % 8))) % 2
|
aggregation_bit = get_bitfield_bit(bitfield, i)
|
||||||
if aggregation_bit == 1:
|
if aggregation_bit == 0b1:
|
||||||
participants.append(validator_index)
|
participants.append(validator_index)
|
||||||
return participants
|
return participants
|
||||||
```
|
```
|
||||||
|
@ -1159,23 +1147,74 @@ def get_domain(fork: Fork,
|
||||||
) * 2**32 + domain_type
|
) * 2**32 + domain_type
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `verify_slashable_vote_data`
|
#### `get_bitfield_bit`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def verify_slashable_vote_data(state: BeaconState, vote_data: SlashableVoteData) -> bool:
|
def get_bitfield_bit(bitfield: bytes, i: int) -> int:
|
||||||
if len(vote_data.custody_bit_0_indices) + len(vote_data.custody_bit_1_indices) > MAX_CASPER_VOTES:
|
"""
|
||||||
|
Extract the bit in ``bitfield`` at position ``i``.
|
||||||
|
"""
|
||||||
|
return (bitfield[i // 8] >> (7 - (i % 8))) % 2
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `verify_bitfield`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def verify_bitfield(bitfield: bytes, committee_size: int) -> bool:
|
||||||
|
"""
|
||||||
|
Verify ``bitfield`` against the ``committee_size``.
|
||||||
|
"""
|
||||||
|
if len(bitfield) != (committee_size + 7) // 8:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return bls_verify_multiple(
|
for i in range(committee_size + 1, committee_size - committee_size % 8 + 8):
|
||||||
|
if get_bitfield_bit(bitfield, i) == 0b1:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `verify_slashable_vote`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def verify_slashable_vote(state: BeaconState, slashable_vote: SlashableVote) -> bool:
|
||||||
|
"""
|
||||||
|
Verify validity of ``slashable_vote`` fields.
|
||||||
|
"""
|
||||||
|
if slashable_vote.custody_bitfield != b'\x00' * len(slashable_vote.custody_bitfield): # [TO BE REMOVED IN PHASE 1]
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(slashable_vote.validator_indices) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for i in range(len(slashable_vote.validator_indices) - 1):
|
||||||
|
if slashable_vote.validator_indices[i] >= slashable_vote.validator_indices[i + 1]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not verify_bitfield(slashable_vote.custody_bitfield, len(slashable_vote.validator_indices)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(slashable_vote.validator_indices) > MAX_INDICES_PER_SLASHABLE_VOTE:
|
||||||
|
return False
|
||||||
|
|
||||||
|
custody_bit_0_indices = []
|
||||||
|
custody_bit_1_indices = []
|
||||||
|
for i, validator_index in enumerate(slashable_vote.validator_indices):
|
||||||
|
if get_bitfield_bit(slashable_vote.custody_bitfield, i) == 0b0:
|
||||||
|
custody_bit_0_indices.append(validator_index)
|
||||||
|
else:
|
||||||
|
custody_bit_1_indices.append(validator_index)
|
||||||
|
|
||||||
|
return bls_verify(
|
||||||
pubkeys=[
|
pubkeys=[
|
||||||
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in vote_data.custody_bit_0_indices]),
|
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_0_indices]),
|
||||||
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in vote_data.custody_bit_1_indices]),
|
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]),
|
||||||
],
|
],
|
||||||
messages=[
|
messages=[
|
||||||
hash_tree_root(AttestationDataAndCustodyBit(vote_data.data, False)),
|
hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_vote.data, custody_bit=0b0)),
|
||||||
hash_tree_root(AttestationDataAndCustodyBit(vote_data.data, True)),
|
hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_vote.data, custody_bit=0b1)),
|
||||||
],
|
],
|
||||||
signature=vote_data.aggregate_signature,
|
signature=slashable_vote.aggregate_signature,
|
||||||
domain=get_domain(
|
domain=get_domain(
|
||||||
state.fork,
|
state.fork,
|
||||||
slot_to_epoch(vote_data.data.slot),
|
slot_to_epoch(vote_data.data.slot),
|
||||||
|
@ -1280,9 +1319,6 @@ A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following val
|
||||||
proposer_slashings=[],
|
proposer_slashings=[],
|
||||||
casper_slashings=[],
|
casper_slashings=[],
|
||||||
attestations=[],
|
attestations=[],
|
||||||
custody_reseeds=[],
|
|
||||||
custody_challenges=[],
|
|
||||||
custody_responses=[],
|
|
||||||
deposits=[],
|
deposits=[],
|
||||||
exits=[],
|
exits=[],
|
||||||
),
|
),
|
||||||
|
@ -1313,7 +1349,6 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
|
||||||
|
|
||||||
# Randomness and committees
|
# Randomness and committees
|
||||||
latest_randao_mixes=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)],
|
latest_randao_mixes=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)],
|
||||||
latest_vdf_outputs=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH // EPOCH_LENGTH)],
|
|
||||||
previous_epoch_start_shard=GENESIS_START_SHARD,
|
previous_epoch_start_shard=GENESIS_START_SHARD,
|
||||||
current_epoch_start_shard=GENESIS_START_SHARD,
|
current_epoch_start_shard=GENESIS_START_SHARD,
|
||||||
previous_calculation_epoch=GENESIS_EPOCH,
|
previous_calculation_epoch=GENESIS_EPOCH,
|
||||||
|
@ -1321,9 +1356,6 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
|
||||||
previous_epoch_seed=ZERO_HASH,
|
previous_epoch_seed=ZERO_HASH,
|
||||||
current_epoch_seed=ZERO_HASH,
|
current_epoch_seed=ZERO_HASH,
|
||||||
|
|
||||||
# Custody challenges
|
|
||||||
custody_challenges=[],
|
|
||||||
|
|
||||||
# Finality
|
# Finality
|
||||||
previous_justified_epoch=GENESIS_EPOCH,
|
previous_justified_epoch=GENESIS_EPOCH,
|
||||||
justified_epoch=GENESIS_EPOCH,
|
justified_epoch=GENESIS_EPOCH,
|
||||||
|
@ -1424,8 +1456,6 @@ def process_deposit(state: BeaconState,
|
||||||
penalized_epoch=FAR_FUTURE_EPOCH,
|
penalized_epoch=FAR_FUTURE_EPOCH,
|
||||||
exit_count=0,
|
exit_count=0,
|
||||||
status_flags=0,
|
status_flags=0,
|
||||||
latest_custody_reseed_slot=GENESIS_SLOT,
|
|
||||||
penultimate_custody_reseed_slot=GENESIS_SLOT,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.
|
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.
|
||||||
|
@ -1551,15 +1581,14 @@ Verify that `len(block.body.casper_slashings) <= MAX_CASPER_SLASHINGS`.
|
||||||
|
|
||||||
For each `casper_slashing` in `block.body.casper_slashings`:
|
For each `casper_slashing` in `block.body.casper_slashings`:
|
||||||
|
|
||||||
* Let `slashable_vote_data_1 = casper_slashing.slashable_vote_data_1`.
|
* Let `slashable_vote_1 = casper_slashing.slashable_vote_1`.
|
||||||
* Let `slashable_vote_data_2 = casper_slashing.slashable_vote_data_2`.
|
* Let `slashable_vote_2 = casper_slashing.slashable_vote_2`.
|
||||||
* Let `indices(slashable_vote_data) = slashable_vote_data.custody_bit_0_indices + slashable_vote_data.custody_bit_1_indices`.
|
* Let `intersection = [x for x in slashable_vote_1.validator_indices if x in slashable_vote_2.validator_indices]`.
|
||||||
* Let `intersection = [x for x in indices(slashable_vote_data_1) if x in indices(slashable_vote_data_2)]`.
|
|
||||||
* Verify that `len(intersection) >= 1`.
|
* Verify that `len(intersection) >= 1`.
|
||||||
* Verify that `slashable_vote_data_1.data != slashable_vote_data_2.data`.
|
* Verify that `slashable_vote_1.data != slashable_vote_2.data`.
|
||||||
* Verify that `is_double_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)` or `is_surround_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)`.
|
* Verify that `is_double_vote(slashable_vote_1.data, slashable_vote_2.data)` or `is_surround_vote(slashable_vote_1.data, slashable_vote_2.data)`.
|
||||||
* Verify that `verify_slashable_vote_data(state, slashable_vote_data_1)`.
|
* Verify that `verify_slashable_vote(state, slashable_vote_1)`.
|
||||||
* Verify that `verify_slashable_vote_data(state, slashable_vote_data_2)`.
|
* Verify that `verify_slashable_vote(state, slashable_vote_2)`.
|
||||||
* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_epoch > get_current_epoch(state)`.
|
* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_epoch > get_current_epoch(state)`.
|
||||||
|
|
||||||
#### Attestations
|
#### Attestations
|
||||||
|
@ -1573,10 +1602,33 @@ For each `attestation` in `block.body.attestations`:
|
||||||
* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`.
|
* Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`.
|
||||||
* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`.
|
* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`.
|
||||||
* Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[shard].shard_block_root`.
|
* Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[shard].shard_block_root`.
|
||||||
* `aggregate_signature` verification:
|
* Verify bitfields and aggregate signature:
|
||||||
* Let `participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)`.
|
|
||||||
* Let `group_public_key = bls_aggregate_pubkeys([state.validator_registry[v].pubkey for v in participants])`.
|
```python
|
||||||
* Verify that `bls_verify(pubkey=group_public_key, message=hash_tree_root(AttestationDataAndCustodyBit(attestation.data, False)), signature=attestation.aggregate_signature, domain=get_domain(state.fork, slot_to_epoch(attestation.data.slot), DOMAIN_ATTESTATION))`.
|
assert attestation.custody_bitfield == b'\x00' * len(attestation.custody_bitfield) # [TO BE REMOVED IN PHASE 1]
|
||||||
|
|
||||||
|
for i in range(len(crosslink_committee)):
|
||||||
|
if get_bitfield_bit(attestation.aggregation_bitfield) == 0b0:
|
||||||
|
assert get_bitfield_bit(attestation.custody_bitfield) == 0b0
|
||||||
|
|
||||||
|
participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
|
||||||
|
custody_bit_1_participants = get_attestation_participants(state, attestation.data, attestation.custody_bitfield)
|
||||||
|
custody_bit_0_participants = [i in participants for i not in custody_bit_1_participants]
|
||||||
|
|
||||||
|
assert bls_verify_multiple(
|
||||||
|
pubkeys=[
|
||||||
|
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_0_participants]),
|
||||||
|
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_participants]),
|
||||||
|
],
|
||||||
|
messages=[
|
||||||
|
hash_tree_root(AttestationDataAndCustodyBit(data=attestation.data, custody_bit=0b0)),
|
||||||
|
hash_tree_root(AttestationDataAndCustodyBit(data=attestation.data, custody_bit=0b1)),
|
||||||
|
],
|
||||||
|
signature=attestation.aggregate_signature,
|
||||||
|
domain=get_domain(state.fork, slot_to_epoch(attestation.data.slot), DOMAIN_ATTESTATION),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_root == ZERO_HASH`.
|
* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_root == ZERO_HASH`.
|
||||||
* Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`.
|
* Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`.
|
||||||
|
|
||||||
|
@ -1628,10 +1680,6 @@ For each `exit` in `block.body.exits`:
|
||||||
* Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`.
|
* Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`.
|
||||||
* Run `initiate_validator_exit(state, exit.validator_index)`.
|
* Run `initiate_validator_exit(state, exit.validator_index)`.
|
||||||
|
|
||||||
#### Custody
|
|
||||||
|
|
||||||
[TO BE REMOVED IN PHASE 1] Verify that `len(block.body.custody_reseeds) == len(block.body.custody_challenges) == len(block.body.custody_responses) == 0`.
|
|
||||||
|
|
||||||
## Per-epoch processing
|
## Per-epoch processing
|
||||||
|
|
||||||
The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`.
|
The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`.
|
||||||
|
|
Loading…
Reference in New Issue