diff --git a/eth2/beacon/state_machines/forks/serenity/block_validation.py b/eth2/beacon/state_machines/forks/serenity/block_validation.py index 5e52f76e4b..23c94176d0 100644 --- a/eth2/beacon/state_machines/forks/serenity/block_validation.py +++ b/eth2/beacon/state_machines/forks/serenity/block_validation.py @@ -89,12 +89,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 = Proposal( + # 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( @@ -111,15 +112,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}" ) @@ -146,7 +147,6 @@ def validate_proposer_slashing(state: BeaconState, validate_proposal_signature( proposal=proposer_slashing.proposal_1, - proposal_signature=proposer_slashing.proposal_signature_1, pubkey=proposer.pubkey, fork=state.fork, slots_per_epoch=slots_per_epoch, @@ -154,7 +154,6 @@ def validate_proposer_slashing(state: BeaconState, validate_proposal_signature( proposal=proposer_slashing.proposal_2, - proposal_signature=proposer_slashing.proposal_signature_2, pubkey=proposer.pubkey, fork=state.fork, slots_per_epoch=slots_per_epoch, @@ -194,14 +193,13 @@ def validate_proposer_slashing_is_slashed(slashed: bool) -> None: def validate_proposal_signature(proposal: Proposal, - proposal_signature: BLSSignature, pubkey: BLSPubkey, fork: Fork, slots_per_epoch: int) -> None: proposal_signature_is_valid = bls.verify( pubkey=pubkey, - message_hash=proposal.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.slot, slots_per_epoch), @@ -212,7 +210,7 @@ def validate_proposal_signature(proposal: Proposal, raise ValidationError( "Proposal signature is invalid: " f"proposer pubkey: {pubkey}, message_hash: {proposal.root}, " - f"signature: {proposal_signature}" + f"signature: {proposal.signature}" ) diff --git a/eth2/beacon/tools/builder/validator.py b/eth2/beacon/tools/builder/validator.py index 1745a3b7ff..afe1ad6845 100644 --- a/eth2/beacon/tools/builder/validator.py +++ b/eth2/beacon/tools/builder/validator.py @@ -172,21 +172,22 @@ def create_proposal_data_and_signature( block_root: Hash32, privkey: int, slots_per_epoch: int, - beacon_chain_shard_number: Shard)-> Tuple[Proposal, BLSSignature]: - proposal_data = Proposal( + 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 # @@ -209,7 +210,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_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], @@ -217,7 +218,7 @@ def create_mock_proposer_slashing_at_block( beacon_chain_shard_number, ) - proposal_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], @@ -229,8 +230,6 @@ def create_mock_proposer_slashing_at_block( proposer_index=proposer_index, proposal_1=proposal_1, proposal_2=proposal_2, - proposal_signature_1=proposal_signature_1, - proposal_signature_2=proposal_signature_2, ) diff --git a/eth2/beacon/types/proposal.py b/eth2/beacon/types/proposal.py index 467a7e11e9..8dbb20f96c 100644 --- a/eth2/beacon/types/proposal.py +++ b/eth2/beacon/types/proposal.py @@ -6,11 +6,16 @@ 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, ) @@ -21,23 +26,28 @@ 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 e00d5623e1..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 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 + # First proposal ('proposal_1', Proposal), - # First proposal signature - ('proposal_signature_1', bytes_sedes), - # Second proposal data + # Second proposal ('proposal_2', Proposal), - # Second proposal signature - ('proposal_signature_2', bytes_sedes), ] def __init__(self, proposer_index: ValidatorIndex, proposal_1: Proposal, - proposal_2: Proposal, - # default arguments follow non-default arguments - proposal_signature_1: BLSSignature = EMPTY_SIGNATURE, - proposal_signature_2: BLSSignature = EMPTY_SIGNATURE) -> None: + proposal_2: Proposal) -> None: super().__init__( proposer_index=proposer_index, proposal_1=proposal_1, proposal_2=proposal_2, - proposal_signature_1=proposal_signature_1, - proposal_signature_2=proposal_signature_2, ) diff --git a/tests/eth2/beacon/conftest.py b/tests/eth2/beacon/conftest.py index 5831947251..294f0ea4de 100644 --- a/tests/eth2/beacon/conftest.py +++ b/tests/eth2/beacon/conftest.py @@ -83,9 +83,7 @@ def sample_proposer_slashing_params(sample_proposal_params): return { 'proposer_index': 1, 'proposal_1': proposal_data, - 'proposal_signature_1': EMPTY_SIGNATURE, 'proposal_2': proposal_data, - 'proposal_signature_2': EMPTY_SIGNATURE, } @@ -263,6 +261,7 @@ def sample_proposal_params(): '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 2c752c45d1..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 @@ -157,7 +157,6 @@ def test_validate_proposal_signature(slots_per_epoch, # Valid validate_proposal_signature( proposal=valid_proposer_slashing.proposal_1, - proposal_signature=valid_proposer_slashing.proposal_signature_1, pubkey=proposer.pubkey, fork=state.fork, slots_per_epoch=slots_per_epoch, @@ -169,7 +168,6 @@ def test_validate_proposal_signature(slots_per_epoch, with pytest.raises(ValidationError): validate_proposal_signature( proposal=valid_proposer_slashing.proposal_1, - proposal_signature=valid_proposer_slashing.proposal_signature_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 9a1775172f..f6b91e9a09 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 @@ -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 = Proposal( + 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_proposer_slashings.py b/tests/eth2/beacon/types/test_proposer_slashings.py index 41520ece67..993adef58b 100644 --- a/tests/eth2/beacon/types/test_proposer_slashings.py +++ b/tests/eth2/beacon/types/test_proposer_slashings.py @@ -9,7 +9,5 @@ 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_1 == sample_proposer_slashing_params['proposal_1'] - assert slashing.proposal_signature_1 == sample_proposer_slashing_params['proposal_signature_1'] assert slashing.proposal_2 == sample_proposer_slashing_params['proposal_2'] - assert slashing.proposal_signature_2 == sample_proposer_slashing_params['proposal_signature_2'] assert ssz.encode(slashing)