diff --git a/eth2/beacon/deposit_helpers.py b/eth2/beacon/deposit_helpers.py index 57ee456097..1628e3dfb8 100644 --- a/eth2/beacon/deposit_helpers.py +++ b/eth2/beacon/deposit_helpers.py @@ -30,13 +30,11 @@ def validate_proof_of_possession(state: BeaconState, proof_of_possession: BLSSignature, withdrawal_credentials: Hash32, randao_commitment: Hash32, - custody_commitment: Hash32, epoch_length: int) -> None: deposit_input = DepositInput( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, proof_of_possession=EMPTY_SIGNATURE, ) @@ -79,7 +77,6 @@ def process_deposit(*, proof_of_possession: BLSSignature, withdrawal_credentials: Hash32, randao_commitment: Hash32, - custody_commitment: Hash32, epoch_length: int) -> BeaconState: """ Process a deposit from Ethereum 1.0. @@ -90,7 +87,6 @@ def process_deposit(*, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, epoch_length=epoch_length, ) @@ -100,7 +96,6 @@ def process_deposit(*, pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, ) # Note: In phase 2 registry indices that has been withdrawn for a long time diff --git a/eth2/beacon/helpers.py b/eth2/beacon/helpers.py index 669aed736c..b231a84a7d 100644 --- a/eth2/beacon/helpers.py +++ b/eth2/beacon/helpers.py @@ -17,7 +17,6 @@ ) from eth2._utils.bitfield import ( - get_bitfield_length, has_voted, ) import eth2._utils.bls as bls @@ -48,6 +47,7 @@ ValidatorIndex, ) from eth2.beacon.validation import ( + validate_bitfield, validate_epoch_for_active_index_root, validate_epoch_for_active_randao_mix, validate_epoch_for_current_epoch, @@ -383,13 +383,13 @@ def _get_committee_for_shard( @to_tuple def get_attestation_participants(state: 'BeaconState', attestation_data: 'AttestationData', - aggregation_bitfield: Bitfield, + bitfield: Bitfield, genesis_epoch: EpochNumber, epoch_length: int, target_committee_size: int, shard_count: int) -> Iterable[ValidatorIndex]: """ - Return the participant indices at for the ``attestation_data`` and ``aggregation_bitfield``. + Return the participant indices at for the ``attestation_data`` and ``bitfield``. """ # Find the committee in the list with the desired shard crosslink_committees = get_crosslink_committees_at_slot( @@ -420,16 +420,11 @@ def get_attestation_participants(state: 'BeaconState', ) ) - committee_size = len(committee) - if len(aggregation_bitfield) != get_bitfield_length(committee_size): - raise ValidationError( - f"Invalid bitfield length," - f"\texpected: {get_bitfield_length(committee_size)}, found: {len(aggregation_bitfield)}" - ) + validate_bitfield(bitfield, len(committee)) # Find the participating attesters in the committee for bitfield_index, validator_index in enumerate(committee): - if has_voted(aggregation_bitfield, bitfield_index): + if has_voted(bitfield, bitfield_index): yield validator_index @@ -618,9 +613,7 @@ def generate_aggregate_pubkeys( Compute the aggregate pubkey we expect based on the proof-of-custody indices found in the ``slashable_attestation``. """ - custody_bit_0_indices = slashable_attestation.custody_bit_0_indices - custody_bit_1_indices = slashable_attestation.custody_bit_1_indices - all_indices = (custody_bit_0_indices, custody_bit_1_indices) + all_indices = slashable_attestation.custody_bit_indices get_pubkeys = functools.partial(get_pubkey_for_indices, validators) return map( bls.aggregate_pubkeys, @@ -628,14 +621,6 @@ def generate_aggregate_pubkeys( ) -def verify_vote_count(slashable_attestation: 'SlashableAttestation', - max_indices_per_slashable_vote: int) -> bool: - """ - Ensure we have no more than ``max_indices_per_slashable_vote`` in the ``slashable_attestation``. - """ - return slashable_attestation.vote_count <= max_indices_per_slashable_vote - - def verify_slashable_attestation_signature(state: 'BeaconState', slashable_attestation: 'SlashableAttestation', epoch_length: int) -> bool: @@ -662,20 +647,51 @@ def verify_slashable_attestation_signature(state: 'BeaconState', ) -def verify_slashable_attestation(state: 'BeaconState', - slashable_attestation: 'SlashableAttestation', - max_indices_per_slashable_vote: int, - epoch_length: int) -> bool: +def validate_slashable_attestation(state: 'BeaconState', + slashable_attestation: 'SlashableAttestation', + max_indices_per_slashable_vote: int, + epoch_length: int) -> None: """ + Verify validity of ``slashable_attestation`` fields. Ensure that the ``slashable_attestation`` is properly assembled and contains the signature we expect from the validators we expect. Otherwise, return False as the ``slashable_attestation`` is invalid. """ - return ( - verify_vote_count(slashable_attestation, max_indices_per_slashable_vote) and - verify_slashable_attestation_signature(state, slashable_attestation, epoch_length) + # [TO BE REMOVED IN PHASE 1] + if not slashable_attestation.is_custody_bitfield_empty: + raise ValidationError( + "`slashable_attestation.custody_bitfield` is not empty." + ) + + if len(slashable_attestation.validator_indices) == 0: + raise ValidationError( + "`slashable_attestation.validator_indices` is empty." + ) + + if not slashable_attestation.is_validator_indices_ascending: + raise ValidationError( + "`slashable_attestation.validator_indices` " + f"({slashable_attestation.validator_indices}) " + "is not ordered in ascending." + ) + + validate_bitfield( + slashable_attestation.custody_bitfield, + len(slashable_attestation.validator_indices), ) + if len(slashable_attestation.validator_indices) > max_indices_per_slashable_vote: + raise ValidationError( + f"`len(slashable_attestation.validator_indices)` " + f"({len(slashable_attestation.validator_indices)}) greater than" + f"MAX_INDICES_PER_SLASHABLE_VOTE ({max_indices_per_slashable_vote})" + ) + + if not verify_slashable_attestation_signature(state, slashable_attestation, epoch_length): + raise ValidationError( + f"slashable_attestation.signature error" + ) + def is_double_vote(attestation_data_1: 'AttestationData', attestation_data_2: 'AttestationData', diff --git a/eth2/beacon/on_startup.py b/eth2/beacon/on_startup.py index 71aa528e6d..a3818921ba 100644 --- a/eth2/beacon/on_startup.py +++ b/eth2/beacon/on_startup.py @@ -95,12 +95,6 @@ def get_initial_beacon_state(*, # Randomness and committees latest_randao_mixes=tuple(ZERO_HASH32 for _ in range(latest_randao_mixes_length)), - latest_vdf_outputs=tuple( - ZERO_HASH32 for _ in range(latest_randao_mixes_length // epoch_length) - ), - # TODO Remove `persistent_committees`, `persistent_committee_reassignments` - persistent_committees=(), - persistent_committee_reassignments=(), previous_epoch_start_shard=genesis_start_shard, current_epoch_start_shard=genesis_start_shard, previous_calculation_epoch=genesis_epoch, @@ -108,9 +102,6 @@ def get_initial_beacon_state(*, previous_epoch_seed=ZERO_HASH32, current_epoch_seed=ZERO_HASH32, - # Custody challenges - custody_challenges=(), - # Finality previous_justified_epoch=genesis_epoch, justified_epoch=genesis_epoch, @@ -145,7 +136,6 @@ def get_initial_beacon_state(*, proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, - custody_commitment=deposit.deposit_data.deposit_input.custody_commitment, epoch_length=epoch_length, ) diff --git a/eth2/beacon/sedes.py b/eth2/beacon/sedes.py index 878a01cbd3..a191505217 100644 --- a/eth2/beacon/sedes.py +++ b/eth2/beacon/sedes.py @@ -5,5 +5,4 @@ hash32 = Binary.fixed_length(32) -uint24 = BigEndianInt(24) uint64 = BigEndianInt(64) diff --git a/eth2/beacon/state_machines/forks/serenity/block_validation.py b/eth2/beacon/state_machines/forks/serenity/block_validation.py index 893bd2695f..a55f1fd715 100644 --- a/eth2/beacon/state_machines/forks/serenity/block_validation.py +++ b/eth2/beacon/state_machines/forks/serenity/block_validation.py @@ -291,7 +291,7 @@ def validate_attestation_aggregate_signature(state: BeaconState, participant_indices = get_attestation_participants( state=state, attestation_data=attestation.data, - aggregation_bitfield=attestation.aggregation_bitfield, + bitfield=attestation.aggregation_bitfield, genesis_epoch=genesis_epoch, epoch_length=epoch_length, target_committee_size=target_committee_size, diff --git a/eth2/beacon/tools/builder/initializer.py b/eth2/beacon/tools/builder/initializer.py index aeae565360..1d6897c7f3 100644 --- a/eth2/beacon/tools/builder/initializer.py +++ b/eth2/beacon/tools/builder/initializer.py @@ -39,7 +39,6 @@ def create_mock_initial_validator_deposits( # Mock data withdrawal_credentials = b'\x22' * 32 randao_commitment = b'\x33' * 32 - custody_commitment = b'\x44' * 32 deposit_timestamp = 0 fork = Fork( previous_version=config.GENESIS_FORK_VERSION, @@ -59,13 +58,11 @@ def create_mock_initial_validator_deposits( pubkey=pubkeys[i], withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, proof_of_possession=sign_proof_of_possession( deposit_input=DepositInput( pubkey=pubkeys[i], withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, ), privkey=keymap[pubkeys[i]], fork=fork, diff --git a/eth2/beacon/types/blocks.py b/eth2/beacon/types/blocks.py index 7fd275694d..2b7053d907 100644 --- a/eth2/beacon/types/blocks.py +++ b/eth2/beacon/types/blocks.py @@ -43,9 +43,6 @@ from .attestations import Attestation -from .custody_challenges import CustodyChallenge -from .custody_reseeds import CustodyReseed -from .custody_responses import CustodyResponse from .attester_slashings import AttesterSlashing from .deposits import Deposit from .eth1_data import Eth1Data @@ -61,9 +58,6 @@ class BeaconBlockBody(rlp.Serializable): ('proposer_slashings', CountableList(ProposerSlashing)), ('attester_slashings', CountableList(AttesterSlashing)), ('attestations', CountableList(Attestation)), - ('custody_reseeds', CountableList(CustodyReseed)), - ('custody_challenges', CountableList(CustodyChallenge)), - ('custody_responses', CountableList(CustodyResponse)), ('deposits', CountableList(Deposit)), ('exits', CountableList(Exit)), ] @@ -72,18 +66,12 @@ def __init__(self, proposer_slashings: Sequence[ProposerSlashing], attester_slashings: Sequence[AttesterSlashing], attestations: Sequence[Attestation], - custody_reseeds: Sequence[CustodyReseed], - custody_challenges: Sequence[CustodyResponse], - custody_responses: Sequence[CustodyResponse], deposits: Sequence[Deposit], exits: Sequence[Exit])-> None: super().__init__( proposer_slashings=proposer_slashings, attester_slashings=attester_slashings, attestations=attestations, - custody_reseeds=custody_reseeds, - custody_challenges=custody_challenges, - custody_responses=custody_responses, deposits=deposits, exits=exits, ) @@ -94,9 +82,6 @@ def create_empty_body(cls) -> 'BeaconBlockBody': proposer_slashings=(), attester_slashings=(), attestations=(), - custody_reseeds=(), - custody_challenges=(), - custody_responses=(), deposits=(), exits=(), ) @@ -107,9 +92,6 @@ def is_empty(self) -> bool: self.proposer_slashings == () and self.attester_slashings == () and self.attestations == () and - self.custody_reseeds == () and - self.custody_challenges == () and - self.custody_responses == () and self.deposits == () and self.exits == () ) @@ -121,9 +103,6 @@ def cast_block_body(cls, proposer_slashings=body.proposer_slashings, attester_slashings=body.attester_slashings, attestations=body.attestations, - custody_reseeds=body.custody_reseeds, - custody_challenges=body.custody_challenges, - custody_responses=body.custody_responses, deposits=body.deposits, exits=body.exits, ) @@ -217,9 +196,6 @@ def from_root(cls, root: Hash32, chaindb: 'BaseBeaconChainDB') -> 'BeaconBlock': proposer_slashings=block.body.proposer_slashings, attester_slashings=block.body.attester_slashings, attestations=block.body.attestations, - custody_reseeds=block.body.custody_reseeds, - custody_challenges=block.body.custody_challenges, - custody_responses=block.body.custody_responses, deposits=block.body.deposits, exits=block.body.exits, ) diff --git a/eth2/beacon/types/custody_challenges.py b/eth2/beacon/types/custody_challenges.py deleted file mode 100644 index 8f87fd0803..0000000000 --- a/eth2/beacon/types/custody_challenges.py +++ /dev/null @@ -1,5 +0,0 @@ -import rlp - - -class CustodyChallenge(rlp.Serializable): - pass diff --git a/eth2/beacon/types/custody_reseeds.py b/eth2/beacon/types/custody_reseeds.py deleted file mode 100644 index 9135c157c6..0000000000 --- a/eth2/beacon/types/custody_reseeds.py +++ /dev/null @@ -1,5 +0,0 @@ -import rlp - - -class CustodyReseed(rlp.Serializable): - pass diff --git a/eth2/beacon/types/custody_responses.py b/eth2/beacon/types/custody_responses.py deleted file mode 100644 index f9ce75b620..0000000000 --- a/eth2/beacon/types/custody_responses.py +++ /dev/null @@ -1,5 +0,0 @@ -import rlp - - -class CustodyResponse(rlp.Serializable): - pass diff --git a/eth2/beacon/types/deposit_input.py b/eth2/beacon/types/deposit_input.py index 47755977dd..50929900d0 100644 --- a/eth2/beacon/types/deposit_input.py +++ b/eth2/beacon/types/deposit_input.py @@ -28,8 +28,6 @@ class DepositInput(rlp.Serializable): ('withdrawal_credentials', hash32), # Initial RANDAO commitment ('randao_commitment', hash32), - # Initial proof of custody commitment - ('custody_commitment', hash32), # BLS proof of possession (a BLS signature) ('proof_of_possession', binary), ] @@ -38,13 +36,11 @@ def __init__(self, pubkey: BLSPubkey, withdrawal_credentials: Hash32, randao_commitment: Hash32, - custody_commitment: Hash32, proof_of_possession: BLSSignature=EMPTY_SIGNATURE) -> None: super().__init__( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, proof_of_possession=proof_of_possession, ) diff --git a/eth2/beacon/types/exits.py b/eth2/beacon/types/exits.py index be86f4d854..5043c9ab87 100644 --- a/eth2/beacon/types/exits.py +++ b/eth2/beacon/types/exits.py @@ -3,7 +3,6 @@ binary, ) from eth2.beacon.sedes import ( - uint24, uint64, ) from eth2.beacon.typing import ( @@ -22,7 +21,7 @@ class Exit(rlp.Serializable): # Minimum epoch for processing exit ('epoch', uint64), # Index of the exiting validator - ('validator_index', uint24), + ('validator_index', uint64), # Validator signature ('signature', binary), ] diff --git a/eth2/beacon/types/proposer_slashings.py b/eth2/beacon/types/proposer_slashings.py index 5320fa610b..e0af722f35 100644 --- a/eth2/beacon/types/proposer_slashings.py +++ b/eth2/beacon/types/proposer_slashings.py @@ -3,7 +3,7 @@ binary, ) from eth2.beacon.sedes import ( - uint24, + uint64, ) from .proposal_signed_data import ProposalSignedData from eth2.beacon.typing import ( @@ -16,7 +16,7 @@ class ProposerSlashing(rlp.Serializable): fields = [ # Proposer index - ('proposer_index', uint24), + ('proposer_index', uint64), # First proposal data ('proposal_data_1', ProposalSignedData), # First proposal signature diff --git a/eth2/beacon/types/shard_reassignment_records.py b/eth2/beacon/types/shard_reassignment_records.py deleted file mode 100644 index a4ddc800e2..0000000000 --- a/eth2/beacon/types/shard_reassignment_records.py +++ /dev/null @@ -1,35 +0,0 @@ -import rlp - -from eth2.beacon.sedes import ( - uint24, - uint64, -) -from eth2.beacon.typing import ( - SlotNumber, - ShardNumber, - ValidatorIndex, -) - - -class ShardReassignmentRecord(rlp.Serializable): - """ - Note: using RLP until we have standardized serialization format. - """ - fields = [ - # Which validator to reassign - ('validator_index', uint24), - # To which shard - ('shard', uint64), - # When - ('slot', uint64), - ] - - def __init__(self, - validator_index: ValidatorIndex, - shard: ShardNumber, - slot: SlotNumber)-> None: - super().__init__( - validator_index=validator_index, - shard=shard, - slot=slot, - ) diff --git a/eth2/beacon/types/slashable_attestations.py b/eth2/beacon/types/slashable_attestations.py index a46bc005f2..a58aeb92f1 100644 --- a/eth2/beacon/types/slashable_attestations.py +++ b/eth2/beacon/types/slashable_attestations.py @@ -1,18 +1,22 @@ +from typing import ( + Sequence, + Tuple, +) + import rlp from rlp.sedes import ( binary, CountableList, ) -from typing import ( - Sequence, - Tuple, -) from eth_typing import ( Hash32, ) +from eth2._utils.bitfield import ( + has_voted, +) from eth2.beacon._utils.hash import hash_eth2 from eth2.beacon.sedes import ( - uint24, + uint64, ) from eth2.beacon.typing import ( BLSSignature, @@ -29,25 +33,25 @@ class SlashableAttestation(rlp.Serializable): Note: using RLP until we have standardized serialization format. """ fields = [ - # Validator indices with custody bit equal to 0 - ('custody_bit_0_indices', CountableList(uint24)), - # Validator indices with custody bit equal to 1 - ('custody_bit_1_indices', CountableList(uint24)), + # Validator indices + ('validator_indices', CountableList(uint64)), # Attestation data ('data', AttestationData), + # Custody bitfield + ('custody_bitfield', binary), # Aggregate signature ('aggregate_signature', binary), ] def __init__(self, - custody_bit_0_indices: Sequence[ValidatorIndex], - custody_bit_1_indices: Sequence[ValidatorIndex], + validator_indices: Sequence[ValidatorIndex], data: AttestationData, + custody_bitfield: bytes, aggregate_signature: BLSSignature = EMPTY_SIGNATURE) -> None: super().__init__( - custody_bit_0_indices, - custody_bit_1_indices, + validator_indices, data, + custody_bitfield, aggregate_signature, ) @@ -65,15 +69,28 @@ def root(self) -> Hash32: # Using flat hash, will likely use SSZ tree hash. return self.hash - _vote_count = None + @property + def is_custody_bitfield_empty(self) -> bool: + return self.custody_bitfield == b'\x00' * len(self.custody_bitfield) @property - def vote_count(self) -> int: - if self._vote_count is None: - count_zero_indices = len(self.custody_bit_0_indices) - count_one_indices = len(self.custody_bit_1_indices) - self._vote_count = count_zero_indices + count_one_indices - return self._vote_count + def is_validator_indices_ascending(self) -> bool: + for i in range(len(self.validator_indices) - 1): + if self.validator_indices[i] >= self.validator_indices[i + 1]: + return False + return True + + @property + def custody_bit_indices(self) -> Tuple[Tuple[ValidatorIndex, ...], Tuple[ValidatorIndex, ...]]: + custody_bit_0_indices = () # type: Tuple[ValidatorIndex, ...] + custody_bit_1_indices = () # type: Tuple[ValidatorIndex, ...] + for i, validator_index in enumerate(self.validator_indices): + if not has_voted(self.custody_bitfield, i): + custody_bit_0_indices += (validator_index,) + else: + custody_bit_1_indices += (validator_index,) + + return (custody_bit_0_indices, custody_bit_1_indices) @property def messages(self) -> Tuple[Hash32, Hash32]: diff --git a/eth2/beacon/types/states.py b/eth2/beacon/types/states.py index b4cf2fd752..3280245024 100644 --- a/eth2/beacon/types/states.py +++ b/eth2/beacon/types/states.py @@ -23,7 +23,6 @@ ) from eth2.beacon.helpers import slot_to_epoch from eth2.beacon.sedes import ( - uint24, uint64, hash32, ) @@ -38,11 +37,9 @@ from .eth1_data import Eth1Data from .eth1_data_vote import Eth1DataVote -from .custody_challenges import CustodyChallenge from .crosslink_records import CrosslinkRecord from .forks import Fork from .pending_attestation_records import PendingAttestationRecord -from .shard_reassignment_records import ShardReassignmentRecord from .validator_records import ValidatorRecord @@ -64,11 +61,6 @@ class BeaconState(rlp.Serializable): # Randomness and committees ('latest_randao_mixes', CountableList(hash32)), - ('latest_vdf_outputs', CountableList(hash32)), - - # TODO Remove `persistent_committee_reassignments` - ('persistent_committees', CountableList(CountableList(uint24))), - ('persistent_committee_reassignments', CountableList(ShardReassignmentRecord)), ('previous_epoch_start_shard', uint64), ('current_epoch_start_shard', uint64), ('previous_calculation_epoch', uint64), @@ -76,9 +68,6 @@ class BeaconState(rlp.Serializable): ('previous_epoch_seed', hash32), ('current_epoch_seed', hash32), - # Custody challenges - ('custody_challenges', CountableList(CustodyChallenge)), - # Finality ('previous_justified_epoch', uint64), ('justified_epoch', uint64), @@ -115,17 +104,12 @@ def __init__( validator_registry_exit_count: int, # Randomness and committees latest_randao_mixes: Sequence[Hash32], - latest_vdf_outputs: Sequence[Hash32], - persistent_committees: Sequence[Sequence[ValidatorIndex]], - persistent_committee_reassignments: Sequence[ShardReassignmentRecord], previous_epoch_start_shard: ShardNumber, current_epoch_start_shard: ShardNumber, previous_calculation_epoch: EpochNumber, current_calculation_epoch: EpochNumber, previous_epoch_seed: Hash32, current_epoch_seed: Hash32, - # Custody challenges - custody_challenges: Sequence[CustodyChallenge], # Finality previous_justified_epoch: EpochNumber, justified_epoch: EpochNumber, @@ -157,17 +141,12 @@ def __init__( validator_registry_exit_count=validator_registry_exit_count, # Randomness and committees latest_randao_mixes=latest_randao_mixes, - latest_vdf_outputs=latest_vdf_outputs, - persistent_committees=persistent_committees, - persistent_committee_reassignments=persistent_committee_reassignments, previous_epoch_start_shard=previous_epoch_start_shard, current_epoch_start_shard=current_epoch_start_shard, previous_calculation_epoch=previous_calculation_epoch, current_calculation_epoch=current_calculation_epoch, previous_epoch_seed=previous_epoch_seed, current_epoch_seed=current_epoch_seed, - # Proof of Custody - custody_challenges=custody_challenges, # Finality previous_justified_epoch=previous_justified_epoch, justified_epoch=justified_epoch, @@ -246,9 +225,6 @@ def create_filled_state(cls, ZERO_HASH32 for _ in range(latest_randao_mixes_length) ), - latest_vdf_outputs=(), - persistent_committees=(), - persistent_committee_reassignments=(), previous_epoch_start_shard=genesis_start_shard, current_epoch_start_shard=genesis_start_shard, previous_calculation_epoch=genesis_epoch, @@ -256,9 +232,6 @@ def create_filled_state(cls, previous_epoch_seed=ZERO_HASH32, current_epoch_seed=ZERO_HASH32, - # Custody challenges - custody_challenges=(), - # Finality previous_justified_epoch=genesis_epoch, justified_epoch=genesis_epoch, diff --git a/eth2/beacon/types/validator_records.py b/eth2/beacon/types/validator_records.py index e586371b2e..26ac330e09 100644 --- a/eth2/beacon/types/validator_records.py +++ b/eth2/beacon/types/validator_records.py @@ -14,9 +14,8 @@ FAR_FUTURE_EPOCH, ) from eth2.beacon.typing import ( - EpochNumber, BLSPubkey, - SlotNumber, + EpochNumber, ) @@ -45,12 +44,6 @@ class ValidatorRecord(rlp.Serializable): ('exit_count', uint64), # Status flags ('status_flags', uint64), - # Proof of custody commitment - ('custody_commitment', hash32), - # Slot of latest custody reseed - ('latest_custody_reseed_slot', uint64), - # Slot of second-latest custody reseed - ('penultimate_custody_reseed_slot', uint64), ] def __init__(self, @@ -63,10 +56,7 @@ def __init__(self, withdrawal_epoch: EpochNumber, penalized_epoch: EpochNumber, exit_count: int, - status_flags: int, - custody_commitment: Hash32, - latest_custody_reseed_slot: SlotNumber, - penultimate_custody_reseed_slot: SlotNumber) -> None: + status_flags: int) -> None: super().__init__( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, @@ -78,9 +68,6 @@ def __init__(self, penalized_epoch=penalized_epoch, exit_count=exit_count, status_flags=status_flags, - custody_commitment=custody_commitment, - latest_custody_reseed_slot=latest_custody_reseed_slot, - penultimate_custody_reseed_slot=penultimate_custody_reseed_slot, ) def is_active(self, epoch: EpochNumber) -> bool: @@ -93,8 +80,7 @@ def is_active(self, epoch: EpochNumber) -> bool: def create_pending_validator(cls, pubkey: BLSPubkey, withdrawal_credentials: Hash32, - randao_commitment: Hash32, - custody_commitment: Hash32) -> 'ValidatorRecord': + randao_commitment: Hash32) -> 'ValidatorRecord': """ Return a new pending ``ValidatorRecord`` with the given fields. """ @@ -109,7 +95,4 @@ def create_pending_validator(cls, penalized_epoch=FAR_FUTURE_EPOCH, exit_count=0, status_flags=0, - custody_commitment=custody_commitment, - latest_custody_reseed_slot=SlotNumber(0), - penultimate_custody_reseed_slot=SlotNumber(0), ) diff --git a/eth2/beacon/typing.py b/eth2/beacon/typing.py index 75ae5bc2ca..a3b7abc67c 100644 --- a/eth2/beacon/typing.py +++ b/eth2/beacon/typing.py @@ -13,7 +13,7 @@ Bitfield = NewType('Bitfield', bytes) # uint64 -ValidatorIndex = NewType('ValidatorIndex', int) # uint24 +ValidatorIndex = NewType('ValidatorIndex', int) # uint64 CommitteeIndex = NewType('CommitteeIndex', int) Gwei = NewType('Gwei', int) # uint64 diff --git a/eth2/beacon/validation.py b/eth2/beacon/validation.py index 587b58ade0..47fb65d3d9 100644 --- a/eth2/beacon/validation.py +++ b/eth2/beacon/validation.py @@ -8,6 +8,11 @@ validate_is_integer, ) +from eth2._utils.bitfield import ( + get_bitfield_length, + has_voted, +) + from eth2.beacon.typing import ( EpochNumber, ) @@ -68,3 +73,19 @@ def validate_epoch_for_active_index_root(state_epoch: EpochNumber, raise ValidationError( f"given_epoch ({given_epoch}) should be less than or equal to state_epoch {state_epoch}" ) + + +def validate_bitfield(bitfield: bytes, committee_size: int) -> None: + """ + Verify ``bitfield`` against the ``committee_size``. + """ + if len(bitfield) != get_bitfield_length(committee_size): + raise ValidationError( + f"len(bitfield) ({len(bitfield)}) != " + f"get_bitfield_length(committee_size) ({get_bitfield_length(committee_size)}), " + f"where committee_size={committee_size}" + ) + + for i in range(committee_size, len(bitfield) * 8): + if has_voted(bitfield, i): + raise ValidationError(f"bit ({i}) should be zero") diff --git a/tests/eth2/beacon/conftest.py b/tests/eth2/beacon/conftest.py index d1cd4a1636..913568a582 100644 --- a/tests/eth2/beacon/conftest.py +++ b/tests/eth2/beacon/conftest.py @@ -120,9 +120,6 @@ def sample_beacon_block_body_params(): 'proposer_slashings': (), 'attester_slashings': (), 'attestations': (), - 'custody_reseeds': (), - 'custody_challenges': (), - 'custody_responses': (), 'deposits': (), 'exits': (), } @@ -153,16 +150,12 @@ def sample_beacon_state_params(sample_fork_params, sample_eth1_data_params): 'validator_registry_update_epoch': 0, 'validator_registry_exit_count': 10, 'latest_randao_mixes': (), - 'latest_vdf_outputs': (), - 'persistent_committees': (), - 'persistent_committee_reassignments': (), 'previous_epoch_start_shard': 1, 'current_epoch_start_shard': 2, 'previous_calculation_epoch': 0, 'current_calculation_epoch': 0, 'previous_epoch_seed': b'\x77' * 32, 'current_epoch_seed': b'\x88' * 32, - 'custody_challenges': (), 'previous_justified_epoch': 0, 'justified_epoch': 0, 'justification_bitfield': 0, @@ -208,7 +201,6 @@ def sample_deposit_input_params(): 'pubkey': 123, 'withdrawal_credentials': b'\11' * 32, 'randao_commitment': b'\11' * 32, - 'custody_commitment': ZERO_HASH32, 'proof_of_possession': (0, 0), } @@ -277,21 +269,12 @@ def sample_recent_proposer_record_params(): } -@pytest.fixture -def sample_shard_reassignment_record(): - return { - 'validator_index': 10, - 'shard': 11, - 'slot': 12, - } - - @pytest.fixture def sample_slashable_attestation_params(sample_attestation_data_params): return { - 'custody_bit_0_indices': (10, 11, 12, 15, 28), - 'custody_bit_1_indices': (7, 8, 100, 131, 249), + 'validator_indices': (10, 11, 12, 15, 28), 'data': AttestationData(**sample_attestation_data_params), + 'custody_bitfield': b'\00' * 4, 'aggregate_signature': EMPTY_SIGNATURE, } @@ -318,9 +301,6 @@ def sample_validator_record_params(): 'penalized_epoch': FAR_FUTURE_EPOCH, 'exit_count': 0, 'status_flags': 0, - 'custody_commitment': ZERO_HASH32, - 'latest_custody_reseed_slot': 0, - 'penultimate_custody_reseed_slot': 0, } diff --git a/tests/eth2/beacon/helpers.py b/tests/eth2/beacon/helpers.py index 086c912747..42e921b050 100644 --- a/tests/eth2/beacon/helpers.py +++ b/tests/eth2/beacon/helpers.py @@ -28,9 +28,6 @@ def mock_validator_record(pubkey, penalized_epoch=FAR_FUTURE_EPOCH, exit_count=0, status_flags=status_flags, - custody_commitment=b'\x55' * 32, - latest_custody_reseed_slot=0, - penultimate_custody_reseed_slot=0, ) diff --git a/tests/eth2/beacon/test_beacon_validation.py b/tests/eth2/beacon/test_beacon_validation.py index 1c37cf6879..7a2a01e7b9 100644 --- a/tests/eth2/beacon/test_beacon_validation.py +++ b/tests/eth2/beacon/test_beacon_validation.py @@ -1,10 +1,21 @@ import pytest +from hypothesis import ( + given, + strategies as st, +) + from eth_utils import ( ValidationError, ) +from eth2._utils.bitfield import ( + get_bitfield_length, + get_empty_bitfield, + set_voted, +) from eth2.beacon.validation import ( + validate_bitfield, validate_slot, validate_epoch_for_current_epoch, ) @@ -76,3 +87,43 @@ def test_validate_epoch_for_current_epoch( genesis_epoch=genesis_epoch, epoch_length=epoch_length ) + + +@pytest.mark.parametrize( + ( + 'is_valid' + ), + [ + (True), + (False), + ] +) +@given(committee_size=st.integers(0, 1000)) +def test_validate_bitfield_bitfield_length(committee_size, is_valid): + if is_valid: + testing_committee_size = committee_size + else: + testing_committee_size = committee_size + 1 + + bitfield = get_empty_bitfield(testing_committee_size) + + if not is_valid and len(bitfield) != get_bitfield_length(committee_size): + with pytest.raises(ValidationError): + validate_bitfield(bitfield, committee_size) + else: + validate_bitfield(bitfield, committee_size) + + +@given(committee_size=st.integers(0, 1000)) +def test_validate_bitfield_padding_zero(committee_size): + + bitfield = get_empty_bitfield(committee_size) + for index in range(committee_size): + bitfield = set_voted(bitfield, index) + + if committee_size % 8 != 0: + bitfield = set_voted(bitfield, committee_size) + with pytest.raises(ValidationError): + validate_bitfield(bitfield, committee_size) + else: + validate_bitfield(bitfield, committee_size) diff --git a/tests/eth2/beacon/test_deposit_helpers.py b/tests/eth2/beacon/test_deposit_helpers.py index 6563bf0310..13899c7ba2 100644 --- a/tests/eth2/beacon/test_deposit_helpers.py +++ b/tests/eth2/beacon/test_deposit_helpers.py @@ -58,14 +58,12 @@ def test_validate_proof_of_possession( privkey = privkeys[0] pubkey = pubkeys[0] withdrawal_credentials = b'\x34' * 32 - custody_commitment = b'\x12' * 32 randao_commitment = b'\x56' * 32 deposit_input = DepositInput( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, ) if expected is True: proof_of_possession = sign_proof_of_possession( @@ -82,7 +80,6 @@ def test_validate_proof_of_possession( proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, epoch_length=epoch_length, ) else: @@ -94,7 +91,6 @@ def test_validate_proof_of_possession( proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, epoch_length=epoch_length, ) @@ -113,14 +109,12 @@ def test_process_deposit(epoch_length, pubkey_1 = pubkeys[0] amount = max_deposit_amount withdrawal_credentials = b'\x34' * 32 - custody_commitment = b'\x11' * 32 randao_commitment = b'\x56' * 32 deposit_input = DepositInput( pubkey=pubkey_1, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, ) proof_of_possession = sign_proof_of_possession( deposit_input, @@ -138,7 +132,6 @@ def test_process_deposit(epoch_length, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, epoch_length=epoch_length, ) @@ -158,7 +151,6 @@ def test_process_deposit(epoch_length, pubkey=pubkey_2, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, ) proof_of_possession = sign_proof_of_possession( deposit_input, @@ -174,7 +166,6 @@ def test_process_deposit(epoch_length, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, epoch_length=epoch_length, ) assert len(result_state.validator_registry) == 2 diff --git a/tests/eth2/beacon/test_helpers.py b/tests/eth2/beacon/test_helpers.py index 6f5a357e94..f3e26e3a29 100644 --- a/tests/eth2/beacon/test_helpers.py +++ b/tests/eth2/beacon/test_helpers.py @@ -14,10 +14,7 @@ big_endian_to_int, ValidationError, ) -from eth2._utils.bitfield import ( - set_voted, - get_empty_bitfield, -) + from eth_utils.toolz import ( assoc, isdistinct, @@ -26,6 +23,11 @@ from eth.constants import ( ZERO_HASH32, ) + +from eth2._utils.bitfield import ( + get_empty_bitfield, + set_voted, +) from eth2.beacon._utils.hash import ( hash_eth2, ) @@ -74,9 +76,8 @@ is_double_vote, is_surround_vote, slot_to_epoch, - verify_vote_count, + validate_slashable_attestation, verify_slashable_attestation_signature, - verify_slashable_attestation, ) import eth2._utils.bls as bls @@ -576,7 +577,7 @@ def mock_get_crosslink_committees_at_slot(state, get_attestation_participants( state=sample_state, attestation_data=attestation_data, - aggregation_bitfield=aggregation_bitfield, + bitfield=aggregation_bitfield, genesis_epoch=genesis_epoch, epoch_length=epoch_length, target_committee_size=target_committee_size, @@ -586,7 +587,7 @@ def mock_get_crosslink_committees_at_slot(state, result = get_attestation_participants( state=sample_state, attestation_data=attestation_data, - aggregation_bitfield=aggregation_bitfield, + bitfield=aggregation_bitfield, genesis_epoch=genesis_epoch, epoch_length=epoch_length, target_committee_size=target_committee_size, @@ -1040,7 +1041,7 @@ def _list_and_index(data, max_size=None, elements=st.integers()): """ Hypothesis helper function cribbed from their docs on @composite """ - xs = data.draw(st.lists(elements, max_size=max_size)) + xs = data.draw(st.lists(elements, max_size=max_size, unique=True)) i = data.draw(st.integers(min_value=0, max_value=max(len(xs) - 1, 0))) return (xs, i) @@ -1050,24 +1051,31 @@ def test_generate_aggregate_pubkeys(activated_genesis_validators, sample_slashable_attestation_params, data): max_value_for_list = len(activated_genesis_validators) - 1 - (indices, some_index) = _list_and_index( + (validator_indices, some_index) = _list_and_index( data, elements=st.integers( min_value=0, max_value=max_value_for_list, ) ) - custody_bit_0_indices = indices[:some_index] - custody_bit_1_indices = indices[some_index:] - key = "custody_bit_0_indices" - sample_slashable_attestation_params[key] = custody_bit_0_indices - key = "custody_bit_1_indices" - sample_slashable_attestation_params[key] = custody_bit_1_indices + key = "validator_indices" + sample_slashable_attestation_params[key] = validator_indices + + custody_bitfield = get_empty_bitfield(len(validator_indices)) + for index in range(some_index): + custody_bitfield = set_voted(custody_bitfield, index) + + key = "custody_bitfield" + sample_slashable_attestation_params[key] = custody_bitfield - votes = SlashableAttestation(**sample_slashable_attestation_params) + slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params) + custody_bit_0_indices, custody_bit_1_indices = slashable_attestation.custody_bit_indices + assert len( + set(custody_bit_0_indices).intersection(set(custody_bit_1_indices)) + ) == 0 - keys = generate_aggregate_pubkeys(activated_genesis_validators, votes) + keys = generate_aggregate_pubkeys(activated_genesis_validators, slashable_attestation) assert len(keys) == 2 (poc_0_key, poc_1_key) = keys @@ -1079,28 +1087,12 @@ def test_generate_aggregate_pubkeys(activated_genesis_validators, assert bls.aggregate_pubkeys(poc_1_keys) == poc_1_key -@given(st.data()) -def test_verify_vote_count(max_indices_per_slashable_vote, - sample_slashable_attestation_params, - data): - (indices, some_index) = _list_and_index(data, max_size=max_indices_per_slashable_vote) - custody_bit_0_indices = indices[:some_index] - custody_bit_1_indices = indices[some_index:] - - key = "custody_bit_0_indices" - sample_slashable_attestation_params[key] = custody_bit_0_indices - key = "custody_bit_1_indices" - sample_slashable_attestation_params[key] = custody_bit_1_indices - - votes = SlashableAttestation(**sample_slashable_attestation_params) - - assert verify_vote_count(votes, max_indices_per_slashable_vote) - - def _get_indices_and_signatures(num_validators, message, privkeys, fork, epoch): num_indices = 5 assert num_validators >= num_indices indices = random.sample(range(num_validators), num_indices) + indices.sort() + privkeys = [privkeys[i] for i in indices] domain_type = SignatureDomain.DOMAIN_ATTESTATION domain = get_domain( @@ -1123,28 +1115,17 @@ def _correct_slashable_attestation_params( fork): valid_params = copy.deepcopy(params) - key = "custody_bit_0_indices" - (poc_0_indices, poc_0_signatures) = _get_indices_and_signatures( - num_validators, - messages[0], - privkeys, - fork, - slot_to_epoch(params["data"].slot, epoch_length), - ) - valid_params[key] = poc_0_indices - - key = "custody_bit_1_indices" - # NOTE: does not guarantee non-empty intersection - (poc_1_indices, poc_1_signatures) = _get_indices_and_signatures( + (validator_indices, signatures) = _get_indices_and_signatures( num_validators, messages[1], privkeys, fork, slot_to_epoch(params["data"].slot, epoch_length), ) - valid_params[key] = poc_1_indices - signatures = poc_0_signatures + poc_1_signatures + valid_params["validator_indices"] = validator_indices + valid_params["custody_bitfield"] = get_empty_bitfield(len(validator_indices)) + aggregate_signature = bls.aggregate_signatures(signatures) valid_params["aggregate_signature"] = aggregate_signature @@ -1152,6 +1133,36 @@ def _correct_slashable_attestation_params( return valid_params +def _corrupt_custody_bitfield_not_empty(params): + validator_indices_length = len(params["validator_indices"]) + corrupt_custody_bitfield = get_empty_bitfield(validator_indices_length) + corrupt_custody_bitfield = set_voted(corrupt_custody_bitfield, 0) + return assoc(params, "custody_bitfield", corrupt_custody_bitfield) + + +def _corrupt_validator_indices(params): + corrupt_validator_indices = ( + params["validator_indices"][1], + params["validator_indices"][0], + ) + tuple(params["validator_indices"][2:]) + + return assoc(params, "validator_indices", corrupt_validator_indices) + + +def _corrupt_custody_bitfield_invalid(params): + validator_indices_length = len(params["validator_indices"]) + corrupt_custody_bitfield = get_empty_bitfield(validator_indices_length + 8) + return assoc(params, "custody_bitfield", corrupt_custody_bitfield) + + +def _corrupt_validator_indices_max(max_indices_per_slashable_vote, params): + corrupt_validator_indices = [ + i + for i in range(max_indices_per_slashable_vote + 1) + ] + return assoc(params, "validator_indices", corrupt_validator_indices) + + def _corrupt_signature(epoch_length, params, fork): message = bytes.fromhex("deadbeefcafe") privkey = 42 @@ -1166,20 +1177,6 @@ def _corrupt_signature(epoch_length, params, fork): return assoc(params, "aggregate_signature", corrupt_signature) -def _corrupt_vote_count(params): - key = "custody_bit_0_indices" - for i in itertools.count(): - if i not in params[key]: - new_vote_count = params[key] + [i] - return assoc( - params, - key, - new_vote_count, - ) - else: - raise Exception("Unreachable code path") - - def _create_slashable_attestation_messages(params): # TODO update when we move to `ssz` tree hash votes = SlashableAttestation(**params) @@ -1236,16 +1233,16 @@ def _run_verify_slashable_vote( max_indices_per_slashable_vote, should_succeed): votes = SlashableAttestation(**params) - result = verify_slashable_attestation( - state, - votes, - max_indices_per_slashable_vote, - epoch_length, - ) if should_succeed: - assert result + validate_slashable_attestation(state, votes, max_indices_per_slashable_vote, epoch_length) else: - assert not result + with pytest.raises(ValidationError): + validate_slashable_attestation( + state, + votes, + max_indices_per_slashable_vote, + epoch_length, + ) @pytest.mark.parametrize( @@ -1261,22 +1258,24 @@ def _run_verify_slashable_vote( 'param_mapper', 'should_succeed', 'needs_fork', + 'is_testing_max_length', ), [ - (lambda params: params, True, False), - (_corrupt_vote_count, False, False), - (_corrupt_signature, False, True), - (lambda epoch_length, params, fork: _corrupt_vote_count( - _corrupt_signature(epoch_length, params, fork) - ), False, True), + (lambda params: params, True, False, False), + (_corrupt_custody_bitfield_not_empty, False, False, False), + (_corrupt_validator_indices, False, False, False), + (_corrupt_custody_bitfield_invalid, False, False, False), + (_corrupt_validator_indices_max, False, False, True), + (_corrupt_signature, False, True, False), ], ) -def test_verify_slashable_attestation( +def test_validate_slashable_attestation( epoch_length, num_validators, param_mapper, should_succeed, needs_fork, + is_testing_max_length, privkeys, sample_beacon_state_params, activated_genesis_validators, @@ -1304,6 +1303,9 @@ def test_verify_slashable_attestation( ) if needs_fork: params = param_mapper(epoch_length, params, state.fork) + elif is_testing_max_length: + params = param_mapper(max_indices_per_slashable_vote, params) + else: params = param_mapper(params) _run_verify_slashable_vote( diff --git a/tests/eth2/beacon/test_on_startup.py b/tests/eth2/beacon/test_on_startup.py index aa388d504c..cff04f020d 100644 --- a/tests/eth2/beacon/test_on_startup.py +++ b/tests/eth2/beacon/test_on_startup.py @@ -68,7 +68,6 @@ def test_get_initial_beacon_state( sample_eth1_data_params): withdrawal_credentials = b'\x22' * 32 randao_commitment = b'\x33' * 32 - custody_commitment = b'\x44' * 32 fork = Fork( previous_version=genesis_fork_version, current_version=genesis_fork_version, @@ -89,13 +88,11 @@ def test_get_initial_beacon_state( pubkey=pubkeys[i], withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, proof_of_possession=sign_proof_of_possession( deposit_input=DepositInput( pubkey=pubkeys[i], withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, ), privkey=privkeys[i], fork=fork, @@ -146,20 +143,12 @@ def test_get_initial_beacon_state( # Randomness and committees assert len(state.latest_randao_mixes) == latest_randao_mixes_length - assert len(state.latest_vdf_outputs) == latest_randao_mixes_length // epoch_length - - # TODO: `persistent_committees`, `persistent_committee_reassignments` will be removed - assert len(state.persistent_committees) == 0 - assert len(state.persistent_committee_reassignments) == 0 assert state.previous_epoch_start_shard == genesis_start_shard assert state.current_epoch_start_shard == genesis_start_shard assert state.previous_calculation_epoch == genesis_epoch assert state.current_calculation_epoch == genesis_epoch assert state.previous_epoch_seed == ZERO_HASH32 - # Custody challenges - assert len(state.custody_challenges) == 0 - # Finality assert state.previous_justified_epoch == genesis_epoch assert state.justified_epoch == genesis_epoch diff --git a/tests/eth2/beacon/types/test_attester_slashings.py b/tests/eth2/beacon/types/test_attester_slashings.py index 642d9e932f..a91bef31b1 100644 --- a/tests/eth2/beacon/types/test_attester_slashings.py +++ b/tests/eth2/beacon/types/test_attester_slashings.py @@ -6,12 +6,12 @@ def test_defaults(sample_attester_slashing_params): attester_slashing = AttesterSlashing(**sample_attester_slashing_params) assert ( - attester_slashing.slashable_attestation_1.custody_bit_0_indices == - sample_attester_slashing_params['slashable_attestation_1'].custody_bit_0_indices + attester_slashing.slashable_attestation_1.validator_indices == + sample_attester_slashing_params['slashable_attestation_1'].validator_indices ) assert ( - attester_slashing.slashable_attestation_2.custody_bit_1_indices == - sample_attester_slashing_params['slashable_attestation_2'].custody_bit_1_indices + attester_slashing.slashable_attestation_2.custody_bitfield == + sample_attester_slashing_params['slashable_attestation_2'].custody_bitfield ) assert rlp.encode(attester_slashing) diff --git a/tests/eth2/beacon/types/test_block.py b/tests/eth2/beacon/types/test_block.py index 6f2fb2e4e5..f688c51d15 100644 --- a/tests/eth2/beacon/types/test_block.py +++ b/tests/eth2/beacon/types/test_block.py @@ -10,7 +10,6 @@ def test_defaults(sample_beacon_block_params): block = BeaconBlock(**sample_beacon_block_params) assert block.slot == sample_beacon_block_params['slot'] - assert len(block.body.custody_challenges) == 0 def test_update_attestations(sample_attestation_params, sample_beacon_block_params): @@ -32,9 +31,6 @@ def test_block_body_empty(sample_attestation_params): assert block_body.proposer_slashings == () assert block_body.attester_slashings == () assert block_body.attestations == () - assert block_body.custody_reseeds == () - assert block_body.custody_challenges == () - assert block_body.custody_responses == () assert block_body.deposits == () assert block_body.exits == () diff --git a/tests/eth2/beacon/types/test_shard_reassignment_record.py b/tests/eth2/beacon/types/test_shard_reassignment_record.py deleted file mode 100644 index 9834c12b78..0000000000 --- a/tests/eth2/beacon/types/test_shard_reassignment_record.py +++ /dev/null @@ -1,10 +0,0 @@ -from eth2.beacon.types.shard_reassignment_records import ( - ShardReassignmentRecord, -) - - -def test_defaults(sample_shard_reassignment_record): - shard_reassignment = ShardReassignmentRecord(**sample_shard_reassignment_record) - assert shard_reassignment.validator_index == sample_shard_reassignment_record['validator_index'] - assert shard_reassignment.shard == sample_shard_reassignment_record['shard'] - assert shard_reassignment.slot == sample_shard_reassignment_record['slot'] diff --git a/tests/eth2/beacon/types/test_slashable_attestation.py b/tests/eth2/beacon/types/test_slashable_attestation.py index 846a1337e0..381c2bd503 100644 --- a/tests/eth2/beacon/types/test_slashable_attestation.py +++ b/tests/eth2/beacon/types/test_slashable_attestation.py @@ -1,3 +1,5 @@ +import pytest + import rlp from eth2.beacon.types.attestation_data_and_custody_bits import ( @@ -11,10 +13,10 @@ def test_defaults(sample_slashable_attestation_params): slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params) - assert (slashable_attestation.custody_bit_0_indices == - sample_slashable_attestation_params['custody_bit_0_indices']) - assert (slashable_attestation.custody_bit_1_indices == - sample_slashable_attestation_params['custody_bit_1_indices']) + assert (slashable_attestation.validator_indices == + sample_slashable_attestation_params['validator_indices']) + assert (slashable_attestation.custody_bitfield == + sample_slashable_attestation_params['custody_bitfield']) assert slashable_attestation.data == sample_slashable_attestation_params['data'] assert ( slashable_attestation.aggregate_signature == @@ -45,17 +47,66 @@ def test_root(sample_slashable_attestation_params): assert slashable_attestation.root == slashable_attestation.hash -def test_vote_count(sample_slashable_attestation_params): - slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params) - - key = "custody_bit_0_indices" - custody_bit_0_indices = sample_slashable_attestation_params[key] - key = "custody_bit_1_indices" - custody_bit_1_indices = sample_slashable_attestation_params[key] - - assert slashable_attestation.vote_count == ( - len(custody_bit_0_indices) + len(custody_bit_1_indices) +@pytest.mark.parametrize( + ( + 'custody_bitfield', + 'is_custody_bitfield_empty' + ), + [ + (b'\x00\x00', True), + (b'\x00\x01', False), + ], +) +def test_is_custody_bitfield_empty(sample_slashable_attestation_params, + custody_bitfield, + is_custody_bitfield_empty): + slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params).copy( + custody_bitfield=custody_bitfield, + ) + assert slashable_attestation.is_custody_bitfield_empty == is_custody_bitfield_empty + + +@pytest.mark.parametrize( + ( + 'validator_indices', + 'is_validator_indices_ascending' + ), + [ + ((0, 1, 2), True), + ((0, 2, 1), False), + ], +) +def test_is_validator_indices_ascending( + sample_slashable_attestation_params, + validator_indices, + is_validator_indices_ascending): + slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params).copy( + validator_indices=validator_indices, + ) + assert slashable_attestation.is_validator_indices_ascending == is_validator_indices_ascending + + +@pytest.mark.parametrize( + ( + 'validator_indices', + 'custody_bitfield', + 'custody_bit_indices' + ), + [ + ((0, 1, 2), b'\x80', ((1, 2), (0,))), + ((0, 1, 2), b'\xC0', ((2,), (0, 1))), + ], +) +def test_custody_bit_indices( + sample_slashable_attestation_params, + validator_indices, + custody_bitfield, + custody_bit_indices): + slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params).copy( + validator_indices=validator_indices, + custody_bitfield=custody_bitfield, ) + assert slashable_attestation.custody_bit_indices == custody_bit_indices def test_messages(sample_slashable_attestation_params): diff --git a/tests/eth2/beacon/types/test_validator_record.py b/tests/eth2/beacon/types/test_validator_record.py index e807157eb6..92f78c8ab9 100644 --- a/tests/eth2/beacon/types/test_validator_record.py +++ b/tests/eth2/beacon/types/test_validator_record.py @@ -41,13 +41,11 @@ def test_create_pending_validator(): pubkey = 123 withdrawal_credentials = b'\x11' * 32 randao_commitment = b'\x22' * 32 - custody_commitment = b'\x33' * 32 validator = ValidatorRecord.create_pending_validator( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - custody_commitment=custody_commitment, ) assert validator.pubkey == pubkey