diff --git a/eth2/beacon/state_machines/forks/serenity/block_validation.py b/eth2/beacon/state_machines/forks/serenity/block_validation.py index 2995fc60f3..b2830a1730 100644 --- a/eth2/beacon/state_machines/forks/serenity/block_validation.py +++ b/eth2/beacon/state_machines/forks/serenity/block_validation.py @@ -50,7 +50,7 @@ from eth2.beacon.types.blocks import BaseBeaconBlock # noqa: F401 from eth2.beacon.types.crosslink_records import CrosslinkRecord from eth2.beacon.types.forks import Fork # noqa: F401 -from eth2.beacon.types.proposal_signed_data import ProposalSignedData +from eth2.beacon.types.proposal import Proposal from eth2.beacon.types.slashable_attestations import SlashableAttestation # noqa: F401 from eth2.beacon.types.proposer_slashings import ProposerSlashing from eth2.beacon.types.states import BeaconState # noqa: F401 @@ -92,12 +92,13 @@ def validate_proposer_signature(state: BeaconState, committee_config: CommitteeConfig) -> None: block_without_signature_root = block.block_without_signature_root - # TODO: Replace this root with tree hash root - proposal_root = ProposalSignedData( + # TODO: Replace this with signed_root + proposal = Proposal( state.slot, beacon_chain_shard_number, block_without_signature_root, - ).root + signature=block.signature, + ) # Get the public key of proposer beacon_proposer_index = get_beacon_proposer_index( @@ -114,15 +115,15 @@ def validate_proposer_signature(state: BeaconState, is_valid_signature = bls.verify( pubkey=proposer_pubkey, - message_hash=proposal_root, - signature=block.signature, + message_hash=proposal.signed_root, + signature=proposal.signature, domain=domain, ) if not is_valid_signature: raise ValidationError( f"Invalid Proposer Signature on block, beacon_proposer_index={beacon_proposer_index}, " - f"pubkey={proposer_pubkey}, message_hash={proposal_root}," + f"pubkey={proposer_pubkey}, message_hash={proposal.signed_root}, " f"block.signature={block.signature}, domain={domain}" ) @@ -148,16 +149,14 @@ def validate_proposer_slashing(state: BeaconState, validate_proposer_slashing_is_slashed(proposer.slashed) validate_proposal_signature( - proposal_signed_data=proposer_slashing.proposal_data_1, - proposal_signature=proposer_slashing.proposal_signature_1, + proposal=proposer_slashing.proposal_1, pubkey=proposer.pubkey, fork=state.fork, slots_per_epoch=slots_per_epoch, ) validate_proposal_signature( - proposal_signed_data=proposer_slashing.proposal_data_2, - proposal_signature=proposer_slashing.proposal_signature_2, + proposal=proposer_slashing.proposal_2, pubkey=proposer.pubkey, fork=state.fork, slots_per_epoch=slots_per_epoch, @@ -165,29 +164,29 @@ def validate_proposer_slashing(state: BeaconState, def validate_proposer_slashing_slot(proposer_slashing: ProposerSlashing) -> None: - if proposer_slashing.proposal_data_1.slot != proposer_slashing.proposal_data_2.slot: + if proposer_slashing.proposal_1.slot != proposer_slashing.proposal_2.slot: raise ValidationError( - f"proposer_slashing.proposal_data_1.slot ({proposer_slashing.proposal_data_1.slot}) !=" - f" proposer_slashing.proposal_data_2.slot ({proposer_slashing.proposal_data_2.slot})" + f"proposer_slashing.proposal_1.slot ({proposer_slashing.proposal_1.slot}) !=" + f" proposer_slashing.proposal_2.slot ({proposer_slashing.proposal_2.slot})" ) def validate_proposer_slashing_shard(proposer_slashing: ProposerSlashing) -> None: - if proposer_slashing.proposal_data_1.shard != proposer_slashing.proposal_data_2.shard: + if proposer_slashing.proposal_1.shard != proposer_slashing.proposal_2.shard: raise ValidationError( - f"proposer_slashing.proposal_data_1.shard ({proposer_slashing.proposal_data_1.shard}) " - f"!= proposer_slashing.proposal_data_2.shard" - f" ({proposer_slashing.proposal_data_2.shard})" + f"proposer_slashing.proposal_1.shard ({proposer_slashing.proposal_1.shard}) " + f"!= proposer_slashing.proposal_2.shard" + f" ({proposer_slashing.proposal_2.shard})" ) def validate_proposer_slashing_block_root(proposer_slashing: ProposerSlashing) -> None: - if proposer_slashing.proposal_data_1.block_root == proposer_slashing.proposal_data_2.block_root: + if proposer_slashing.proposal_1.block_root == proposer_slashing.proposal_2.block_root: raise ValidationError( - "proposer_slashing.proposal_data_1.block_root " - f"({proposer_slashing.proposal_data_1.block_root}) " - "should not be equal to proposer_slashing.proposal_data_2.block_root " - f"({proposer_slashing.proposal_data_2.block_root})" + "proposer_slashing.proposal_1.block_root " + f"({proposer_slashing.proposal_1.block_root}) " + "should not be equal to proposer_slashing.proposal_2.block_root " + f"({proposer_slashing.proposal_2.block_root})" ) @@ -196,26 +195,25 @@ def validate_proposer_slashing_is_slashed(slashed: bool) -> None: raise ValidationError(f"proposer.slashed is True") -def validate_proposal_signature(proposal_signed_data: ProposalSignedData, - proposal_signature: BLSSignature, +def validate_proposal_signature(proposal: Proposal, pubkey: BLSPubkey, fork: Fork, slots_per_epoch: int) -> None: proposal_signature_is_valid = bls.verify( pubkey=pubkey, - message_hash=proposal_signed_data.root, # TODO: use hash_tree_root - signature=proposal_signature, + message_hash=proposal.signed_root, # TODO: use signed_root + signature=proposal.signature, domain=get_domain( fork, - slot_to_epoch(proposal_signed_data.slot, slots_per_epoch), + slot_to_epoch(proposal.slot, slots_per_epoch), SignatureDomain.DOMAIN_PROPOSAL, ) ) if not proposal_signature_is_valid: raise ValidationError( "Proposal signature is invalid: " - f"proposer pubkey: {pubkey}, message_hash: {proposal_signed_data.root}, " - f"signature: {proposal_signature}" + f"proposer pubkey: {pubkey}, message_hash: {proposal.signed_root}, " + f"signature: {proposal.signature}" ) diff --git a/eth2/beacon/tools/builder/proposer.py b/eth2/beacon/tools/builder/proposer.py index 1c5a065bb8..c00f24eeec 100644 --- a/eth2/beacon/tools/builder/proposer.py +++ b/eth2/beacon/tools/builder/proposer.py @@ -30,7 +30,7 @@ BeaconBlockBody, ) from eth2.beacon.types.eth1_data import Eth1Data -from eth2.beacon.types.proposal_signed_data import ProposalSignedData +from eth2.beacon.types.proposal import Proposal from eth2.beacon.types.states import BeaconState from eth2.beacon.typing import ( BLSPubkey, @@ -103,7 +103,7 @@ def create_block_on_state( # Sign empty_signature_block_root = block.block_without_signature_root - proposal_root = ProposalSignedData( + proposal_root = Proposal( slot, config.BEACON_CHAIN_SHARD_NUMBER, empty_signature_block_root, diff --git a/eth2/beacon/tools/builder/validator.py b/eth2/beacon/tools/builder/validator.py index 5f6a8bd454..25fdaae1b0 100644 --- a/eth2/beacon/tools/builder/validator.py +++ b/eth2/beacon/tools/builder/validator.py @@ -55,7 +55,7 @@ from eth2.beacon.types.attester_slashings import AttesterSlashing from eth2.beacon.types.deposit_input import DepositInput from eth2.beacon.types.forks import Fork -from eth2.beacon.types.proposal_signed_data import ProposalSignedData +from eth2.beacon.types.proposal import Proposal from eth2.beacon.types.proposer_slashings import ProposerSlashing from eth2.beacon.types.slashable_attestations import SlashableAttestation from eth2.beacon.types.states import BeaconState @@ -176,21 +176,22 @@ def create_proposal_data_and_signature( block_root: Hash32, privkey: int, slots_per_epoch: int, - beacon_chain_shard_number: Shard)-> Tuple[ProposalSignedData, BLSSignature]: - proposal_data = ProposalSignedData( + beacon_chain_shard_number: Shard)-> Proposal: + proposal = Proposal( state.slot, beacon_chain_shard_number, block_root, ) proposal_signature = sign_transaction( - message_hash=proposal_data.root, + message_hash=proposal.signed_root, privkey=privkey, fork=state.fork, - slot=proposal_data.slot, + slot=proposal.slot, signature_domain=SignatureDomain.DOMAIN_PROPOSAL, slots_per_epoch=slots_per_epoch, ) - return proposal_data, proposal_signature + proposal = proposal.copy(signature=proposal_signature) + return proposal # @@ -213,7 +214,7 @@ def create_mock_proposer_slashing_at_block( slots_per_epoch = config.SLOTS_PER_EPOCH beacon_chain_shard_number = config.BEACON_CHAIN_SHARD_NUMBER - proposal_data_1, proposal_signature_1 = create_proposal_data_and_signature( + proposal_1 = create_proposal_data_and_signature( state, block_root_1, keymap[state.validator_registry[proposer_index].pubkey], @@ -221,7 +222,7 @@ def create_mock_proposer_slashing_at_block( beacon_chain_shard_number, ) - proposal_data_2, proposal_signature_2 = create_proposal_data_and_signature( + proposal_2 = create_proposal_data_and_signature( state, block_root_2, keymap[state.validator_registry[proposer_index].pubkey], @@ -231,10 +232,8 @@ def create_mock_proposer_slashing_at_block( return ProposerSlashing( proposer_index=proposer_index, - proposal_data_1=proposal_data_1, - proposal_data_2=proposal_data_2, - proposal_signature_1=proposal_signature_1, - proposal_signature_2=proposal_signature_2, + proposal_1=proposal_1, + proposal_2=proposal_2, ) diff --git a/eth2/beacon/types/proposal_signed_data.py b/eth2/beacon/types/proposal.py similarity index 55% rename from eth2/beacon/types/proposal_signed_data.py rename to eth2/beacon/types/proposal.py index b13084a279..8dbb20f96c 100644 --- a/eth2/beacon/types/proposal_signed_data.py +++ b/eth2/beacon/types/proposal.py @@ -6,38 +6,48 @@ from ssz.sedes import ( bytes32, uint64, + bytes96, ) from eth2.beacon._utils.hash import hash_eth2 +from eth2.beacon.constants import ( + EMPTY_SIGNATURE, +) from eth2.beacon.typing import ( + BLSSignature, Slot, Shard, ) -class ProposalSignedData(ssz.Serializable): +class Proposal(ssz.Serializable): fields = [ # Slot number ('slot', uint64), - # Shard number (or `2**64 - 1` for beacon chain) + # Shard number (`BEACON_CHAIN_SHARD_NUMBER` for beacon chain) ('shard', uint64), - # block root + # Block root ('block_root', bytes32), + # Signature + ('signature', bytes96) ] def __init__(self, slot: Slot, shard: Shard, - block_root: Hash32) -> None: + block_root: Hash32, + signature: BLSSignature=EMPTY_SIGNATURE) -> None: super().__init__( slot, shard, block_root, + signature, ) _hash = None + _signed_root = None @property def hash(self) -> Hash32: @@ -50,3 +60,10 @@ def root(self) -> Hash32: # Alias of `hash`. # Using flat hash, might change to SSZ tree hash. return self.hash + + @property + def signed_root(self) -> Hash32: + # Use SSZ built-in function + if self._signed_root is None: + self._signed_root = hash_eth2(ssz.encode(self.copy(signature=EMPTY_SIGNATURE))) + return self._signed_root diff --git a/eth2/beacon/types/proposer_slashings.py b/eth2/beacon/types/proposer_slashings.py index bd7db9ebf6..c50a24e917 100644 --- a/eth2/beacon/types/proposer_slashings.py +++ b/eth2/beacon/types/proposer_slashings.py @@ -1,15 +1,12 @@ import ssz from ssz.sedes import ( - bytes_sedes, uint64, ) -from .proposal_signed_data import ProposalSignedData +from .proposal import Proposal from eth2.beacon.typing import ( - BLSSignature, ValidatorIndex, ) -from eth2.beacon.constants import EMPTY_SIGNATURE class ProposerSlashing(ssz.Serializable): @@ -17,27 +14,18 @@ class ProposerSlashing(ssz.Serializable): fields = [ # Proposer index ('proposer_index', uint64), - # First proposal data - ('proposal_data_1', ProposalSignedData), - # First proposal signature - ('proposal_signature_1', bytes_sedes), - # Second proposal data - ('proposal_data_2', ProposalSignedData), - # Second proposal signature - ('proposal_signature_2', bytes_sedes), + # First proposal + ('proposal_1', Proposal), + # Second proposal + ('proposal_2', Proposal), ] def __init__(self, proposer_index: ValidatorIndex, - proposal_data_1: ProposalSignedData, - proposal_data_2: ProposalSignedData, - # default arguments follow non-default arguments - proposal_signature_1: BLSSignature = EMPTY_SIGNATURE, - proposal_signature_2: BLSSignature = EMPTY_SIGNATURE) -> None: + proposal_1: Proposal, + proposal_2: Proposal) -> None: super().__init__( proposer_index=proposer_index, - proposal_data_1=proposal_data_1, - proposal_data_2=proposal_data_2, - proposal_signature_1=proposal_signature_1, - proposal_signature_2=proposal_signature_2, + proposal_1=proposal_1, + proposal_2=proposal_2, ) diff --git a/tests/eth2/beacon/conftest.py b/tests/eth2/beacon/conftest.py index bdb19120f8..9e153cbc4d 100644 --- a/tests/eth2/beacon/conftest.py +++ b/tests/eth2/beacon/conftest.py @@ -24,7 +24,7 @@ from eth2.beacon.types.deposit_data import DepositData from eth2.beacon.types.deposit_input import DepositInput from eth2.beacon.types.eth1_data import Eth1Data -from eth2.beacon.types.proposal_signed_data import ProposalSignedData +from eth2.beacon.types.proposal import Proposal from eth2.beacon.types.slashable_attestations import SlashableAttestation from eth2.beacon.types.states import BeaconState @@ -81,14 +81,12 @@ def pubkeys(keymap): @pytest.fixture -def sample_proposer_slashing_params(sample_proposal_signed_data_params): - proposal_data = ProposalSignedData(**sample_proposal_signed_data_params) +def sample_proposer_slashing_params(sample_proposal_params): + proposal_data = Proposal(**sample_proposal_params) return { 'proposer_index': 1, - 'proposal_data_1': proposal_data, - 'proposal_signature_1': EMPTY_SIGNATURE, - 'proposal_data_2': proposal_data, - 'proposal_signature_2': EMPTY_SIGNATURE, + 'proposal_1': proposal_data, + 'proposal_2': proposal_data, } @@ -266,11 +264,12 @@ def sample_pending_attestation_record_params(sample_attestation_data_params): @pytest.fixture -def sample_proposal_signed_data_params(): +def sample_proposal_params(): return { 'slot': 10, 'shard': 12, 'block_root': b'\x43' * 32, + 'signature': b'\x56' * 96, } diff --git a/tests/eth2/beacon/state_machines/forks/test_serenity_block_proposer_slashing_validation.py b/tests/eth2/beacon/state_machines/forks/test_serenity_block_proposer_slashing_validation.py index 12cd0b2645..691e275542 100644 --- a/tests/eth2/beacon/state_machines/forks/test_serenity_block_proposer_slashing_validation.py +++ b/tests/eth2/beacon/state_machines/forks/test_serenity_block_proposer_slashing_validation.py @@ -56,11 +56,11 @@ def test_validate_proposer_slashing_slot(genesis_state, # Valid validate_proposer_slashing_slot(valid_proposer_slashing) - proposal_data_1 = valid_proposer_slashing.proposal_data_1.copy( - slot=valid_proposer_slashing.proposal_data_2.slot + 1 + proposal_1 = valid_proposer_slashing.proposal_1.copy( + slot=valid_proposer_slashing.proposal_2.slot + 1 ) invalid_proposer_slashing = valid_proposer_slashing.copy( - proposal_data_1=proposal_data_1, + proposal_1=proposal_1, ) # Invalid @@ -81,11 +81,11 @@ def test_validate_proposer_slashing_shard(genesis_state, # Valid validate_proposer_slashing_shard(valid_proposer_slashing) - proposal_data_1 = valid_proposer_slashing.proposal_data_1.copy( - shard=valid_proposer_slashing.proposal_data_2.shard + 1 + proposal_1 = valid_proposer_slashing.proposal_1.copy( + shard=valid_proposer_slashing.proposal_2.shard + 1 ) invalid_proposer_slashing = valid_proposer_slashing.copy( - proposal_data_1=proposal_data_1, + proposal_1=proposal_1, ) # Invalid @@ -106,11 +106,11 @@ def test_validate_proposer_slashing_block_root(genesis_state, # Valid validate_proposer_slashing_block_root(valid_proposer_slashing) - proposal_data_1 = valid_proposer_slashing.proposal_data_1.copy( - block_root=valid_proposer_slashing.proposal_data_2.block_root + proposal_1 = valid_proposer_slashing.proposal_1.copy( + block_root=valid_proposer_slashing.proposal_2.block_root ) invalid_proposer_slashing = valid_proposer_slashing.copy( - proposal_data_1=proposal_data_1, + proposal_1=proposal_1, ) # Invalid @@ -156,8 +156,7 @@ def test_validate_proposal_signature(slots_per_epoch, # Valid validate_proposal_signature( - proposal_signed_data=valid_proposer_slashing.proposal_data_1, - proposal_signature=valid_proposer_slashing.proposal_signature_1, + proposal=valid_proposer_slashing.proposal_1, pubkey=proposer.pubkey, fork=state.fork, slots_per_epoch=slots_per_epoch, @@ -168,8 +167,7 @@ def test_validate_proposal_signature(slots_per_epoch, wrong_proposer = state.validator_registry[wrong_proposer_index] with pytest.raises(ValidationError): validate_proposal_signature( - proposal_signed_data=valid_proposer_slashing.proposal_data_1, - proposal_signature=valid_proposer_slashing.proposal_signature_1, + proposal=valid_proposer_slashing.proposal_1, pubkey=wrong_proposer.pubkey, fork=state.fork, slots_per_epoch=slots_per_epoch, diff --git a/tests/eth2/beacon/state_machines/forks/test_serenity_block_validation.py b/tests/eth2/beacon/state_machines/forks/test_serenity_block_validation.py index 7691f2bbac..320ea73235 100644 --- a/tests/eth2/beacon/state_machines/forks/test_serenity_block_validation.py +++ b/tests/eth2/beacon/state_machines/forks/test_serenity_block_validation.py @@ -38,8 +38,8 @@ verify_slashable_attestation_signature, ) from eth2.beacon.types.blocks import BeaconBlock -from eth2.beacon.types.proposal_signed_data import ( - ProposalSignedData, +from eth2.beacon.types.proposal import ( + Proposal, ) from eth2.beacon.types.forks import Fork from eth2.beacon.types.slashable_attestations import SlashableAttestation @@ -113,15 +113,15 @@ def test_validate_proposer_signature( default_block = BeaconBlock(**sample_beacon_block_params) empty_signature_block_root = default_block.block_without_signature_root - proposal_root = ProposalSignedData( + proposal_signed_root = Proposal( state.slot, beacon_chain_shard_number, empty_signature_block_root, - ).root + ).signed_root proposed_block = BeaconBlock(**sample_beacon_block_params).copy( signature=bls.sign( - message_hash=proposal_root, + message_hash=proposal_signed_root, privkey=proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), diff --git a/tests/eth2/beacon/types/test_proposal.py b/tests/eth2/beacon/types/test_proposal.py new file mode 100644 index 0000000000..9e91b3587f --- /dev/null +++ b/tests/eth2/beacon/types/test_proposal.py @@ -0,0 +1,10 @@ +from eth2.beacon.types.proposal import ( + Proposal, +) + + +def test_defaults(sample_proposal_params): + proposal = Proposal(**sample_proposal_params) + assert proposal.slot == sample_proposal_params['slot'] + assert proposal.shard == sample_proposal_params['shard'] + assert proposal.block_root == sample_proposal_params['block_root'] diff --git a/tests/eth2/beacon/types/test_proposal_signed_data.py b/tests/eth2/beacon/types/test_proposal_signed_data.py deleted file mode 100644 index 988d6e4fd5..0000000000 --- a/tests/eth2/beacon/types/test_proposal_signed_data.py +++ /dev/null @@ -1,10 +0,0 @@ -from eth2.beacon.types.proposal_signed_data import ( - ProposalSignedData, -) - - -def test_defaults(sample_proposal_signed_data_params): - proposal_signed_data = ProposalSignedData(**sample_proposal_signed_data_params) - assert proposal_signed_data.slot == sample_proposal_signed_data_params['slot'] - assert proposal_signed_data.shard == sample_proposal_signed_data_params['shard'] - assert proposal_signed_data.block_root == sample_proposal_signed_data_params['block_root'] diff --git a/tests/eth2/beacon/types/test_proposer_slashings.py b/tests/eth2/beacon/types/test_proposer_slashings.py index 0ecfc85a7c..993adef58b 100644 --- a/tests/eth2/beacon/types/test_proposer_slashings.py +++ b/tests/eth2/beacon/types/test_proposer_slashings.py @@ -8,8 +8,6 @@ def test_defaults(sample_proposer_slashing_params): slashing = ProposerSlashing(**sample_proposer_slashing_params) assert slashing.proposer_index == sample_proposer_slashing_params['proposer_index'] - assert slashing.proposal_data_1 == sample_proposer_slashing_params['proposal_data_1'] - assert slashing.proposal_signature_1 == sample_proposer_slashing_params['proposal_signature_1'] - assert slashing.proposal_data_2 == sample_proposer_slashing_params['proposal_data_2'] - assert slashing.proposal_signature_2 == sample_proposer_slashing_params['proposal_signature_2'] + assert slashing.proposal_1 == sample_proposer_slashing_params['proposal_1'] + assert slashing.proposal_2 == sample_proposer_slashing_params['proposal_2'] assert ssz.encode(slashing)