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

Commit

Permalink
RANDAO processing (#252)
Browse files Browse the repository at this point in the history
* Add RANDAO signature domain

* Add epoch number helpers

* RANDAO validation

* RANDAO block processing

* Fix some type hints

* Fix block processing tests

* flake8

* Prettify imports

* Use keys from fixtures in tests

* Use state.current_epoch

* Remove unused import

* Fix after rebase

* Reslove conflicts
  • Loading branch information
jannikluhn authored and hwwhww committed Feb 17, 2019
1 parent cc76bfe commit b6667b6
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 3 deletions.
1 change: 1 addition & 0 deletions eth2/beacon/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ class SignatureDomain(IntEnum):
DOMAIN_ATTESTATION = 1
DOMAIN_PROPOSAL = 2
DOMAIN_EXIT = 3
DOMAIN_RANDAO = 4
1 change: 0 additions & 1 deletion eth2/beacon/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@

if TYPE_CHECKING:
from eth2.beacon.types.attestation_data import AttestationData # noqa: F401
from eth2.beacon.types.blocks import BaseBeaconBlock # noqa: F401
from eth2.beacon.types.states import BeaconState # noqa: F401
from eth2.beacon.types.forks import Fork # noqa: F401
from eth2.beacon.types.slashable_attestations import SlashableAttestation # noqa: F401
Expand Down
59 changes: 58 additions & 1 deletion eth2/beacon/state_machines/forks/serenity/block_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,30 @@
)

from eth2._utils.tuple import update_tuple_item
from eth2._utils.numeric import (
bitwise_xor,
)
from eth2.beacon._utils.hash import hash_eth2

from eth2.beacon.configs import BeaconConfig
from eth2.beacon.configs import (
BeaconConfig,
CommitteeConfig,
)
from eth2.beacon.types.states import BeaconState
from eth2.beacon.types.blocks import BaseBeaconBlock
from eth2.beacon.types.eth1_data_vote import Eth1DataVote

from eth2.beacon.state_machines.forks.serenity.block_validation import (
validate_randao_reveal,
)

from eth2.beacon.helpers import (
get_randao_mix,
)
from eth2.beacon.committee_helpers import (
get_beacon_proposer_index,
)


