collect signature production and verificaiton in one place (#1179)

* collect signature production and verificaiton in one place

Signatures are made over data and domain - here we collect all such
activities in one place.

Also:
* security: fix cast-before-range-check
* log block/attestation verification consistently
* run block verification based on `getProposer` in its own history
* clean up some unused stuff

* import

* missing raises
This commit is contained in:
Jacek Sieka 2020-06-16 07:45:04 +02:00 committed by GitHub
parent 9335533503
commit 89e4819ce9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 285 additions and 276 deletions

View File

@ -9,10 +9,13 @@
import
options, chronicles,
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator,
state_transition_block],
./spec/[
beaconstate, datatypes, crypto, digest, helpers, validator, signatures],
./block_pool, ./attestation_pool, ./beacon_node_types, ./ssz
logScope:
topics = "att_aggr"
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregation-selection
func is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex,
slot_signature: ValidatorSig): bool =
@ -75,17 +78,20 @@ proc aggregate_attestations*(
proc isValidAttestation*(
pool: AttestationPool, attestation: Attestation, current_slot: Slot,
topicCommitteeIndex: uint64): bool =
logScope:
topics = "att_aggr valid_att"
received_attestation = shortLog(attestation)
# The attestation's committee index (attestation.data.index) is for the
# correct subnet.
if attestation.data.index != topicCommitteeIndex:
debug "isValidAttestation: attestation's committee index not for the correct subnet",
topicCommitteeIndex = topicCommitteeIndex,
attestation_data_index = attestation.data.index
debug "attestation's committee index not for the correct subnet",
topicCommitteeIndex = topicCommitteeIndex
return false
if not (attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >=
current_slot and current_slot >= attestation.data.slot):
debug "isValidAttestation: attestation.data.slot not within ATTESTATION_PROPAGATION_SLOT_RANGE"
debug "attestation.data.slot not within ATTESTATION_PROPAGATION_SLOT_RANGE"
return false
# The attestation is unaggregated -- that is, it has exactly one
@ -100,11 +106,10 @@ proc isValidAttestation*(
continue
onesCount += 1
if onesCount > 1:
debug "isValidAttestation: attestation has too many aggregation bits",
aggregation_bits = attestation.aggregation_bits
debug "attestation has too many aggregation bits"
return false
if onesCount != 1:
debug "isValidAttestation: attestation has too few aggregation bits"
debug "attestation has too few aggregation bits"
return false
# The attestation is the first valid attestation received for the
@ -117,9 +122,7 @@ proc isValidAttestation*(
# Attestations might be aggregated eagerly or lazily; allow for both.
for validation in attestationEntry.validations:
if attestation.aggregation_bits.isSubsetOf(validation.aggregation_bits):
debug "isValidAttestation: attestation already exists at slot",
attestation_data_slot = attestation.data.slot,
attestation_aggregation_bits = attestation.aggregation_bits,
debug "attestation already exists at slot",
attestation_pool_validation = validation.aggregation_bits
return false
@ -131,8 +134,7 @@ proc isValidAttestation*(
# propagated - i.e. imagine that attestations are smaller than blocks and
# therefore propagate faster, thus reordering their arrival in some nodes
if pool.blockPool.get(attestation.data.beacon_block_root).isNone():
debug "isValidAttestation: block doesn't exist in block pool",
attestation_data_beacon_block_root = attestation.data.beacon_block_root
debug "block doesn't exist in block pool"
return false
# The signature of attestation is valid.
@ -143,7 +145,7 @@ proc isValidAttestation*(
pool.blockPool.headState.data.data,
get_indexed_attestation(
pool.blockPool.headState.data.data, attestation, cache), {}):
debug "isValidAttestation: signature verification failed"
debug "signature verification failed"
return false
true

View File

@ -5,12 +5,13 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [Defect].}
import
chronicles, sequtils, tables,
metrics, stew/results,
../ssz/merkleization, ../state_transition, ../extras,
../spec/[crypto, datatypes, digest, helpers],
../spec/[crypto, datatypes, digest, helpers, signatures],
block_pools_types, candidate_chains
export results
@ -22,8 +23,8 @@ export results
# "quarantined" network blocks
# pass the firewall and be stored in the blockpool
logScope: topics = "clearblk"
{.push raises: [Defect].}
logScope:
topics = "clearance"
func getOrResolve*(dag: CandidateChains, quarantine: var Quarantine, root: Eth2Digest): BlockRef =
## Fetch a block ref, or nil if not found (will be added to list of
@ -259,6 +260,10 @@ proc isValidBeaconBlock*(
dag: CandidateChains, quarantine: var Quarantine,
signed_beacon_block: SignedBeaconBlock, current_slot: Slot,
flags: UpdateFlags): bool =
logScope:
topics = "clearance valid_blck"
received_block = shortLog(signed_beacon_block.message)
# In general, checks are ordered from cheap to expensive. Especially, crypto
# verification could be quite a bit more expensive than the rest. This is an
# externally easy-to-invoke function by tossing network packets at the node.
@ -269,9 +274,8 @@ proc isValidBeaconBlock*(
# TODO using +1 here while this is being sorted - should queue these until
# they're within the DISPARITY limit
if not (signed_beacon_block.message.slot <= current_slot + 1):
debug "isValidBeaconBlock: block is from a future slot",
signed_beacon_block_message_slot = signed_beacon_block.message.slot,
current_slot = current_slot
debug "block is from a future slot",
current_slot
return false
# The block is from a slot greater than the latest finalized slot (with a
@ -279,7 +283,7 @@ proc isValidBeaconBlock*(
# signed_beacon_block.message.slot >
# compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)
if not (signed_beacon_block.message.slot > dag.finalizedHead.slot):
debug "isValidBeaconBlock: block is not from a slot greater than the latest finalized slot"
debug "block is not from a slot greater than the latest finalized slot"
return false
# The block is the first block with valid signature received for the proposer
@ -317,11 +321,9 @@ proc isValidBeaconBlock*(
signed_beacon_block.message.proposer_index and
blck.message.slot == signed_beacon_block.message.slot and
blck.signature.toRaw() != signed_beacon_block.signature.toRaw():
debug "isValidBeaconBlock: block isn't first block with valid signature received for the proposer",
signed_beacon_block_message_slot = signed_beacon_block.message.slot,
debug "block isn't first block with valid signature received for the proposer",
blckRef = slotBlockRef,
received_block = shortLog(signed_beacon_block.message),
existing_block = shortLog(dag.get(slotBlockRef).data.message)
existing_block = shortLog(blck.message)
return false
# If this block doesn't have a parent we know about, we can't/don't really
@ -342,27 +344,36 @@ proc isValidBeaconBlock*(
# CandidateChains.add(...) directly, with no additional validity checks. TODO,
# not specific to this, but by the pending dag keying on the htr of the
# BeaconBlock, not SignedBeaconBlock, opens up certain spoofing attacks.
debug "parent unknown, putting block in quarantine"
quarantine.pending[hash_tree_root(signed_beacon_block.message)] =
signed_beacon_block
return false
# The proposer signature, signed_beacon_block.signature, is valid with
# respect to the proposer_index pubkey.
let bs =
BlockSlot(blck: parent_ref, slot: dag.get(parent_ref).data.message.slot)
dag.withState(dag.tmpState, bs):
let
blockRoot = hash_tree_root(signed_beacon_block.message)
domain = get_domain(dag.headState.data.data, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(signed_beacon_block.message.slot))
signing_root = compute_signing_root(blockRoot, domain)
proposer_index = signed_beacon_block.message.proposer_index
let
proposer = getProposer(dag, parent_ref, signed_beacon_block.message.slot)
if proposer_index >= dag.headState.data.data.validators.len.uint64:
return false
if not blsVerify(dag.headState.data.data.validators[proposer_index].pubkey,
signing_root.data, signed_beacon_block.signature):
debug "isValidBeaconBlock: block failed signature verification"
return false
if proposer.isNone:
notice "cannot compute proposer for message"
return false
if proposer.get()[0] !=
ValidatorIndex(signed_beacon_block.message.proposer_index):
debug "block had unexpected proposer",
expected_proposer = proposer.get()[0]
return false
if not verify_block_signature(
dag.headState.data.data.fork,
dag.headState.data.data.genesis_validators_root,
signed_beacon_block.message.slot,
signed_beacon_block.message,
proposer.get()[1],
signed_beacon_block.signature):
debug "block failed signature verification",
signature = shortLog(signed_beacon_block.signature)
return false
true

View File

@ -3,7 +3,7 @@
import
stew/endians2, stint,
./extras, ./ssz/merkleization,
spec/[crypto, datatypes, digest, helpers, keystore]
spec/[crypto, datatypes, digest, keystore, signatures]
func get_eth1data_stub*(deposit_count: uint64, current_epoch: Epoch): Eth1Data =
# https://github.com/ethereum/eth2.0-pm/blob/e596c70a19e22c7def4fd3519e20ae4022349390/interop/mocked_eth1data/README.md
@ -47,9 +47,6 @@ func makeDeposit*(
withdrawal_credentials: makeWithdrawalCredentials(pubkey)))
if skipBLSValidation notin flags:
let domain = compute_domain(DOMAIN_DEPOSIT)
let signing_root = compute_signing_root(ret.getDepositMessage, domain)
ret.data.signature = bls_sign(privkey, signing_root.data)
ret.data.signature = get_deposit_signature(ret.data, privkey)
ret

View File

@ -11,7 +11,7 @@ import
tables, algorithm, math, sequtils, options,
json_serialization/std/sets, chronicles,
../extras, ../ssz/merkleization,
./crypto, ./datatypes, ./digest, ./helpers, ./validator,
./crypto, ./datatypes, ./digest, ./helpers, ./signatures, ./validator,
../../nbench/bench_lab
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#is_valid_merkle_branch
@ -79,19 +79,13 @@ proc process_deposit*(
if index == -1:
# Verify the deposit signature (proof of possession) which is not checked
# by the deposit contract
# Fork-agnostic domain since deposits are valid across forks
let domain = compute_domain(DOMAIN_DEPOSIT)
let signing_root = compute_signing_root(deposit.getDepositMessage, domain)
if skipBLSValidation notin flags and not bls_verify(
pubkey, signing_root.data,
deposit.data.signature):
# It's ok that deposits fail - they get included in blocks regardless
# TODO spec test?
debug "Skipping deposit with invalid signature",
pubkey, signing_root, signature = deposit.data.signature
return true
if skipBLSValidation notin flags:
if not verify_deposit_signature(deposit.data):
# It's ok that deposits fail - they get included in blocks regardless
# TODO spec test?
debug "Skipping deposit with invalid signature",
deposit = shortLog(deposit.data)
return true
# Add validator and balance entries
state.validators.add(Validator(
@ -418,15 +412,14 @@ proc is_valid_indexed_attestation*(
return false
# Verify aggregate signature
let pubkeys = mapIt(indices, state.validators[it.int].pubkey) # TODO: fuse loops with blsFastAggregateVerify
let domain = state.get_domain(DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
let signing_root = compute_signing_root(indexed_attestation.data, domain)
if skipBLSValidation notin flags and
not blsFastAggregateVerify(
pubkeys, signing_root.data, indexed_attestation.signature
):
notice "indexed attestation: signature verification failure"
return false
if skipBLSValidation notin flags:
# TODO: fuse loops with blsFastAggregateVerify
let pubkeys = mapIt(indices, state.validators[it.int].pubkey)
if not verify_attestation_signature(
state.fork, state.genesis_validators_root, indexed_attestation.data,
pubkeys, indexed_attestation.signature):
notice "indexed attestation: signature verification failure"
return false
true

View File

@ -99,16 +99,6 @@ func toPubKey*(privkey: ValidatorPrivKey): ValidatorPubKey =
else:
privkey.getKey
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#bls-signatures
func aggregate*[T](values: openarray[ValidatorSig]): ValidatorSig =
## Aggregate arrays of sequences of Validator Signatures
## This assumes that they are real signatures
result = BlsValue[T](kind: Real, blsValue: values[0].BlsValue)
for i in 1 ..< values.len:
result.blsValue.aggregate(values[i].blsValue)
func aggregate*(x: var ValidatorSig, other: ValidatorSig) =
## Aggregate 2 Validator Signatures
## This assumes that they are real signatures
@ -143,13 +133,13 @@ func blsVerify*(
# return true
pubkey.blsValue.verify(message, signature.blsValue)
func blsSign*(privkey: ValidatorPrivKey, message: openarray[byte]): ValidatorSig =
func blsSign*(privkey: ValidatorPrivKey, message: openArray[byte]): ValidatorSig =
## Computes a signature from a secret key and a message
ValidatorSig(kind: Real, blsValue: SecretKey(privkey).sign(message))
func blsFastAggregateVerify*[T: byte|char](
publicKeys: openarray[ValidatorPubKey],
message: openarray[T],
func blsFastAggregateVerify*(
publicKeys: openArray[ValidatorPubKey],
message: openArray[byte],
signature: ValidatorSig
): bool =
## Verify the aggregate of multiple signatures on the same message
@ -177,7 +167,8 @@ func blsFastAggregateVerify*[T: byte|char](
if pubkey.kind != Real:
return false
unwrapped.add pubkey.blsValue
return fastAggregateVerify(unwrapped, message, signature.blsValue)
fastAggregateVerify(unwrapped, message, signature.blsValue)
proc newKeyPair*(): BlsResult[tuple[pub: ValidatorPubKey, priv: ValidatorPrivKey]] =
## Generates a new public-private keypair
@ -230,14 +221,14 @@ func toRaw*(x: BlsValue): auto =
func toHex*(x: BlsCurveType): string =
toHex(toRaw(x))
func fromRaw*(T: type ValidatorPrivKey, bytes: openarray[byte]): BlsResult[T] =
func fromRaw*(T: type ValidatorPrivKey, bytes: openArray[byte]): BlsResult[T] =
var val: SecretKey
if val.fromBytes(bytes):
ok ValidatorPrivKey(val)
else:
err "bls: invalid private key"
func fromRaw*[N, T](BT: type BlsValue[N, T], bytes: openarray[byte]): BlsResult[BT] =
func fromRaw*[N, T](BT: type BlsValue[N, T], bytes: openArray[byte]): BlsResult[BT] =
# This is a workaround, so that we can deserialize the serialization of a
# default-initialized BlsValue without raising an exception
when defined(ssz_testing):
@ -294,7 +285,7 @@ proc readValue*(reader: var JsonReader, value: var ValidatorPrivKey) {.
inline, raises: [Exception].} =
value = ValidatorPrivKey.fromHex(reader.readValue(string)).tryGet()
template fromSszBytes*(T: type BlsValue, bytes: openarray[byte]): auto =
template fromSszBytes*(T: type BlsValue, bytes: openArray[byte]): auto =
let v = fromRaw(T, bytes)
if v.isErr:
raise newException(MalformedSszError, $v.error)
@ -350,10 +341,9 @@ proc getRandomBytes*(n: Natural): seq[byte]
if randomBytes(result) != result.len:
raise newException(RandomSourceDepleted, "Failed to generate random bytes")
proc getRandomBytesOrPanic*(output: var openarray[byte]) =
proc getRandomBytesOrPanic*(output: var openArray[byte]) =
doAssert randomBytes(output) == output.len
proc getRandomBytesOrPanic*(n: Natural): seq[byte] =
result = newSeq[byte](n)
getRandomBytesOrPanic(result)

View File

@ -213,7 +213,7 @@ type
DepositData* = object
pubkey*: ValidatorPubKey
withdrawal_credentials*: Eth2Digest
amount*: uint64
amount*: Gwei
signature*: ValidatorSig # Signing over DepositMessage
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#voluntaryexit
@ -622,6 +622,14 @@ func shortLog*(v: SignedBeaconBlock): auto =
signature: shortLog(v.signature)
)
func shortLog*(v: DepositData): auto =
(
pubkey: shortLog(v.pubkey),
withdrawal_credentials: shortlog(v.withdrawal_credentials),
amount: v.amount,
signature: shortLog(v.signature)
)
func shortLog*(v: AttestationData): auto =
(
slot: shortLog(v.slot),

View File

@ -10,7 +10,7 @@ import
stew/[results, byteutils, bitseqs, bitops2], stew/shims/macros,
eth/keyfile/uuid, blscurve,
nimcrypto/[sha2, rijndael, pbkdf2, bcmode, hash, sysrand],
datatypes, crypto, digest, helpers
./datatypes, ./crypto, ./digest, ./signatures
export
results
@ -396,9 +396,6 @@ proc prepareDeposit*(credentials: Credentials,
pubkey: signingPubKey,
withdrawal_credentials: makeWithdrawalCredentials(withdrawalPubKey)))
let domain = compute_domain(DOMAIN_DEPOSIT)
let signing_root = compute_signing_root(ret.getDepositMessage, domain)
ret.data.signature = get_deposit_signature(ret.data, credentials.signingKey)
ret.data.signature = bls_sign(credentials.signingKey, signing_root.data)
ret

View File

@ -0,0 +1,137 @@
# beacon_chain
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [Defect].}
import
./crypto, ./digest, ./datatypes, ./helpers, ../ssz/merkleization
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#aggregation-selection
func get_slot_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
privkey: ValidatorPrivKey): ValidatorSig =
let
epoch = compute_epoch_at_slot(slot)
domain = get_domain(
fork, DOMAIN_SELECTION_PROOF, epoch, genesis_validators_root)
signing_root = compute_signing_root(slot, domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#randao-reveal
func get_epoch_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch,
privkey: ValidatorPrivKey): ValidatorSig =
let
domain = get_domain(fork, DOMAIN_RANDAO, epoch, genesis_validators_root)
signing_root = compute_signing_root(epoch, domain)
blsSign(privKey, signing_root.data)
func verify_epoch_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch,
pubkey: ValidatorPubKey, signature: ValidatorSig): bool =
let
domain = get_domain(fork, DOMAIN_RANDAO, epoch, genesis_validators_root)
signing_root = compute_signing_root(epoch, domain)
blsVerify(pubkey, signing_root.data, signature)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#signature
func get_block_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
root: Eth2Digest, privkey: ValidatorPrivKey): ValidatorSig =
let
epoch = compute_epoch_at_slot(slot)
domain = get_domain(
fork, DOMAIN_BEACON_PROPOSER, epoch, genesis_validators_root)
signing_root = compute_signing_root(root, domain)
blsSign(privKey, signing_root.data)
func verify_block_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
blck: Eth2Digest | BeaconBlock | BeaconBlockHeader, pubkey: ValidatorPubKey,
signature: ValidatorSig): bool =
let
epoch = compute_epoch_at_slot(slot)
domain = get_domain(
fork, DOMAIN_BEACON_PROPOSER, epoch, genesis_validators_root)
signing_root = compute_signing_root(blck, domain)
blsVerify(pubKey, signing_root.data, signature)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#broadcast-aggregate
func get_aggregate_and_proof_signature*(fork: Fork, genesis_validators_root: Eth2Digest,
aggregate_and_proof: AggregateAndProof,
privKey: ValidatorPrivKey): ValidatorSig =
let
epoch = compute_epoch_at_slot(aggregate_and_proof.aggregate.data.slot)
domain = get_domain(
fork, DOMAIN_AGGREGATE_AND_PROOF, epoch, genesis_validators_root)
signing_root = compute_signing_root(aggregate_and_proof, domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#aggregate-signature
func get_attestation_signature*(
fork: Fork, genesis_validators_root: Eth2Digest,
attestation_data: AttestationData,
privkey: ValidatorPrivKey): ValidatorSig =
let
epoch = attestation_data.target.epoch
domain = get_domain(
fork, DOMAIN_BEACON_ATTESTER, epoch, genesis_validators_root)
signing_root = compute_signing_root(attestation_data, domain)
blsSign(privKey, signing_root.data)
func verify_attestation_signature*(
fork: Fork, genesis_validators_root: Eth2Digest,
attestation_data: AttestationData,
pubkeys: openArray[ValidatorPubKey],
signature: ValidatorSig): bool =
let
epoch = attestation_data.target.epoch
domain = get_domain(
fork, DOMAIN_BEACON_ATTESTER, epoch, genesis_validators_root)
signing_root = compute_signing_root(attestation_data, domain)
blsFastAggregateVerify(pubkeys, signing_root.data, signature)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#deposits
func get_deposit_signature*(
deposit: DepositData,
privkey: ValidatorPrivKey): ValidatorSig =
let
deposit_message = deposit.getDepositMessage()
# Fork-agnostic domain since deposits are valid across forks
domain = compute_domain(DOMAIN_DEPOSIT)
signing_root = compute_signing_root(deposit_message, domain)
blsSign(privKey, signing_root.data)
func verify_deposit_signature*(deposit: DepositData): bool =
let
deposit_message = deposit.getDepositMessage()
# Fork-agnostic domain since deposits are valid across forks
domain = compute_domain(DOMAIN_DEPOSIT)
signing_root = compute_signing_root(deposit_message, domain)
blsVerify(deposit.pubkey, signing_root.data, deposit.signature)
func verify_voluntary_exit_signature*(
fork: Fork, genesis_validators_root: Eth2Digest,
voluntary_exit: VoluntaryExit,
pubkey: ValidatorPubKey, signature: ValidatorSig): bool =
let
domain = get_domain(
fork, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch, genesis_validators_root)
signing_root = compute_signing_root(voluntary_exit, domain)
blsVerify(pubkey, signing_root.data, signature)

View File

@ -32,7 +32,8 @@
import
algorithm, collections/sets, chronicles, options, sequtils, sets,
../extras, ../ssz/merkleization, metrics,
beaconstate, crypto, datatypes, digest, helpers, validator,
./beaconstate, ./crypto, ./datatypes, ./digest, ./helpers, ./validator,
./signatures,
../../nbench/bench_lab
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#additional-metrics
@ -104,7 +105,6 @@ proc process_randao(
state: var BeaconState, body: BeaconBlockBody, flags: UpdateFlags,
stateCache: var StateCache): bool {.nbench.}=
let
epoch = state.get_current_epoch()
proposer_index = get_beacon_proposer_index(state, stateCache)
if proposer_index.isNone:
@ -112,14 +112,17 @@ proc process_randao(
return false
# Verify RANDAO reveal
let proposer = addr state.validators[proposer_index.get]
let
epoch = state.get_current_epoch()
let signing_root = compute_signing_root(
epoch, get_domain(state, DOMAIN_RANDAO, get_current_epoch(state)))
if skipBLSValidation notin flags:
if not blsVerify(proposer.pubkey, signing_root.data, body.randao_reveal):
notice "Randao mismatch", proposer_pubkey = shortLog(proposer.pubkey),
message = epoch,
let proposer_pubkey = state.validators[proposer_index.get].pubkey
if not verify_epoch_signature(
state.fork, state.genesis_validators_root, epoch, proposer_pubkey,
body.randao_reveal):
notice "Randao mismatch", proposer_pubkey = shortLog(proposer_pubkey),
epoch,
signature = shortLog(body.randao_reveal),
slot = state.slot
return false
@ -187,12 +190,9 @@ proc process_proposer_slashing*(
if skipBlsValidation notin flags:
for i, signed_header in [proposer_slashing.signed_header_1,
proposer_slashing.signed_header_2]:
let domain = get_domain(
state, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(signed_header.message.slot)
)
let signing_root = compute_signing_root(signed_header.message, domain)
if not blsVerify(proposer.pubkey, signing_root.data, signed_header.signature):
if not verify_block_signature(
state.fork, state.genesis_validators_root, signed_header.message.slot,
signed_header.message, proposer.pubkey, signed_header.signature):
notice "Proposer slashing: invalid signature",
signature_index = i
return false
@ -260,7 +260,7 @@ proc process_voluntary_exit*(
let voluntary_exit = signed_voluntary_exit.message
# Not in spec. Check that validator_index is in range
if voluntary_exit.validator_index.int >= state.validators.len:
if voluntary_exit.validator_index >= state.validators.len.uint64:
notice "Exit: invalid validator index",
index = voluntary_exit.validator_index,
num_validators = state.validators.len
@ -298,9 +298,9 @@ proc process_voluntary_exit*(
# Verify signature
if skipBlsValidation notin flags:
let domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
let signing_root = compute_signing_root(voluntary_exit, domain)
if not bls_verify(validator.pubkey, signing_root.data, signed_voluntary_exit.signature):
if not verify_voluntary_exit_signature(
state.fork, state.genesis_validators_root, voluntary_exit,
validator.pubkey, signed_voluntary_exit.signature):
notice "Exit: invalid signature"
return false
@ -393,61 +393,3 @@ proc process_block*(
return false
true
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregation-selection
func get_slot_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
privkey: ValidatorPrivKey): ValidatorSig =
let
domain = get_domain(fork, DOMAIN_SELECTION_PROOF,
compute_epoch_at_slot(slot), genesis_validators_root)
signing_root = compute_signing_root(slot, domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#randao-reveal
func get_epoch_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
privkey: ValidatorPrivKey): ValidatorSig =
let
domain = get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot),
genesis_validators_root)
signing_root = compute_signing_root(compute_epoch_at_slot(slot), domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#signature
func get_block_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
root: Eth2Digest, privkey: ValidatorPrivKey): ValidatorSig =
let
domain = get_domain(fork, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(slot), genesis_validators_root)
signing_root = compute_signing_root(root, domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#broadcast-aggregate
func get_aggregate_and_proof_signature*(fork: Fork, genesis_validators_root: Eth2Digest,
aggregate_and_proof: AggregateAndProof,
privKey: ValidatorPrivKey): ValidatorSig =
let
aggregate = aggregate_and_proof.aggregate
domain = get_domain(fork, DOMAIN_AGGREGATE_AND_PROOF,
compute_epoch_at_slot(aggregate.data.slot),
genesis_validators_root)
signing_root = compute_signing_root(aggregate_and_proof, domain)
return blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#aggregate-signature
func get_attestation_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, attestation: AttestationData,
privkey: ValidatorPrivKey): ValidatorSig =
let
attestationRoot = hash_tree_root(attestation)
domain = get_domain(fork, DOMAIN_BEACON_ATTESTER,
attestation.target.epoch, genesis_validators_root)
signing_root = compute_signing_root(attestationRoot, domain)
blsSign(privKey, signing_root.data)

View File

@ -32,7 +32,7 @@ import
chronicles,
stew/results,
./extras, ./ssz/merkleization, metrics,
./spec/[datatypes, crypto, digest, helpers, validator],
./spec/[datatypes, crypto, digest, helpers, signatures, validator],
./spec/[state_transition_block, state_transition_epoch],
../nbench/bench_lab
@ -64,23 +64,20 @@ func get_epoch_validator_count(state: BeaconState): int64 {.nbench.} =
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
proc verify_block_signature*(
state: BeaconState, signedBlock: SignedBeaconBlock): bool {.nbench.} =
if signedBlock.message.proposer_index >= state.validators.len.uint64:
state: BeaconState, signed_block: SignedBeaconBlock): bool {.nbench.} =
let
proposer_index = signed_block.message.proposer_index
if proposer_index >= state.validators.len.uint64:
notice "Invalid proposer index in block",
blck = shortLog(signedBlock.message)
blck = shortLog(signed_block.message)
return false
let
proposer = state.validators[signedBlock.message.proposer_index]
domain = get_domain(
state, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(signedBlock.message.slot))
signing_root = compute_signing_root(signedBlock.message, domain)
if not bls_verify(proposer.pubKey, signing_root.data, signedBlock.signature):
if not verify_block_signature(
state.fork, state.genesis_validators_root, signed_block.message.slot,
signed_block.message, state.validators[proposer_index].pubkey,
signed_block.signature):
notice "Block: signature verification failed",
blck = shortLog(signedBlock),
signingRoot = shortLog(signing_root)
blck = shortLog(signedBlock)
return false
true

View File

@ -1,7 +1,7 @@
import
tables,
chronos, chronicles,
spec/[datatypes, crypto, digest, state_transition_block],
spec/[datatypes, crypto, digest, signatures, helpers],
beacon_node_types
func init*(T: type ValidatorPool): T =
@ -83,7 +83,8 @@ proc signAggregateAndProof*(v: AttachedValidator,
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#randao-reveal
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork,
genesis_validators_root: Eth2Digest, slot: Slot): ValidatorSig =
get_epoch_signature(fork, genesis_validators_root, slot, k)
get_epoch_signature(
fork, genesis_validators_root, slot.compute_epoch_at_slot, k)
func genRandaoReveal*(v: AttachedValidator, fork: Fork,
genesis_validators_root: Eth2Digest, slot: Slot): ValidatorSig =

View File

@ -20,8 +20,7 @@ import
options, random, tables,
../tests/[testblockutil],
../beacon_chain/spec/[
beaconstate, crypto, datatypes, digest, helpers, validator,
state_transition_block],
beaconstate, crypto, datatypes, digest, helpers, validator, signatures],
../beacon_chain/[
attestation_pool, block_pool, beacon_node_types, beacon_chain_db,
interop, state_transition, validator_pool],

View File

@ -13,7 +13,7 @@ import
sets,
# Specs
../../beacon_chain/spec/[datatypes, beaconstate, helpers, validator, crypto,
state_transition_block],
signatures],
# Internals
../../beacon_chain/[ssz, extras, state_transition],
# Mocking procs

View File

@ -8,7 +8,7 @@
import
options,
# Specs
../../beacon_chain/spec/[datatypes, validator, state_transition_block],
../../beacon_chain/spec/[datatypes, helpers, signatures, validator],
# Internals
../../beacon_chain/[ssz, extras],
# Mock helpers
@ -27,7 +27,8 @@ proc signMockBlockImpl(
let privkey = MockPrivKeys[signedBlock.message.proposer_index]
signedBlock.message.body.randao_reveal = get_epoch_signature(
state.fork, state.genesis_validators_root, block_slot, privkey)
state.fork, state.genesis_validators_root, block_slot.compute_epoch_at_slot,
privkey)
signedBlock.signature = get_block_signature(
state.fork, state.genesis_validators_root, block_slot,
hash_tree_root(signedBlock.message), privkey)

View File

@ -12,88 +12,34 @@ import
# Standard library
math, random,
# Specs
../../beacon_chain/spec/[datatypes, crypto, helpers, digest],
../../beacon_chain/spec/[datatypes, crypto, digest, keystore, signatures],
# Internals
../../beacon_chain/[ssz, extras, merkle_minimal],
# Mocking procs
./mock_validator_keys
func signMockDepositData(
deposit_data: var DepositData,
privkey: ValidatorPrivKey
) =
# No state --> Genesis
let domain = compute_domain(
DOMAIN_DEPOSIT,
Version(GENESIS_FORK_VERSION)
)
let signing_root = compute_signing_root(
deposit_data.getDepositMessage(),
domain
)
deposit_data.signature = blsSign(
privkey,
signing_root.data
)
func signMockDepositData(
deposit_data: var DepositData,
privkey: ValidatorPrivKey,
state: BeaconState
) =
let domain = compute_domain(
DOMAIN_DEPOSIT,
Version(GENESIS_FORK_VERSION)
)
let signing_root = compute_signing_root(
deposit_data.getDepositMessage(),
domain
)
deposit_data.signature = blsSign(
privkey,
signing_root.data
)
func mockDepositData(
deposit_data: var DepositData,
pubkey: ValidatorPubKey,
amount: uint64,
# withdrawal_credentials: Eth2Digest
) =
deposit_data.pubkey = pubkey
deposit_data.amount = amount
): DepositData =
# Insecurely use pubkey as withdrawal key
deposit_data.withdrawal_credentials.data[0] = byte BLS_WITHDRAWAL_PREFIX
deposit_data.withdrawal_credentials.data[1..^1] = pubkey.toRaw()
.eth2hash()
.data
.toOpenArray(1, 31)
DepositData(
pubkey: pubkey,
withdrawal_credentials: makeWithdrawalCredentials(pubkey),
amount: amount,
)
func mockDepositData(
deposit_data: var DepositData,
pubkey: ValidatorPubKey,
privkey: ValidatorPrivKey,
amount: uint64,
# withdrawal_credentials: Eth2Digest,
flags: UpdateFlags = {}
) =
mockDepositData(deposit_data, pubkey, amount)
): DepositData =
var ret = mockDepositData(pubkey, amount)
if skipBlsValidation notin flags:
signMockDepositData(deposit_data, privkey)
func mockDepositData(
deposit_data: var DepositData,
pubkey: ValidatorPubKey,
privkey: ValidatorPrivKey,
amount: uint64,
# withdrawal_credentials: Eth2Digest,
state: BeaconState,
flags: UpdateFlags = {}
) =
mockDepositData(deposit_data, pubkey, amount)
if skipBlsValidation notin flags:
signMockDepositData(deposit_data, privkey, state)
ret.signature = get_deposit_signature(ret, privkey)
ret
template mockGenesisDepositsImpl(
result: seq[Deposit],
@ -115,11 +61,7 @@ template mockGenesisDepositsImpl(
updateAmount
# DepositData
mockDepositData(
result[valIdx].data,
MockPubKeys[valIdx],
amount
)
result[valIdx].data = mockDepositData(MockPubKeys[valIdx], amount)
else: # With signing
var depositsDataHash: seq[Eth2Digest]
var depositsData: seq[DepositData]
@ -132,13 +74,8 @@ template mockGenesisDepositsImpl(
updateAmount
# DepositData
mockDepositData(
result[valIdx].data,
MockPubKeys[valIdx],
MockPrivKeys[valIdx],
amount,
flags
)
result[valIdx].data = mockDepositData(
MockPubKeys[valIdx], MockPrivKeys[valIdx], amount, flags)
depositsData.add result[valIdx].data
depositsDataHash.add hash_tree_root(result[valIdx].data)
@ -193,8 +130,7 @@ proc mockUpdateStateForNewDeposit*(
# TODO withdrawal credentials
mockDepositData(
result.data,
result.data = mockDepositData(
MockPubKeys[validator_index],
MockPrivKeys[validator_index],
amount,

View File

@ -12,7 +12,7 @@ import
../beacon_chain/ssz/merkleization,
state_transition, validator_pool],
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest,
helpers, validator, state_transition_block]
helpers, validator, signatures]
func makeFakeValidatorPrivKey(i: int): ValidatorPrivKey =
# 0 is not a valid BLS private key - 1000 helps interop with rust BLS library,
@ -44,7 +44,6 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit =
privkey = makeFakeValidatorPrivKey(i)
pubkey = privkey.toPubKey()
withdrawal_credentials = makeFakeHash(i)
domain = compute_domain(DOMAIN_DEPOSIT, Version(GENESIS_FORK_VERSION))
result = Deposit(
data: DepositData(
@ -55,8 +54,7 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit =
)
if skipBLSValidation notin flags:
let signing_root = compute_signing_root(result.getDepositMessage, domain)
result.data.signature = bls_sign(privkey, signing_root.data)
result.data.signature = get_deposit_signature(result.data, privkey)
proc makeInitialDeposits*(
n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[Deposit] =