more checking that various functions match v0.1; some variable/etc renaming; fix is_double_vote to check epoch, not slot equivalence; finish verify_slashable_attestation; adjust is_surround_vote logic (#100)

This commit is contained in:
Dustin Brody 2019-02-07 23:07:15 +00:00 committed by Jacek Sieka
parent c567bc410f
commit 4747477160
5 changed files with 70 additions and 34 deletions

View File

@ -10,11 +10,10 @@ import
../extras, ../ssz, ../extras, ../ssz,
./crypto, ./datatypes, ./digest, ./helpers, ./validator ./crypto, ./datatypes, ./digest, ./helpers, ./validator
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#get_effective_balance
func get_effective_balance*(state: BeaconState, index: ValidatorIndex): uint64 = func get_effective_balance*(state: BeaconState, index: ValidatorIndex): uint64 =
# Validators collect rewards which increases their balance but not their ## Return the effective balance (also known as "balance at stake") for a
# influence. Validators may also lose balance if they fail to do their duty ## validator with the given ``index``.
# in which case their influence decreases. Once they drop below a certain
# balance, they're removed from the validator registry.
min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT) min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT)
func sum_effective_balances*( func sum_effective_balances*(
@ -82,6 +81,7 @@ func process_deposit(state: var BeaconState,
state.validator_balances[index] += amount state.validator_balances[index] += amount
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#get_entry_exit_effect_epoch
func get_entry_exit_effect_epoch*(epoch: EpochNumber): EpochNumber = func get_entry_exit_effect_epoch*(epoch: EpochNumber): EpochNumber =
## An entry or exit triggered in the ``epoch`` given by the input takes effect at ## An entry or exit triggered in the ``epoch`` given by the input takes effect at
## the epoch given by the output. ## the epoch given by the output.
@ -222,13 +222,15 @@ func get_block_root*(state: BeaconState,
doAssert slot < state.slot doAssert slot < state.slot
state.latest_block_roots[slot mod LATEST_BLOCK_ROOTS_LENGTH] state.latest_block_roots[slot mod LATEST_BLOCK_ROOTS_LENGTH]
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#get_attestation_participants
func get_attestation_participants*(state: BeaconState, func get_attestation_participants*(state: BeaconState,
attestation_data: AttestationData, attestation_data: AttestationData,
aggregation_bitfield: seq[byte]): seq[ValidatorIndex] = bitfield: seq[byte]): seq[ValidatorIndex] =
## Attestation participants in the attestation data are called out in a ## Attestation participants in the attestation data are called out in a
## bit field that corresponds to the committee of the shard at the time - this ## bit field that corresponds to the committee of the shard at the time - this
## function converts it to list of indices in to BeaconState.validators ## function converts it to list of indices in to BeaconState.validators
## Returns empty list if the shard is not found ## Returns empty list if the shard is not found
## Return the participant indices at for the ``attestation_data`` and ``bitfield``.
# TODO Linear search through shard list? borderline ok, it's a small list # TODO Linear search through shard list? borderline ok, it's a small list
# TODO bitfield type needed, once bit order settles down # TODO bitfield type needed, once bit order settles down
# TODO iterator candidate # TODO iterator candidate
@ -243,12 +245,13 @@ func get_attestation_participants*(state: BeaconState,
let crosslink_committee = mapIt( let crosslink_committee = mapIt(
filterIt(crosslink_committees, it.shard == attestation_data.shard), filterIt(crosslink_committees, it.shard == attestation_data.shard),
it.committee)[0] it.committee)[0]
assert len(aggregation_bitfield) == (len(crosslink_committee) + 7) div 8
assert verify_bitfield(bitfield, len(crosslink_committee))
# Find the participating attesters in the committee # Find the participating attesters in the committee
result = @[] result = @[]
for i, validator_index in crosslink_committee: for i, validator_index in crosslink_committee:
let aggregation_bit = (aggregation_bitfield[i div 8] shr (7 - (i mod 8))) mod 2 let aggregation_bit = get_bitfield_bit(bitfield, i)
if aggregation_bit == 1: if aggregation_bit == 1:
result.add(validator_index) result.add(validator_index)

View File

@ -73,14 +73,25 @@ func bls_aggregate_pubkeys*(keys: openArray[ValidatorPubKey]): ValidatorPubKey =
else: else:
result.combine(key) result.combine(key)
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/bls_signature.md#bls_verify
func bls_verify*( func bls_verify*(
pubkey: ValidatorPubKey, msg: openArray[byte], sig: ValidatorSig, pubkey: ValidatorPubKey, msg: openArray[byte], sig: ValidatorSig,
domain: uint64): bool = domain: uint64): bool =
# name from spec! # name from spec!
sig.verify(msg, domain, pubkey) sig.verify(msg, domain, pubkey)
func bls_sign*(key: ValidatorPrivKey, # https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/bls_signature.md#bls_verify_multiple
msg: openarray[byte], domain: uint64): ValidatorSig = func bls_verify_multiple*(
pubkeys: seq[ValidatorPubKey], messages: seq[array[0..31, byte]],
sig: ValidatorSig, domain: uint64): bool =
let L = len(pubkeys)
assert L == len(messages)
# TODO calculate product of ate pairings; check how sig.verify works
true
func bls_sign*(key: ValidatorPrivKey, msg: openarray[byte],
domain: uint64): ValidatorSig =
# name from spec! # name from spec!
key.sign(domain, msg) key.sign(domain, msg)

View File

@ -229,7 +229,7 @@ type
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#attestationdata # https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#attestationdata
AttestationDataAndCustodyBit* = object AttestationDataAndCustodyBit* = object
data*: AttestationData data*: AttestationData
custody_bit: bool custody_bit*: bool
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#deposit # https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#deposit
Deposit* = object Deposit* = object

View File

@ -14,13 +14,13 @@ import ./datatypes, ./digest, sequtils, math
func bitSet*(bitfield: var openArray[byte], index: int) = func bitSet*(bitfield: var openArray[byte], index: int) =
bitfield[index div 8] = bitfield[index div 8] or 1'u8 shl (7 - (index mod 8)) bitfield[index div 8] = bitfield[index div 8] or 1'u8 shl (7 - (index mod 8))
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#get_bitfield_bit
func get_bitfield_bit*(bitfield: openarray[byte], i: int): byte = func get_bitfield_bit*(bitfield: openarray[byte], i: int): byte =
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#get_bitfield_bit
# Extract the bit in ``bitfield`` at position ``i``. # Extract the bit in ``bitfield`` at position ``i``.
(bitfield[i div 8] shr (7 - (i mod 8))) mod 2 (bitfield[i div 8] shr (7 - (i mod 8))) mod 2
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#verify_bitfield
func verify_bitfield*(bitfield: openarray[byte], committee_size: int): bool = func verify_bitfield*(bitfield: openarray[byte], committee_size: int): bool =
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#verify_bitfield
# Verify ``bitfield`` against the ``committee_size``. # Verify ``bitfield`` against the ``committee_size``.
if len(bitfield) != (committee_size + 7) div 8: if len(bitfield) != (committee_size + 7) div 8:
return false return false
@ -113,6 +113,7 @@ func repeat_hash*(v: Eth2Digest, n: SomeInteger): Eth2Digest =
result = eth2hash(result.data) result = eth2hash(result.data)
dec n dec n
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#integer_squareroot
func integer_squareroot*(n: SomeInteger): SomeInteger = func integer_squareroot*(n: SomeInteger): SomeInteger =
## The largest integer ``x`` such that ``x**2`` is less than ``n``. ## The largest integer ``x`` such that ``x**2`` is less than ``n``.
var var
@ -123,12 +124,15 @@ func integer_squareroot*(n: SomeInteger): SomeInteger =
y = (x + n div x) div 2 y = (x + n div x) div 2
x x
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#get_fork_version
func get_fork_version*(fork: Fork, epoch: EpochNumber): uint64 = func get_fork_version*(fork: Fork, epoch: EpochNumber): uint64 =
## Return the fork version of the given ``epoch``.
if epoch < fork.epoch: if epoch < fork.epoch:
fork.previous_version fork.previous_version
else: else:
fork.current_version fork.current_version
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#get_domain
func get_domain*( func get_domain*(
fork: Fork, epoch: EpochNumber, domain_type: SignatureDomain): uint64 = fork: Fork, epoch: EpochNumber, domain_type: SignatureDomain): uint64 =
# TODO Slot overflow? Or is slot 32 bits for all intents and purposes? # TODO Slot overflow? Or is slot 32 bits for all intents and purposes?
@ -151,37 +155,31 @@ func merkle_root*(values: openArray[Eth2Digest]): Eth2Digest =
#TODO #TODO
discard discard
proc is_double_vote*(attestation_data_1: AttestationData,
attestation_data_2: AttestationData): bool =
## Assumes ``attestation_data_1`` is distinct from ``attestation_data_2``.
## Returns True if the provided ``AttestationData`` are slashable
## due to a 'double vote'.
## A double vote is when a validator votes for two attestations within the
## same slot - doing so means risking getting slashed.
attestation_data_1.slot == attestation_data_2.slot
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#slot_to_epoch # https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#slot_to_epoch
func slot_to_epoch*(slot: SlotNumber): EpochNumber = func slot_to_epoch*(slot: SlotNumber): EpochNumber =
slot div EPOCH_LENGTH slot div EPOCH_LENGTH
proc is_surround_vote*(attestation_data_1: AttestationData, # https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#is_double_vote
func is_double_vote*(attestation_data_1: AttestationData,
attestation_data_2: AttestationData): bool = attestation_data_2: AttestationData): bool =
## Assumes ``attestation_data_1`` is distinct from ``attestation_data_2``. ## Check if ``attestation_data_1`` and ``attestation_data_2`` have the same
## Returns True if the provided ``AttestationData`` are slashable ## target.
## due to a 'surround vote'. let
## Note: parameter order matters as this function only checks target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
## that ``attestation_data_1`` surrounds ``attestation_data_2``. target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
target_epoch_1 == target_epoch_2
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#is_surround_vote
func is_surround_vote*(attestation_data_1: AttestationData,
attestation_data_2: AttestationData): bool =
## Check if ``attestation_data_1`` surrounds ``attestation_data_2``.
let let
source_epoch_1 = attestation_data_1.justified_epoch source_epoch_1 = attestation_data_1.justified_epoch
source_epoch_2 = attestation_data_2.justified_epoch source_epoch_2 = attestation_data_2.justified_epoch
target_epoch_1 = slot_to_epoch(attestation_data_1.slot) target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
target_epoch_2 = slot_to_epoch(attestation_data_2.slot) target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
( source_epoch_1 < source_epoch_2 and target_epoch_2 < target_epoch_1
(source_epoch_1 < source_epoch_2) and
(source_epoch_2 + 1 == target_epoch_2) and
(target_epoch_2 < target_epoch_1)
)
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#is_active_validator # https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#is_active_validator
func is_active_validator*(validator: Validator, epoch: EpochNumber): bool = func is_active_validator*(validator: Validator, epoch: EpochNumber): bool =

View File

@ -184,8 +184,8 @@ proc processProposerSlashings(
return true return true
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#verify_slashable_attestation
func verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation): bool = func verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation): bool =
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#verify_slashable_attestation
# Verify validity of ``slashable_attestation`` fields. # Verify validity of ``slashable_attestation`` fields.
if anyIt(slashable_attestation.custody_bitfield, it != 0): # [TO BE REMOVED IN PHASE 1] if anyIt(slashable_attestation.custody_bitfield, it != 0): # [TO BE REMOVED IN PHASE 1]
@ -208,7 +208,31 @@ func verify_slashable_attestation(state: BeaconState, slashable_attestation: Sla
custody_bit_0_indices: seq[uint64] = @[] custody_bit_0_indices: seq[uint64] = @[]
custody_bit_1_indices: seq[uint64] = @[] custody_bit_1_indices: seq[uint64] = @[]
return true for i, validator_index in slashable_attestation.validator_indices:
if get_bitfield_bit(slashable_attestation.custody_bitfield, i) == 0b0:
custody_bit_0_indices.add(validator_index)
else:
custody_bit_1_indices.add(validator_index)
# bug in 0.1 version of spec, fixed later; bls_verify is what works
bls_verify_multiple(
@[
bls_aggregate_pubkeys(mapIt(custody_bit_0_indices, state.validator_registry[it.int].pubkey)),
bls_aggregate_pubkeys(mapIt(custody_bit_1_indices, state.validator_registry[it.int].pubkey)),
],
@[
hash_tree_root(AttestationDataAndCustodyBit(
data: slashable_attestation.data, custody_bit: false)),
hash_tree_root(AttestationDataAndCustodyBit(
data: slashable_attestation.data, custody_bit: true)),
],
slashable_attestation.aggregate_signature,
get_domain(
state.fork,
slot_to_epoch(slashable_attestation.data.slot),
DOMAIN_ATTESTATION,
),
)
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool = proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
## https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#attester-slashings-1 ## https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#attester-slashings-1