2021-01-25 18:45:48 +00:00
|
|
|
# beacon_chain
|
2021-02-25 13:37:22 +00:00
|
|
|
# Copyright (c) 2018-2021 Status Research & Development GmbH
|
2021-01-25 18:45:48 +00:00
|
|
|
# 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
|
|
|
|
# Status lib
|
|
|
|
blscurve,
|
2021-06-01 11:13:40 +00:00
|
|
|
stew/[byteutils, results],
|
2021-12-06 09:49:01 +00:00
|
|
|
taskpools,
|
|
|
|
bearssl,
|
2021-01-25 18:45:48 +00:00
|
|
|
# Internal
|
2021-08-12 13:08:20 +00:00
|
|
|
"."/[helpers, beaconstate, forks],
|
2021-09-27 14:22:58 +00:00
|
|
|
"."/datatypes/[altair, merge, phase0]
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-05-28 15:25:58 +00:00
|
|
|
# Otherwise, error.
|
|
|
|
import chronicles
|
|
|
|
|
2021-12-06 09:49:01 +00:00
|
|
|
export altair, phase0, taskpools, bearssl
|
|
|
|
|
|
|
|
type
|
|
|
|
TaskPoolPtr* = TaskPool
|
|
|
|
|
|
|
|
BatchVerifier* = object
|
|
|
|
sigVerifCache*: BatchedBLSVerifierCache ##\
|
|
|
|
## A cache for batch BLS signature verification contexts
|
|
|
|
rng*: ref BrHmacDrbgContext ##\
|
|
|
|
## A reference to the Nimbus application-wide RNG
|
|
|
|
|
|
|
|
taskpool*: TaskPoolPtr
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
func `$`*(s: SignatureSet): string =
|
|
|
|
"(pubkey: 0x" & s.pubkey.toHex() &
|
|
|
|
", signing_root: 0x" & s.message.toHex() &
|
|
|
|
", signature: 0x" & s.signature.toHex() & ')'
|
|
|
|
|
|
|
|
# Important:
|
|
|
|
# - Due to lazy loading, when we do crypto verification
|
|
|
|
# and only then state-transition verification,
|
|
|
|
# there is no guarantee that pubkeys and signatures received are valid
|
|
|
|
# unlike when Nimbus did eager loading which ensured they were correct beforehand
|
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
template loadOrExit(signature: ValidatorSig, error: cstring):
|
|
|
|
untyped =
|
2021-01-25 18:45:48 +00:00
|
|
|
## Load a BLS signature from a raw signature
|
2021-04-09 12:59:24 +00:00
|
|
|
## Exits the **caller** with false if the signature is invalid
|
2021-01-25 18:45:48 +00:00
|
|
|
let sig = signature.load()
|
|
|
|
if sig.isNone:
|
2021-06-01 11:13:40 +00:00
|
|
|
return err(error) # this exits the calling scope, as templates are inlined.
|
2021-01-25 18:45:48 +00:00
|
|
|
sig.unsafeGet()
|
|
|
|
|
|
|
|
func addSignatureSet[T](
|
|
|
|
sigs: var seq[SignatureSet],
|
2021-06-01 11:13:40 +00:00
|
|
|
pubkey: CookedPubKey,
|
2021-01-25 18:45:48 +00:00
|
|
|
sszObj: T,
|
2021-04-26 20:39:44 +00:00
|
|
|
signature: CookedSig,
|
2021-01-25 18:45:48 +00:00
|
|
|
fork: Fork,
|
2021-06-01 11:13:40 +00:00
|
|
|
genesis_validators_root: Eth2Digest,
|
2021-01-25 18:45:48 +00:00
|
|
|
epoch: Epoch,
|
2021-04-26 20:39:44 +00:00
|
|
|
domain: DomainType) =
|
2021-01-25 18:45:48 +00:00
|
|
|
## Add a new signature set triplet (pubkey, message, signature)
|
|
|
|
## to a collection of signature sets for batch verification.
|
|
|
|
## Can return false if `signature` wasn't deserialized to a valid BLS signature.
|
|
|
|
let signing_root = compute_signing_root(
|
|
|
|
sszObj,
|
|
|
|
get_domain(
|
|
|
|
fork, domain,
|
|
|
|
epoch,
|
|
|
|
genesis_validators_root
|
|
|
|
)
|
|
|
|
).data
|
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.add((
|
2021-06-01 11:13:40 +00:00
|
|
|
blscurve.PublicKey(pubkey),
|
2021-04-26 20:39:44 +00:00
|
|
|
signing_root,
|
|
|
|
blscurve.Signature(signature)
|
|
|
|
))
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
proc aggregateAttesters(
|
2021-06-01 11:13:40 +00:00
|
|
|
validatorIndices: openArray[uint64],
|
2021-06-10 07:37:02 +00:00
|
|
|
validatorKeys: auto,
|
2021-06-01 11:13:40 +00:00
|
|
|
): Result[CookedPubKey, cstring] =
|
|
|
|
if validatorIndices.len == 0:
|
|
|
|
# Aggregation spec requires non-empty collection
|
|
|
|
# - https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04
|
|
|
|
# Eth2 spec requires at least one attesting index in attestation
|
2021-12-03 17:40:23 +00:00
|
|
|
# - https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/phase0/beacon-chain.md#is_valid_indexed_attestation
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("aggregateAttesters: no attesting indices")
|
|
|
|
|
2021-06-10 07:37:02 +00:00
|
|
|
let
|
|
|
|
firstKey = validatorKeys.load(validatorIndices[0])
|
|
|
|
|
|
|
|
if not firstKey.isSome():
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("aggregateAttesters: invalid attesting index")
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-06-10 07:37:02 +00:00
|
|
|
var attestersAgg{.noInit.}: AggregatePublicKey
|
|
|
|
|
|
|
|
attestersAgg.init(firstKey.get())
|
2021-06-01 11:13:40 +00:00
|
|
|
for i in 1 ..< validatorIndices.len:
|
2021-06-10 07:37:02 +00:00
|
|
|
let key = validatorKeys.load(validatorIndices[i])
|
|
|
|
if not key.isSome():
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("aggregateAttesters: invalid attesting index")
|
2021-06-10 07:37:02 +00:00
|
|
|
attestersAgg.aggregate(key.get())
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(finish(attestersAgg))
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2021-01-25 18:45:48 +00:00
|
|
|
proc addIndexedAttestation(
|
|
|
|
sigs: var seq[SignatureSet],
|
|
|
|
attestation: IndexedAttestation,
|
2021-06-10 07:37:02 +00:00
|
|
|
validatorKeys: auto,
|
2021-06-11 17:51:46 +00:00
|
|
|
state: ForkedHashedBeaconState,
|
2021-06-01 11:13:40 +00:00
|
|
|
): Result[void, cstring] =
|
2021-04-02 14:36:43 +00:00
|
|
|
## Add an indexed attestation for batched BLS verification
|
|
|
|
## purposes
|
|
|
|
## This only verifies cryptography, checking that
|
|
|
|
## the indices are sorted and unique is not checked for example.
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
let aggPk =
|
|
|
|
? aggregateAttesters(attestation.attesting_indices.asSeq(), validatorKeys)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-01-25 18:45:48 +00:00
|
|
|
aggPK,
|
|
|
|
attestation.data,
|
2021-06-01 11:13:40 +00:00
|
|
|
attestation.signature.loadOrExit(
|
|
|
|
"addIndexedAttestation: cannot load signature"),
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
2021-06-01 11:13:40 +00:00
|
|
|
getStateField(state, genesis_validators_root),
|
2021-01-25 18:45:48 +00:00
|
|
|
attestation.data.target.epoch,
|
2021-04-26 20:39:44 +00:00
|
|
|
DOMAIN_BEACON_ATTESTER)
|
2021-06-01 11:13:40 +00:00
|
|
|
ok()
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
proc addAttestation(
|
|
|
|
sigs: var seq[SignatureSet],
|
|
|
|
attestation: Attestation,
|
2021-06-10 07:37:02 +00:00
|
|
|
validatorKeys: auto,
|
2021-06-11 17:51:46 +00:00
|
|
|
state: ForkedHashedBeaconState,
|
2021-01-25 18:45:48 +00:00
|
|
|
cache: var StateCache
|
2021-06-01 11:13:40 +00:00
|
|
|
): Result[void, cstring] =
|
2021-06-11 17:51:46 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
var inited = false
|
2021-01-25 18:45:48 +00:00
|
|
|
var attestersAgg{.noInit.}: AggregatePublicKey
|
2021-06-11 17:51:46 +00:00
|
|
|
for valIndex in state.get_attesting_indices(
|
2021-01-25 18:45:48 +00:00
|
|
|
attestation.data,
|
|
|
|
attestation.aggregation_bits,
|
|
|
|
cache
|
|
|
|
):
|
2021-04-26 20:39:44 +00:00
|
|
|
if not inited: # first iteration
|
2021-06-10 07:37:02 +00:00
|
|
|
attestersAgg.init(validatorKeys.load(valIndex).get())
|
2021-04-26 20:39:44 +00:00
|
|
|
inited = true
|
2021-01-25 18:45:48 +00:00
|
|
|
else:
|
2021-06-10 07:37:02 +00:00
|
|
|
attestersAgg.aggregate(validatorKeys.load(valIndex).get())
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
if not inited:
|
2021-04-09 12:59:24 +00:00
|
|
|
# There were no attesters
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("addAttestation: no attesting indices")
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
let attesters = finish(attestersAgg)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-01-25 18:45:48 +00:00
|
|
|
attesters,
|
|
|
|
attestation.data,
|
2021-06-01 11:13:40 +00:00
|
|
|
attestation.signature.loadOrExit(
|
|
|
|
"addAttestation: cannot load signature"),
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
2021-06-01 11:13:40 +00:00
|
|
|
getStateField(state, genesis_validators_root),
|
2021-01-25 18:45:48 +00:00
|
|
|
attestation.data.target.epoch,
|
2021-04-26 20:39:44 +00:00
|
|
|
DOMAIN_BEACON_ATTESTER)
|
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok()
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-04-02 14:36:43 +00:00
|
|
|
# Public API
|
|
|
|
# ------------------------------------------------------
|
|
|
|
|
|
|
|
proc addAttestation*(
|
|
|
|
sigs: var seq[SignatureSet],
|
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
epochRef: auto,
|
|
|
|
attestation: Attestation
|
2021-06-01 11:13:40 +00:00
|
|
|
): Result[CookedSig, cstring] =
|
2021-04-02 14:36:43 +00:00
|
|
|
## Add an attestation for batched BLS verification
|
|
|
|
## purposes
|
|
|
|
## This only verifies cryptography
|
|
|
|
##
|
|
|
|
## Returns true if the attestation was added to the batching buffer
|
2021-04-09 12:59:24 +00:00
|
|
|
## Returns false if sanity checks failed (non-empty, keys are valid)
|
2021-04-02 14:36:43 +00:00
|
|
|
## In that case the seq[SignatureSet] is unmodified
|
2021-06-10 07:37:02 +00:00
|
|
|
mixin get_attesting_indices, validatorKey
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
var inited = false
|
2021-04-02 14:36:43 +00:00
|
|
|
var attestersAgg{.noInit.}: AggregatePublicKey
|
|
|
|
for valIndex in epochRef.get_attesting_indices(
|
|
|
|
attestation.data,
|
|
|
|
attestation.aggregation_bits):
|
2021-04-26 20:39:44 +00:00
|
|
|
if not inited: # first iteration
|
2021-06-10 07:37:02 +00:00
|
|
|
attestersAgg.init(epochRef.validatorKey(valIndex).get())
|
2021-04-26 20:39:44 +00:00
|
|
|
inited = true
|
2021-04-02 14:36:43 +00:00
|
|
|
else:
|
2021-06-10 07:37:02 +00:00
|
|
|
attestersAgg.aggregate(epochRef.validatorKey(valIndex).get())
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
if not inited:
|
2021-04-09 12:59:24 +00:00
|
|
|
# There were no attesters
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("addAttestation: no attesting indices")
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
let
|
|
|
|
attesters = finish(attestersAgg)
|
|
|
|
cookedSig = attestation.signature.loadOrExit(
|
|
|
|
"addAttestation: cannot load signature")
|
2021-04-09 12:59:24 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-04-09 12:59:24 +00:00
|
|
|
attesters,
|
|
|
|
attestation.data,
|
|
|
|
cookedSig,
|
|
|
|
fork,
|
2021-06-01 11:13:40 +00:00
|
|
|
genesis_validators_root,
|
2021-04-09 12:59:24 +00:00
|
|
|
attestation.data.target.epoch,
|
2021-04-26 20:39:44 +00:00
|
|
|
DOMAIN_BEACON_ATTESTER)
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(CookedSig(cookedSig))
|
2021-04-02 14:36:43 +00:00
|
|
|
|
|
|
|
proc addSlotSignature*(
|
|
|
|
sigs: var seq[SignatureSet],
|
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
slot: Slot,
|
2021-06-01 11:13:40 +00:00
|
|
|
pubkey: CookedPubKey,
|
|
|
|
signature: ValidatorSig): Result[void, cstring] =
|
2021-04-02 14:36:43 +00:00
|
|
|
let epoch = compute_epoch_at_slot(slot)
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-06-01 11:13:40 +00:00
|
|
|
pubkey,
|
2021-04-02 14:36:43 +00:00
|
|
|
sszObj = slot,
|
2021-06-01 11:13:40 +00:00
|
|
|
signature.loadOrExit("addSlotSignature: cannot load signature"),
|
2021-04-02 14:36:43 +00:00
|
|
|
fork,
|
2021-06-01 11:13:40 +00:00
|
|
|
genesis_validators_root,
|
2021-04-02 14:36:43 +00:00
|
|
|
epoch,
|
|
|
|
DOMAIN_SELECTION_PROOF
|
|
|
|
)
|
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok()
|
2021-04-26 20:39:44 +00:00
|
|
|
|
2021-04-02 14:36:43 +00:00
|
|
|
proc addAggregateAndProofSignature*(
|
|
|
|
sigs: var seq[SignatureSet],
|
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
aggregate_and_proof: AggregateAndProof,
|
2021-06-01 11:13:40 +00:00
|
|
|
pubkey: CookedPubKey,
|
2021-04-02 14:36:43 +00:00
|
|
|
signature: ValidatorSig
|
2021-06-01 11:13:40 +00:00
|
|
|
): Result[void, cstring] =
|
2021-04-02 14:36:43 +00:00
|
|
|
|
|
|
|
let epoch = compute_epoch_at_slot(aggregate_and_proof.aggregate.data.slot)
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-06-01 11:13:40 +00:00
|
|
|
pubkey,
|
2021-04-02 14:36:43 +00:00
|
|
|
sszObj = aggregate_and_proof,
|
2021-06-01 11:13:40 +00:00
|
|
|
signature.loadOrExit("addAggregateAndProofSignature: cannot load signature"),
|
2021-04-02 14:36:43 +00:00
|
|
|
fork,
|
2021-06-01 11:13:40 +00:00
|
|
|
genesis_validators_root,
|
2021-04-02 14:36:43 +00:00
|
|
|
epoch,
|
|
|
|
DOMAIN_AGGREGATE_AND_PROOF
|
|
|
|
)
|
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok()
|
2021-04-26 20:39:44 +00:00
|
|
|
|
2021-01-25 18:45:48 +00:00
|
|
|
proc collectSignatureSets*(
|
|
|
|
sigs: var seq[SignatureSet],
|
2021-11-05 07:34:34 +00:00
|
|
|
signed_block: ForkySignedBeaconBlock,
|
2021-06-10 07:37:02 +00:00
|
|
|
validatorKeys: auto,
|
2021-06-11 17:51:46 +00:00
|
|
|
state: ForkedHashedBeaconState,
|
2021-06-01 11:13:40 +00:00
|
|
|
cache: var StateCache): Result[void, cstring] =
|
2021-01-25 18:45:48 +00:00
|
|
|
## Collect all signatures in a single signed block.
|
|
|
|
## This includes
|
|
|
|
## - Block proposer
|
|
|
|
## - Randao Reaveal
|
|
|
|
## - Proposer slashings
|
|
|
|
## - Attester slashings
|
|
|
|
## - Attestations
|
|
|
|
## - VoluntaryExits
|
|
|
|
##
|
|
|
|
## We do not include deposits as they can be invalid per protocol
|
|
|
|
## (secp256k1 signature instead of BLS)
|
|
|
|
|
|
|
|
# Metadata
|
|
|
|
# ----------------------------------------------------
|
2021-06-10 07:37:02 +00:00
|
|
|
mixin load
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
proposer_index = signed_block.message.proposer_index
|
2021-06-10 07:37:02 +00:00
|
|
|
proposer_key = validatorKeys.load(proposer_index)
|
|
|
|
if not proposer_key.isSome():
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("collectSignatureSets: invalid proposer index")
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
let epoch = signed_block.message.slot.compute_epoch_at_slot()
|
|
|
|
|
|
|
|
# 1. Block proposer
|
|
|
|
# ----------------------------------------------------
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-06-10 07:37:02 +00:00
|
|
|
proposer_key.get(),
|
2021-12-01 09:52:40 +00:00
|
|
|
signed_block.root,
|
2021-06-01 11:13:40 +00:00
|
|
|
signed_block.signature.loadOrExit(
|
|
|
|
"collectSignatureSets: cannot load signature"),
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
2021-06-01 11:13:40 +00:00
|
|
|
getStateField(state, genesis_validators_root),
|
2021-01-25 18:45:48 +00:00
|
|
|
epoch,
|
2021-04-26 20:39:44 +00:00
|
|
|
DOMAIN_BEACON_PROPOSER)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 2. Randao Reveal
|
|
|
|
# ----------------------------------------------------
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-06-10 07:37:02 +00:00
|
|
|
proposer_key.get(),
|
2021-01-25 18:45:48 +00:00
|
|
|
epoch,
|
2021-06-01 11:13:40 +00:00
|
|
|
signed_block.message.body.randao_reveal.loadOrExit(
|
|
|
|
"collectSignatureSets: cannot load randao"),
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
2021-06-01 11:13:40 +00:00
|
|
|
getStateField(state, genesis_validators_root),
|
2021-01-25 18:45:48 +00:00
|
|
|
epoch,
|
2021-04-26 20:39:44 +00:00
|
|
|
DOMAIN_RANDAO)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 3. Proposer slashings
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_PROPOSER_SLASHINGS proposer slashings.
|
|
|
|
for i in 0 ..< signed_block.message.body.proposer_slashings.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
|
|
|
|
|
|
|
# Alias
|
|
|
|
template slashing: untyped = signed_block.message.body.proposer_slashings[i]
|
|
|
|
|
|
|
|
# Proposed block 1
|
|
|
|
block:
|
2021-06-10 07:37:02 +00:00
|
|
|
let
|
|
|
|
header_1 = slashing.signed_header_1
|
|
|
|
key_1 = validatorKeys.load(header_1.message.proposer_index)
|
|
|
|
if not key_1.isSome():
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("collectSignatureSets: invalid slashing proposer index 1")
|
|
|
|
|
2021-01-25 18:45:48 +00:00
|
|
|
let epoch1 = header_1.message.slot.compute_epoch_at_slot()
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-06-10 07:37:02 +00:00
|
|
|
key_1.get(),
|
2021-01-25 18:45:48 +00:00
|
|
|
header_1.message,
|
2021-06-01 11:13:40 +00:00
|
|
|
header_1.signature.loadOrExit(
|
|
|
|
"collectSignatureSets: cannot load proposer slashing 1 signature"),
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
2021-06-01 11:13:40 +00:00
|
|
|
getStateField(state, genesis_validators_root),
|
2021-01-25 18:45:48 +00:00
|
|
|
epoch1,
|
|
|
|
DOMAIN_BEACON_PROPOSER
|
2021-04-26 20:39:44 +00:00
|
|
|
)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# Conflicting block 2
|
|
|
|
block:
|
2021-06-10 07:37:02 +00:00
|
|
|
let
|
|
|
|
header_2 = slashing.signed_header_2
|
|
|
|
key_2 = validatorKeys.load(header_2.message.proposer_index)
|
|
|
|
if not key_2.isSome():
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("collectSignatureSets: invalid slashing proposer index 2")
|
2021-01-25 18:45:48 +00:00
|
|
|
let epoch2 = header_2.message.slot.compute_epoch_at_slot()
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-06-10 07:37:02 +00:00
|
|
|
key_2.get(),
|
2021-01-25 18:45:48 +00:00
|
|
|
header_2.message,
|
2021-06-01 11:13:40 +00:00
|
|
|
header_2.signature.loadOrExit(
|
|
|
|
"collectSignatureSets: cannot load proposer slashing 2 signature"),
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
2021-06-01 11:13:40 +00:00
|
|
|
getStateField(state, genesis_validators_root),
|
2021-01-25 18:45:48 +00:00
|
|
|
epoch2,
|
|
|
|
DOMAIN_BEACON_PROPOSER
|
2021-04-26 20:39:44 +00:00
|
|
|
)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 4. Attester slashings
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_ATTESTER_SLASHINGS attester slashings.
|
|
|
|
for i in 0 ..< signed_block.message.body.attester_slashings.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
|
|
|
|
|
|
|
# Alias
|
|
|
|
template slashing: untyped = signed_block.message.body.attester_slashings[i]
|
|
|
|
|
|
|
|
# Attestation 1
|
2021-06-01 11:13:40 +00:00
|
|
|
? sigs.addIndexedAttestation(slashing.attestation_1, validatorKeys, state)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# Conflicting attestation 2
|
2021-06-01 11:13:40 +00:00
|
|
|
? sigs.addIndexedAttestation(slashing.attestation_2, validatorKeys, state)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 5. Attestations
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_ATTESTATIONS attestations.
|
|
|
|
for i in 0 ..< signed_block.message.body.attestations.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
2021-06-01 11:13:40 +00:00
|
|
|
? sigs.addAttestation(
|
|
|
|
signed_block.message.body.attestations[i],
|
|
|
|
validatorKeys, state, cache)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 6. VoluntaryExits
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_VOLUNTARY_EXITS voluntary exits.
|
|
|
|
for i in 0 ..< signed_block.message.body.voluntary_exits.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
|
|
|
template volex: untyped = signed_block.message.body.voluntary_exits[i]
|
2021-06-10 07:37:02 +00:00
|
|
|
let key = validatorKeys.load(volex.message.validator_index)
|
|
|
|
if not key.isSome():
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("collectSignatureSets: invalid voluntary exit")
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
sigs.addSignatureSet(
|
2021-06-10 07:37:02 +00:00
|
|
|
key.get(),
|
2021-01-25 18:45:48 +00:00
|
|
|
volex.message,
|
2021-06-01 11:13:40 +00:00
|
|
|
volex.signature.loadOrExit(
|
|
|
|
"collectSignatureSets: cannot load voluntary exit signature"),
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
2021-06-01 11:13:40 +00:00
|
|
|
getStateField(state, genesis_validators_root),
|
2021-01-25 18:45:48 +00:00
|
|
|
volex.message.epoch,
|
2021-04-26 20:39:44 +00:00
|
|
|
DOMAIN_VOLUNTARY_EXIT)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
Speed up altair block processing 2x (#3115)
* Speed up altair block processing >2x
Like #3089, this PR drastially speeds up historical REST queries and
other long state replays.
* cache sync committee validator indices
* use ~80mb less memory for validator pubkey mappings
* batch-verify sync aggregate signature (fixes #2985)
* document sync committee hack with head block vs sync message block
* add batch signature verification failure tests
Before:
```
../env.sh nim c -d:release -r ncli_db --db:mainnet_0/db bench --start-slot:-1000
All time are ms
Average, StdDev, Min, Max, Samples, Test
Validation is turned off meaning that no BLS operations are performed
5830.675, 0.000, 5830.675, 5830.675, 1, Initialize DB
0.481, 1.878, 0.215, 59.167, 981, Load block from database
8422.566, 0.000, 8422.566, 8422.566, 1, Load state from database
6.996, 1.678, 0.042, 14.385, 969, Advance slot, non-epoch
93.217, 8.318, 84.192, 122.209, 32, Advance slot, epoch
20.513, 23.665, 11.510, 201.561, 981, Apply block, no slot processing
0.000, 0.000, 0.000, 0.000, 0, Database load
0.000, 0.000, 0.000, 0.000, 0, Database store
```
After:
```
7081.422, 0.000, 7081.422, 7081.422, 1, Initialize DB
0.553, 2.122, 0.175, 66.692, 981, Load block from database
5439.446, 0.000, 5439.446, 5439.446, 1, Load state from database
6.829, 1.575, 0.043, 12.156, 969, Advance slot, non-epoch
94.716, 2.749, 88.395, 100.026, 32, Advance slot, epoch
11.636, 23.766, 4.889, 205.250, 981, Apply block, no slot processing
0.000, 0.000, 0.000, 0.000, 0, Database load
0.000, 0.000, 0.000, 0.000, 0, Database store
```
* add comment
2021-11-24 12:43:50 +00:00
|
|
|
block:
|
|
|
|
# 7. SyncAggregate
|
|
|
|
# ----------------------------------------------------
|
|
|
|
withState(state):
|
|
|
|
when stateFork >= BeaconStateFork.Altair and
|
|
|
|
(signed_block is altair.SignedBeaconBlock or
|
|
|
|
signed_block is merge.SignedBeaconBlock):
|
|
|
|
let
|
|
|
|
current_sync_committee =
|
|
|
|
state.data.get_sync_committee_cache(cache).current_sync_committee
|
|
|
|
|
|
|
|
var inited = false
|
|
|
|
var attestersAgg{.noInit.}: AggregatePublicKey
|
|
|
|
for i in 0 ..< current_sync_committee.len:
|
|
|
|
if signed_block.message.body.sync_aggregate.sync_committee_bits[i]:
|
|
|
|
let key = validatorKeys.load(current_sync_committee[i])
|
|
|
|
if not key.isSome():
|
|
|
|
return err("Invalid key cache")
|
|
|
|
|
|
|
|
if not inited: # first iteration
|
|
|
|
attestersAgg.init(key.get())
|
|
|
|
inited = true
|
|
|
|
else:
|
|
|
|
attestersAgg.aggregate(key.get())
|
|
|
|
|
|
|
|
if not inited:
|
|
|
|
if signed_block.message.body.sync_aggregate.sync_committee_signature !=
|
|
|
|
default(CookedSig).toValidatorSig():
|
|
|
|
return err("process_sync_aggregate: empty sync aggregates need signature of point at infinity")
|
|
|
|
else:
|
|
|
|
let
|
|
|
|
attesters = finish(attestersAgg)
|
|
|
|
previous_slot = max(state.data.slot, Slot(1)) - 1
|
|
|
|
|
|
|
|
sigs.addSignatureSet(
|
|
|
|
attesters,
|
|
|
|
get_block_root_at_slot(state.data, previous_slot),
|
|
|
|
signed_block.message.body.sync_aggregate.sync_committee_signature.loadOrExit(
|
|
|
|
"process_sync_aggregate: cannot load signature"),
|
|
|
|
state.data.fork,
|
|
|
|
state.data.genesis_validators_root,
|
|
|
|
previous_slot.epoch,
|
|
|
|
DOMAIN_SYNC_COMMITTEE)
|
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok()
|
2021-12-06 09:49:01 +00:00
|
|
|
|
|
|
|
proc batchVerify*(verifier: var BatchVerifier, sigs: openArray[SignatureSet]): bool =
|
|
|
|
var bytes: array[32, byte]
|
|
|
|
verifier.rng[].brHmacDrbgGenerate(bytes)
|
|
|
|
try:
|
|
|
|
verifier.taskpool.batchVerify(verifier.sigVerifCache, sigs, bytes)
|
|
|
|
except Exception as exc:
|
|
|
|
raiseAssert exc.msg # Shouldn't happen
|