Skip to content
This repository has been archived by the owner on Jul 1, 2021. It is now read-only.

Rework ProposalSignedData to Proposal and signature refactoring #355

Merged
merged 4 commits into from
Mar 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 28 additions & 30 deletions eth2/beacon/state_machines/forks/serenity/block_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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}"
)

Expand All @@ -148,46 +149,44 @@ 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,
)


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})"
)


Expand All @@ -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}"
)


Expand Down
4 changes: 2 additions & 2 deletions eth2/beacon/tools/builder/proposer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
23 changes: 11 additions & 12 deletions eth2/beacon/tools/builder/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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


#
Expand All @@ -213,15 +214,15 @@ 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],
slots_per_epoch,
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],
Expand All @@ -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,
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
30 changes: 9 additions & 21 deletions eth2/beacon/types/proposer_slashings.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,31 @@
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):

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,
)
15 changes: 7 additions & 8 deletions tests/eth2/beacon/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
}


Expand Down Expand Up @@ -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,
}


Expand Down
Loading