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

Commit

Permalink
Update AttesationData: latest_crosslink_root: Hash32 to `latest_c…
Browse files Browse the repository at this point in the history
…rosslink: CrosslinkRecord` (#351)

* Change `AttesationData.latest_crosslink_root` to
`AttestationData.latest_crosslink` and also change the type

* Apply PR feedback, thanks to Nic
  • Loading branch information
hwwhww authored Mar 2, 2019
1 parent c70315a commit 87a133f
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 66 deletions.
41 changes: 24 additions & 17 deletions eth2/beacon/state_machines/forks/serenity/block_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from eth2.beacon.types.attestation_data_and_custody_bits import AttestationDataAndCustodyBit
from eth2.beacon.types.attester_slashings import AttesterSlashing # noqa: F401
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.slashable_attestations import SlashableAttestation # noqa: F401
Expand Down Expand Up @@ -330,8 +331,9 @@ def validate_attestation(state: BeaconState,
)

validate_attestation_latest_crosslink_root(
attestation.data,
latest_crosslink_root=state.latest_crosslinks[attestation.data.shard].crosslink_data_root,
attestation_data=attestation.data,
state_latest_crosslink=state.latest_crosslinks[attestation.data.shard],
slots_per_epoch=slots_per_epoch,
)

validate_attestation_crosslink_data_root(attestation.data)
Expand Down Expand Up @@ -427,26 +429,31 @@ def validate_attestation_justified_block_root(attestation_data: AttestationData,


def validate_attestation_latest_crosslink_root(attestation_data: AttestationData,
latest_crosslink_root: Hash32) -> None:
state_latest_crosslink: CrosslinkRecord,
slots_per_epoch: int) -> None:
"""
Validate that either the attestation ``latest_crosslink_root`` or ``crosslink_data_root``
field of ``attestation_data`` is the provided ``latest_crosslink_root``.
Validate that either the attestation ``latest_crosslink`` or ``crosslink_data_root``
field of ``attestation_data`` is the provided ``latest_crosslink``.
Raise ``ValidationError`` if it's invalid.
"""
acceptable_crosslink_data_roots = {
attestation_data.latest_crosslink_root,
attestation_data.crosslink_data_root,
attestation_creating_crosslink = CrosslinkRecord(
epoch=slot_to_epoch(attestation_data.slot, slots_per_epoch),
crosslink_data_root=attestation_data.crosslink_data_root,
)
acceptable_crosslink_data = {
# Case 1: Latest crosslink matches the one in the state
attestation_data.latest_crosslink,
# Case 2: State has already been updated, state's latest crosslink matches the crosslink
# the attestation is trying to create
attestation_creating_crosslink,
}
if latest_crosslink_root not in acceptable_crosslink_data_roots:
if state_latest_crosslink not in acceptable_crosslink_data:
raise ValidationError(
"Neither the attestation ``latest_crosslink_root`` nor the attestation "
"``crosslink_data_root`` are equal to the ``latest_crosslink_root``.\n"
"\tFound: %s and %s, Expected %s" %
(
attestation_data.latest_crosslink_root,
attestation_data.crosslink_data_root,
latest_crosslink_root,
)
f"State's latests crosslink ({state_latest_crosslink}) doesn't match "
" case 1: the `attestation_data.latest_crosslink` "
f"({attestation_data.latest_crosslink.root}) or "
"`case 2: the crosslink the attestation is trying to create "
f"({attestation_creating_crosslink})"
)


Expand Down
8 changes: 4 additions & 4 deletions eth2/beacon/tools/builder/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,15 @@ def create_mock_slashable_attestation(state: BeaconState,
get_epoch_start_slot(state.justified_epoch, config.SLOTS_PER_EPOCH),
config.LATEST_BLOCK_ROOTS_LENGTH,
)
latest_crosslink_root = state.latest_crosslinks[shard].crosslink_data_root
latest_crosslink = state.latest_crosslinks[shard]

attestation_data = attestation_data = AttestationData(
slot=attestation_slot,
shard=shard,
beacon_block_root=beacon_block_root,
epoch_boundary_root=epoch_boundary_root,
crosslink_data_root=ZERO_HASH32,
latest_crosslink_root=latest_crosslink_root,
latest_crosslink=latest_crosslink,
justified_epoch=state.justified_epoch,
justified_block_root=justified_block_root,
)
Expand Down Expand Up @@ -493,15 +493,15 @@ def create_mock_signed_attestations_at_slot(
committee, shard = crosslink_committee

num_voted_attesters = int(len(committee) * voted_attesters_ratio)
latest_crosslink_root = state.latest_crosslinks[shard].crosslink_data_root
latest_crosslink = state.latest_crosslinks[shard]

attestation_data = AttestationData(
slot=attestation_slot,
shard=shard,
beacon_block_root=beacon_block_root,
epoch_boundary_root=epoch_boundary_root,
crosslink_data_root=ZERO_HASH32,
latest_crosslink_root=latest_crosslink_root,
latest_crosslink=latest_crosslink,
justified_epoch=state.justified_epoch,
justified_block_root=justified_block_root,
)
Expand Down
7 changes: 4 additions & 3 deletions eth2/beacon/types/attestation_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
Slot,
Shard,
)
from eth2.beacon.types.crosslink_records import CrosslinkRecord


class AttestationData(ssz.Serializable):
Expand All @@ -29,7 +30,7 @@ class AttestationData(ssz.Serializable):
# Shard block root being attested to
('crosslink_data_root', bytes32),
# Last crosslink hash
('latest_crosslink_root', bytes32),
('latest_crosslink', CrosslinkRecord),
# epoch of the last justified beacon block
('justified_epoch', uint64),
# Hash of the last justified beacon block
Expand All @@ -42,7 +43,7 @@ def __init__(self,
beacon_block_root: Hash32,
epoch_boundary_root: Hash32,
crosslink_data_root: Hash32,
latest_crosslink_root: Hash32,
latest_crosslink: CrosslinkRecord,
justified_epoch: Epoch,
justified_block_root: Hash32) -> None:
super().__init__(
Expand All @@ -51,7 +52,7 @@ def __init__(self,
beacon_block_root,
epoch_boundary_root,
crosslink_data_root,
latest_crosslink_root,
latest_crosslink,
justified_epoch,
justified_block_root,
)
15 changes: 15 additions & 0 deletions eth2/beacon/types/crosslink_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
bytes32,
)

from eth2.beacon._utils.hash import hash_eth2
from eth2.beacon.typing import Epoch


Expand All @@ -28,3 +29,17 @@ def __init__(self,
epoch=epoch,
crosslink_data_root=crosslink_data_root,
)

_hash = None

@property
def hash(self) -> Hash32:
if self._hash is None:
self._hash = hash_eth2(ssz.encode(self))
return self._hash

@property
def root(self) -> Hash32:
# Alias of `hash`.
# Using flat hash, will likely use SSZ tree hash.
return self.hash
14 changes: 7 additions & 7 deletions tests/core/p2p-proto/bcc/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

import ssz

from eth.constants import (
ZERO_HASH32,
)

from eth2.beacon.types.attestations import Attestation
from eth2.beacon.types.attestation_data import AttestationData
from eth2.beacon.types.blocks import (
BeaconBlock,
BeaconBlockBody,
)

from eth.constants import (
ZERO_HASH32,
)

from eth2.beacon.types.eth1_data import Eth1Data
from eth2.beacon.types.crosslink_records import CrosslinkRecord

from p2p.peer import (
MsgBuffer,
Expand Down Expand Up @@ -167,7 +167,7 @@ async def test_send_single_attestation(request, event_loop):
beacon_block_root=ZERO_HASH32,
epoch_boundary_root=ZERO_HASH32,
crosslink_data_root=ZERO_HASH32,
latest_crosslink_root=ZERO_HASH32,
latest_crosslink=CrosslinkRecord(SERENITY_CONFIG.GENESIS_EPOCH, ZERO_HASH32),
justified_epoch=SERENITY_CONFIG.GENESIS_EPOCH,
justified_block_root=ZERO_HASH32,
),
Expand All @@ -194,7 +194,7 @@ async def test_send_multiple_attestations(request, event_loop):
beacon_block_root=ZERO_HASH32,
epoch_boundary_root=ZERO_HASH32,
crosslink_data_root=ZERO_HASH32,
latest_crosslink_root=ZERO_HASH32,
latest_crosslink=CrosslinkRecord(SERENITY_CONFIG.GENESIS_EPOCH, ZERO_HASH32),
justified_epoch=SERENITY_CONFIG.GENESIS_EPOCH,
justified_block_root=ZERO_HASH32,
),
Expand Down
4 changes: 2 additions & 2 deletions tests/eth2/beacon/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ def sample_attestation_params(sample_attestation_data_params):


@pytest.fixture
def sample_attestation_data_params():
def sample_attestation_data_params(sample_crosslink_record_params):
return {
'slot': 10,
'shard': 12,
'beacon_block_root': b'\x11' * 32,
'epoch_boundary_root': b'\x22' * 32,
'crosslink_data_root': b'\x33' * 32,
'latest_crosslink_root': b'\x44' * 32,
'latest_crosslink': CrosslinkRecord(**sample_crosslink_record_params),
'justified_epoch': 0,
'justified_block_root': b'\x55' * 32,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
create_mock_signed_attestation,
)
from eth2.beacon.types.attestation_data import AttestationData
from eth2.beacon.types.crosslink_records import CrosslinkRecord


@pytest.mark.parametrize(
Expand Down Expand Up @@ -138,8 +139,8 @@ def test_validate_attestation_justified_epoch(
'is_valid,'
),
[
(b'\x42' * 32, b'\x35' * 32, False), # attestation.justified_block_root != justified_block_root # noqa: E501
(b'\x42' * 32, b'\x42' * 32, True),
(b'\x33' * 32, b'\x22' * 32, False), # attestation.justified_block_root != justified_block_root # noqa: E501
(b'\x33' * 32, b'\x33' * 32, True),
]
)
def test_validate_attestation_justified_block_root(sample_attestation_data_params,
Expand All @@ -165,41 +166,69 @@ def test_validate_attestation_justified_block_root(sample_attestation_data_param

@pytest.mark.parametrize(
(
'attestation_latest_crosslink_root,'
'attestation_latest_crosslink,'
'attestation_crosslink_data_root,'
'latest_crosslink_root,'
'state_latest_crosslink,'
'is_valid,'
),
[
(b'\x66' * 32, b'\x42' * 32, b'\x35' * 32, False),
(b'\x42' * 32, b'\x42' * 32, b'\x66' * 32, False),
(b'\x66' * 32, b'\x42' * 32, b'\x42' * 32, True),
(b'\x42' * 32, b'\x35' * 32, b'\x42' * 32, True),
(b'\x42' * 32, b'\x42' * 32, b'\x42' * 32, True),
(
CrosslinkRecord(0, b'\x11' * 32),
b'\x33' * 32,
CrosslinkRecord(0, b'\x22' * 32),
False,
),
(
CrosslinkRecord(0, b'\x33' * 32),
b'\x33' * 32,
CrosslinkRecord(0, b'\x11' * 32),
False,
),
(
CrosslinkRecord(0, b'\x11' * 32),
b'\x33' * 32,
CrosslinkRecord(0, b'\x33' * 32),
True,
),
(
CrosslinkRecord(0, b'\x33' * 32),
b'\x22' * 32,
CrosslinkRecord(0, b'\x33' * 32),
True,
),
(
CrosslinkRecord(0, b'\x33' * 32),
b'\x33' * 32,
CrosslinkRecord(0, b'\x33' * 32),
True,
),
]
)
def test_validate_attestation_latest_crosslink_root(sample_attestation_data_params,
attestation_latest_crosslink_root,
attestation_crosslink_data_root,
latest_crosslink_root,
is_valid):
sample_attestation_data_params['latest_crosslink_root'] = attestation_latest_crosslink_root
def test_validate_attestation_latest_crosslink(sample_attestation_data_params,
attestation_latest_crosslink,
attestation_crosslink_data_root,
state_latest_crosslink,
slots_per_epoch,
is_valid):
sample_attestation_data_params['latest_crosslink'] = attestation_latest_crosslink
sample_attestation_data_params['crosslink_data_root'] = attestation_crosslink_data_root
attestation_data = AttestationData(**sample_attestation_data_params).copy(
latest_crosslink_root=attestation_latest_crosslink_root,
latest_crosslink=attestation_latest_crosslink,
crosslink_data_root=attestation_crosslink_data_root,
)

if is_valid:
validate_attestation_latest_crosslink_root(
attestation_data,
latest_crosslink_root,
state_latest_crosslink,
slots_per_epoch=slots_per_epoch,
)
else:
with pytest.raises(ValidationError):
validate_attestation_latest_crosslink_root(
attestation_data,
latest_crosslink_root,
state_latest_crosslink,
slots_per_epoch=slots_per_epoch,
)


Expand All @@ -210,8 +239,8 @@ def test_validate_attestation_latest_crosslink_root(sample_attestation_data_para
),
[
(ZERO_HASH32, True),
(b'\x35' * 32, False),
(b'\x66' * 32, False),
(b'\x22' * 32, False),
(b'\x11' * 32, False),
]
)
def test_validate_attestation_crosslink_data_root(sample_attestation_data_params,
Expand Down
13 changes: 0 additions & 13 deletions tests/eth2/beacon/types/test_slashable_attestation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,6 @@ def test_defaults(sample_slashable_attestation_params):
assert ssz.encode(slashable_attestation)


def test_hash(sample_slashable_attestation_params):
slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params)

# NOTE: this hash was simply copied from the existing implementation
# which should be the keccak-256 of the rlp serialization of `votes`.
# Given that this value will change soon (cf. ssz tree hash), we just
# do this to get the test passing for now and will need to update later
# if we expect the hash computation is not working correctly
hash_hex = "8cba2c190eae977ba16cbcc7ef1b95b32384fd12e86292ca8a91cc320eaeac9c"

assert slashable_attestation.hash == bytes.fromhex(hash_hex)


def test_root(sample_slashable_attestation_params):
slashable_attestation = SlashableAttestation(**sample_slashable_attestation_params)

Expand Down

0 comments on commit 87a133f

Please sign in to comment.