Skip to content

Slashable vote data cleanup #1697

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 10, 2019
Merged
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
126 changes: 95 additions & 31 deletions tests/beacon/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
denoms,
ValidationError,
)
from eth_utils.toolz import assoc

from eth.constants import (
ZERO_HASH32,
Expand Down Expand Up @@ -779,28 +780,33 @@ def test_verify_vote_count(max_casper_votes, sample_slashable_vote_data_params,
assert verify_vote_count(votes, max_casper_votes)


def _get_indices_and_signatures(num_validators, message, privkeys):
def _get_indices_and_signatures(num_validators, message, privkeys, fork_data, slot):
num_indices = 5
assert num_validators >= num_indices
indices = random.sample(range(num_validators), num_indices)
privkeys = [privkeys[i] for i in indices]
domain = SignatureDomain.DOMAIN_ATTESTATION
domain_type = SignatureDomain.DOMAIN_ATTESTATION
domain = get_domain(
fork_data=fork_data,
slot=slot,
domain_type=domain_type,
)
signatures = tuple(
map(lambda key: bls.sign(message, key, domain), privkeys)
)
return (indices, signatures)


def _correct_slashable_vote_data_params(params, validators, messages, privkeys):
def _correct_slashable_vote_data_params(num_validators, params, messages, privkeys, fork_data):
valid_params = copy.deepcopy(params)

num_validators = len(validators)

key = "custody_bit_0_indices"
(poc_0_indices, poc_0_signatures) = _get_indices_and_signatures(
num_validators,
messages[0],
privkeys,
fork_data,
params["data"].slot,
)
valid_params[key] = poc_0_indices

Expand All @@ -810,6 +816,8 @@ def _correct_slashable_vote_data_params(params, validators, messages, privkeys):
num_validators,
messages[1],
privkeys,
fork_data,
params["data"].slot,
)
valid_params[key] = poc_1_indices

Expand All @@ -821,23 +829,32 @@ def _correct_slashable_vote_data_params(params, validators, messages, privkeys):
return valid_params


def _corrupt_signature(params):
params = copy.deepcopy(params)
def _corrupt_signature(params, fork_data):
message = bytes.fromhex("deadbeefcafe")
privkey = 42
domain = SignatureDomain.DOMAIN_ATTESTATION
params["aggregate_signature"] = bls.sign(message, privkey, domain)
return params
domain_type = SignatureDomain.DOMAIN_ATTESTATION
domain = get_domain(
fork_data=fork_data,
slot=params["data"].slot,
domain_type=domain_type,
)
corrupt_signature = bls.sign(message, privkey, domain)

return assoc(params, "aggregate_signature", corrupt_signature)


def _corrupt_vote_count(params):
params = copy.deepcopy(params)
key = "custody_bit_0_indices"
for i in itertools.count():
if i not in params[key]:
params[key].append(i)
break
return params
new_vote_count = params[key] + [i]
return assoc(
params,
key,
new_vote_count,
)
else:
raise Exception("Unreachable code path")


def _create_slashable_vote_data_messages(params):
Expand All @@ -846,27 +863,47 @@ def _create_slashable_vote_data_messages(params):
return votes.messages


def test_verify_slashable_vote_data_signature(privkeys,
@pytest.mark.parametrize(
(
'num_validators',
),
[
(40,),
]
)
def test_verify_slashable_vote_data_signature(num_validators,
privkeys,
sample_beacon_state_params,
genesis_validators,
sample_slashable_vote_data_params):
sample_beacon_state_params["validator_registry"] = genesis_validators
state = BeaconState(**sample_beacon_state_params)
sample_slashable_vote_data_params,
sample_fork_data_params):
beacon_state_params_with_genesis_validators = assoc(
sample_beacon_state_params,
"validator_registry",
genesis_validators,
)
beacon_state_params_with_fork_data = assoc(
beacon_state_params_with_genesis_validators,
"fork_data",
ForkData(**sample_fork_data_params),
)
state = BeaconState(**beacon_state_params_with_fork_data)

# NOTE: we can do this before "correcting" the params as they
# touch disjoint subsets of the provided params
messages = _create_slashable_vote_data_messages(sample_slashable_vote_data_params)

valid_params = _correct_slashable_vote_data_params(
num_validators,
sample_slashable_vote_data_params,
genesis_validators,
messages,
privkeys,
state.fork_data,
)
valid_votes = SlashableVoteData(**valid_params)
assert verify_slashable_vote_data_signature(state, valid_votes)

invalid_params = _corrupt_signature(valid_params)
invalid_params = _corrupt_signature(valid_params, state.fork_data)
invalid_votes = SlashableVoteData(**invalid_params)
assert not verify_slashable_vote_data_signature(state, invalid_votes)

Expand All @@ -882,37 +919,64 @@ def _run_verify_slashable_vote(params, state, max_casper_votes, should_succeed):

@pytest.mark.parametrize(
(
'param_mapper,'
'should_succeed'
'num_validators',
),
[
(lambda params: params, True),
(lambda params: _corrupt_vote_count(params), False),
(lambda params: _corrupt_signature(params), False),
(lambda params: _corrupt_vote_count(_corrupt_signature(params)), False),
(40,),
]
)
@pytest.mark.parametrize(
(
'param_mapper',
'should_succeed',
'needs_fork_data',
),
[
(lambda params: params, True, False),
(_corrupt_vote_count, False, False),
(_corrupt_signature, False, True),
(lambda params, fork_data: _corrupt_vote_count(
_corrupt_signature(params, fork_data)
), False, True),
],
)
def test_verify_slashable_vote_data(param_mapper,
def test_verify_slashable_vote_data(num_validators,
param_mapper,
should_succeed,
needs_fork_data,
privkeys,
sample_beacon_state_params,
genesis_validators,
sample_slashable_vote_data_params,
sample_fork_data_params,
max_casper_votes):
sample_beacon_state_params["validator_registry"] = genesis_validators
state = BeaconState(**sample_beacon_state_params)
beacon_state_params_with_genesis_validators = assoc(
sample_beacon_state_params,
"validator_registry",
genesis_validators,
)
beacon_state_params_with_fork_data = assoc(
beacon_state_params_with_genesis_validators,
"fork_data",
ForkData(**sample_fork_data_params),
)
state = BeaconState(**beacon_state_params_with_fork_data)

# NOTE: we can do this before "correcting" the params as they
# touch disjoint subsets of the provided params
messages = _create_slashable_vote_data_messages(sample_slashable_vote_data_params)

params = _correct_slashable_vote_data_params(
num_validators,
sample_slashable_vote_data_params,
genesis_validators,
messages,
privkeys,
state.fork_data,
)
params = param_mapper(params)
if needs_fork_data:
params = param_mapper(params, state.fork_data)
else:
params = param_mapper(params)
_run_verify_slashable_vote(params, state, max_casper_votes, should_succeed)


Expand Down