def process_eth1_data(state: BeaconState,
block: BaseBeaconBlock,
Expand Down Expand Up @@ -36,3 +54,42 @@ def process_eth1_data(state: BeaconState,
)

return state


def process_randao(state: BeaconState,
block: BaseBeaconBlock,
config: BeaconConfig) -> BeaconState:
proposer_index = get_beacon_proposer_index(
state=state,
slot=state.slot,
committee_config=CommitteeConfig(config),
)
proposer = state.validator_registry[proposer_index]

epoch = state.current_epoch(config.EPOCH_LENGTH)

validate_randao_reveal(
randao_reveal=block.randao_reveal,
proposer_pubkey=proposer.pubkey,
epoch=epoch,
fork=state.fork,
)

randao_mix_index = epoch % config.LATEST_RANDAO_MIXES_LENGTH
new_randao_mix = bitwise_xor(
get_randao_mix(
state=state,
epoch=epoch,
epoch_length=config.EPOCH_LENGTH,
latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH,
),
hash_eth2(block.randao_reveal),
)

return state.copy(
latest_randao_mixes=update_tuple_item(
state.latest_randao_mixes,
randao_mix_index,
new_randao_mix,
),
)
25 changes: 25 additions & 0 deletions eth2/beacon/state_machines/forks/serenity/block_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
from eth2.beacon.types.attestations import Attestation # noqa: F401
from eth2.beacon.types.attestation_data import AttestationData # noqa: F401
from eth2.beacon.types.proposal_signed_data import ProposalSignedData
from eth2.beacon.types.forks import Fork
from eth2.beacon.typing import (
BLSPubkey,
BLSSignature,
EpochNumber,
ShardNumber,
SlotNumber,
Expand Down Expand Up @@ -323,3 +326,25 @@ def validate_attestation_aggregate_signature(state: BeaconState,
domain,
)
)


def validate_randao_reveal(randao_reveal: BLSSignature,
proposer_pubkey: BLSPubkey,
epoch: EpochNumber,
fork: Fork) -> None:
message = epoch.to_bytes(32, byteorder="big")
domain = get_domain(fork, epoch, SignatureDomain.DOMAIN_RANDAO)

is_randao_reveal_valid = bls.verify(
pubkey=proposer_pubkey,
message=message,
signature=randao_reveal,
domain=domain,
)

if not is_randao_reveal_valid:
raise ValidationError(
f"RANDAO reveal is invalid. "
f"reveal={randao_reveal}, proposer_pubkey={proposer_pubkey}, message={message}, "
f"domain={domain}"
)
2 changes: 1 addition & 1 deletion eth2/beacon/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@


SlotNumber = NewType('SlotNumber', int) # uint64
ShardNumber = NewType('ShardNumber', int) # uint64
EpochNumber = NewType('EpochNumber', int) # uint64
ShardNumber = NewType('ShardNumber', int) # uint64
BLSPubkey = NewType('BLSPubkey', bytes) # bytes48
BLSSignature = NewType('BLSSignature', bytes) # bytes96

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,123 @@
import pytest

from eth.constants import (
ZERO_HASH32,
)
from eth_utils import (
ValidationError,
)
from eth_utils.toolz import (
first,
)

from eth2._utils import bls

from eth2.beacon.types.forks import Fork
from eth2.beacon.types.eth1_data_vote import Eth1DataVote
from eth2.beacon.types.states import BeaconState
from eth2.beacon.types.blocks import BeaconBlock
from eth2.beacon.enums import SignatureDomain

from eth2.beacon.helpers import (
get_domain,
)

from eth2.beacon.state_machines.forks.serenity.blocks import (
SerenityBeaconBlock,
)
from eth2.beacon.state_machines.forks.serenity.states import (
SerenityBeaconState,
)

from eth2.beacon.state_machines.forks.serenity.block_processing import (
process_randao,
)

from tests.eth2.beacon.helpers import (
mock_validator_record,
)

from eth2.beacon.state_machines.forks.serenity.block_processing import (
process_eth1_data,
)


def test_randao_processing(sample_beacon_block_params,
sample_beacon_state_params,
sample_fork_params,
keymap,
config):
proposer_pubkey, proposer_privkey = first(keymap.items())
state = SerenityBeaconState(**sample_beacon_state_params).copy(
validator_registry=tuple(
mock_validator_record(proposer_pubkey)
for _ in range(config.TARGET_COMMITTEE_SIZE)
),
validator_balances=(config.MAX_DEPOSIT_AMOUNT,) * config.TARGET_COMMITTEE_SIZE,

latest_randao_mixes=tuple(
ZERO_HASH32
for _ in range(config.LATEST_RANDAO_MIXES_LENGTH)
),
)

epoch = state.current_epoch(config.EPOCH_LENGTH)
slot = epoch * config.EPOCH_LENGTH
message = epoch.to_bytes(32, byteorder="big")
fork = Fork(**sample_fork_params)
domain = get_domain(fork, slot, SignatureDomain.DOMAIN_RANDAO)
randao_reveal = bls.sign(message, proposer_privkey, domain)

block = SerenityBeaconBlock(**sample_beacon_block_params).copy(
randao_reveal=randao_reveal,
)

new_state = process_randao(state, block, config)

updated_index = epoch % config.LATEST_RANDAO_MIXES_LENGTH
original_mixes = state.latest_randao_mixes
updated_mixes = new_state.latest_randao_mixes

assert all(
updated == original if index != updated_index else updated != original
for index, (updated, original) in enumerate(zip(updated_mixes, original_mixes))
)


def test_randao_processing_validates_randao_reveal(sample_beacon_block_params,
sample_beacon_state_params,
sample_fork_params,
keymap,
config):
proposer_pubkey, proposer_privkey = first(keymap.items())
state = SerenityBeaconState(**sample_beacon_state_params).copy(
validator_registry=tuple(
mock_validator_record(proposer_pubkey)
for _ in range(config.TARGET_COMMITTEE_SIZE)
),
validator_balances=(config.MAX_DEPOSIT_AMOUNT,) * config.TARGET_COMMITTEE_SIZE,

latest_randao_mixes=tuple(
ZERO_HASH32
for _ in range(config.LATEST_RANDAO_MIXES_LENGTH)
),
)

epoch = state.current_epoch(config.EPOCH_LENGTH)
slot = epoch * config.EPOCH_LENGTH
message = (epoch + 1).to_bytes(32, byteorder="big")
fork = Fork(**sample_fork_params)
domain = get_domain(fork, slot, SignatureDomain.DOMAIN_RANDAO)
randao_reveal = bls.sign(message, proposer_privkey, domain)

block = SerenityBeaconBlock(**sample_beacon_block_params).copy(
randao_reveal=randao_reveal,
)

with pytest.raises(ValidationError):
process_randao(state, block, config)


HASH1 = b"\x11" * 32
HASH2 = b"\x22" * 32

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@
ProposalSignedData,
)
from eth2.beacon.types.states import BeaconState
from eth2.beacon.types.forks import Fork

from eth2.beacon.helpers import (
get_domain,
)

from eth2.beacon.state_machines.forks.serenity.block_validation import (
validate_block_slot,
validate_proposer_signature,
validate_randao_reveal,
)

from tests.eth2.beacon.helpers import mock_validator_record
Expand Down Expand Up @@ -120,3 +126,46 @@ def test_validate_proposer_signature(
beacon_chain_shard_number,
CommitteeConfig(config),
)


@pytest.mark.parametrize(
["is_valid", "epoch", "expected_epoch", "proposer_key_index", "expected_proposer_key_index"],
(
(True, 0, 0, 0, 0),
(True, 1, 1, 1, 1),
(False, 0, 1, 0, 0),
(False, 0, 0, 0, 1),
)
)
def test_randao_reveal_validation(is_valid,
epoch,
expected_epoch,
proposer_key_index,
expected_proposer_key_index,
privkeys,
pubkeys,
sample_fork_params,
config):
message = epoch.to_bytes(32, byteorder="big")
slot = epoch * config.EPOCH_LENGTH
fork = Fork(**sample_fork_params)
domain = get_domain(fork, slot, SignatureDomain.DOMAIN_RANDAO)

proposer_privkey = privkeys[proposer_key_index]
randao_reveal = bls.sign(message, proposer_privkey, domain)

expected_proposer_pubkey = pubkeys[expected_proposer_key_index]

try:
validate_randao_reveal(
randao_reveal=randao_reveal,
proposer_pubkey=expected_proposer_pubkey,
epoch=expected_epoch,
fork=fork,
)
except ValidationError:
if is_valid:
raise
else:
if not is_valid:
pytest.fail("Did not raise")

0 comments on commit b6667b6

Please sign in to comment.