mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-05 03:03:26 +00:00
batch-verify sync messages for a small perf boost (#3151)
* batch-verify sync messages for a small perf boost Generally reuses the same structure as attestation and aggregate verification * normalize `signatures` and `signature_batch` to use the same pattern of verification * normalize parameter names, order etc for signature stuff in general * avoid calling `blsSign` directly - instead, go through `signatures` consistently
This commit is contained in:
parent
d93a279565
commit
069bccd51b
@ -1496,3 +1496,59 @@ proc getProposer*(
|
||||
return none(ValidatorIndex)
|
||||
|
||||
proposer
|
||||
|
||||
proc aggregateAll*(
|
||||
dag: ChainDAGRef,
|
||||
validator_indices: openArray[ValidatorIndex]): Result[CookedPubKey, cstring] =
|
||||
if validator_indices.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
|
||||
# - https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/phase0/beacon-chain.md#is_valid_indexed_attestation
|
||||
return err("aggregate: no attesting keys")
|
||||
|
||||
let
|
||||
firstKey = dag.validatorKey(validator_indices[0])
|
||||
|
||||
if not firstKey.isSome():
|
||||
return err("aggregate: invalid validator index")
|
||||
|
||||
var aggregateKey{.noInit.}: AggregatePublicKey
|
||||
|
||||
aggregateKey.init(firstKey.get())
|
||||
|
||||
for i in 1 ..< validator_indices.len:
|
||||
let key = dag.validatorKey(validator_indices[i])
|
||||
if not key.isSome():
|
||||
return err("aggregate: invalid validator index")
|
||||
aggregateKey.aggregate(key.get())
|
||||
|
||||
ok(finish(aggregateKey))
|
||||
|
||||
proc aggregateAll*(
|
||||
dag: ChainDAGRef,
|
||||
validator_indices: openArray[ValidatorIndex|uint64],
|
||||
bits: BitSeq | BitArray): Result[CookedPubKey, cstring] =
|
||||
if validator_indices.len() != bits.len():
|
||||
return err("aggregateAll: mismatch in bits length")
|
||||
|
||||
var
|
||||
aggregateKey{.noInit.}: AggregatePublicKey
|
||||
inited = false
|
||||
|
||||
for i in 0..<bits.len():
|
||||
if bits[i]:
|
||||
let key = dag.validatorKey(validator_indices[i])
|
||||
if not key.isSome():
|
||||
return err("aggregate: invalid validator index")
|
||||
|
||||
if inited:
|
||||
aggregateKey.aggregate(key.get)
|
||||
else:
|
||||
aggregateKey = AggregatePublicKey.init(key.get)
|
||||
inited = true
|
||||
|
||||
if not inited:
|
||||
err("aggregate: no attesting keys")
|
||||
else:
|
||||
ok(finish(aggregateKey))
|
||||
|
@ -11,7 +11,7 @@ import
|
||||
std/[sets, tables],
|
||||
stew/shims/hashes,
|
||||
chronicles,
|
||||
../spec/digest,
|
||||
../spec/[crypto, digest],
|
||||
../spec/datatypes/altair
|
||||
|
||||
export hashes, sets, tables, altair
|
||||
@ -97,7 +97,7 @@ func isSeen*(
|
||||
subcommitteeIndex: subcommitteeIndex.uint64)
|
||||
seenKey in pool.seenSyncMsgByAuthor
|
||||
|
||||
func addSyncCommitteeMessage*(
|
||||
proc addSyncCommitteeMessage*(
|
||||
pool: var SyncCommitteeMsgPool,
|
||||
slot: Slot,
|
||||
blockRoot: Eth2Digest,
|
||||
@ -121,6 +121,9 @@ func addSyncCommitteeMessage*(
|
||||
positionInCommittee: position,
|
||||
signature: signature)
|
||||
|
||||
debug "Sync committee message resolved",
|
||||
slot = slot, blockRoot = shortLog(blockRoot), validatorIndex
|
||||
|
||||
func computeAggregateSig(votes: seq[TrustedSyncCommitteeMsg],
|
||||
subcommitteeIndex: SyncSubcommitteeIndex,
|
||||
contribution: var SyncCommitteeContribution): bool =
|
||||
|
@ -8,6 +8,7 @@
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
std/sequtils,
|
||||
# Status
|
||||
chronicles, chronos,
|
||||
../spec/signatures_batch,
|
||||
@ -188,6 +189,7 @@ proc getBatch(batchCrypto: ref BatchCrypto): (ref Batch, bool) =
|
||||
# len will be 0 when the batch was created but nothing added to it
|
||||
# because of early failures
|
||||
(batch, batch[].len() == 0)
|
||||
|
||||
proc scheduleBatch(batchCrypto: ref BatchCrypto, fresh: bool) =
|
||||
if fresh:
|
||||
# Every time we start a new round of batching, we need to launch a deferred
|
||||
@ -201,12 +203,36 @@ proc scheduleBatch(batchCrypto: ref BatchCrypto, fresh: bool) =
|
||||
# If there's a full batch, process it eagerly assuming the callback allows
|
||||
batchCrypto.processBatch()
|
||||
|
||||
template orReturnErr(v: Option, error: cstring): untyped =
|
||||
## Returns with given error string if the option does not have a value
|
||||
let tmp = v
|
||||
if tmp.isNone:
|
||||
return err(error) # this exits the calling scope, as templates are inlined.
|
||||
tmp.unsafeGet()
|
||||
|
||||
template withBatch(
|
||||
batchCrypto: ref BatchCrypto, name: cstring,
|
||||
body: untyped): Future[BatchResult] =
|
||||
block:
|
||||
let
|
||||
(batch {.inject.}, fresh) = batchCrypto.getBatch()
|
||||
|
||||
body
|
||||
|
||||
let fut = newFuture[BatchResult](name)
|
||||
|
||||
batch[].resultsBuffer.add(fut)
|
||||
|
||||
batchCrypto.scheduleBatch(fresh)
|
||||
fut
|
||||
|
||||
# See also verify_attestation_signature
|
||||
proc scheduleAttestationCheck*(
|
||||
batchCrypto: ref BatchCrypto,
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
epochRef: EpochRef,
|
||||
attestation: Attestation
|
||||
): Result[(Future[BatchResult], CookedSig), cstring] =
|
||||
attestationData: AttestationData,
|
||||
pubkey: CookedPubKey, signature: ValidatorSig
|
||||
): Result[tuple[fut: Future[BatchResult], sig: CookedSig], cstring] =
|
||||
## Schedule crypto verification of an attestation
|
||||
##
|
||||
## The buffer is processed:
|
||||
@ -216,37 +242,23 @@ proc scheduleAttestationCheck*(
|
||||
## This returns an error if crypto sanity checks failed
|
||||
## and a future with the deferred attestation check otherwise.
|
||||
##
|
||||
let (batch, fresh) = batchCrypto.getBatch()
|
||||
let
|
||||
sig = signature.load().orReturnErr("attestation: cannot load signature")
|
||||
fut = batchCrypto.withBatch("batch_validation.scheduleAttestationCheck"):
|
||||
batch.pendingBuffer.add_attestation_signature(
|
||||
fork, genesis_validators_root, attestationData, pubkey, sig)
|
||||
|
||||
doAssert batch.pendingBuffer.len < BatchedCryptoSize
|
||||
|
||||
let sig = ? batch
|
||||
.pendingBuffer
|
||||
.addAttestation(
|
||||
fork, genesis_validators_root, epochRef,
|
||||
attestation
|
||||
)
|
||||
|
||||
let fut = newFuture[BatchResult](
|
||||
"batch_validation.scheduleAttestationCheck"
|
||||
)
|
||||
|
||||
batch[].resultsBuffer.add(fut)
|
||||
|
||||
batchCrypto.scheduleBatch(fresh)
|
||||
|
||||
return ok((fut, sig))
|
||||
ok((fut, sig))
|
||||
|
||||
proc scheduleAggregateChecks*(
|
||||
batchCrypto: ref BatchCrypto,
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
signedAggregateAndProof: SignedAggregateAndProof,
|
||||
epochRef: EpochRef,
|
||||
signedAggregateAndProof: SignedAggregateAndProof
|
||||
): Result[
|
||||
(
|
||||
tuple[slotCheck, aggregatorCheck, aggregateCheck: Future[BatchResult]],
|
||||
CookedSig
|
||||
), cstring] =
|
||||
attesting_indices: openArray[ValidatorIndex]
|
||||
): Result[tuple[
|
||||
aggregatorFut, slotFut, aggregateFut: Future[BatchResult],
|
||||
sig: CookedSig], cstring] =
|
||||
## Schedule crypto verification of an aggregate
|
||||
##
|
||||
## This involves 3 checks:
|
||||
@ -260,72 +272,111 @@ proc scheduleAggregateChecks*(
|
||||
##
|
||||
## This returns None if the signatures could not be loaded.
|
||||
## and 3 futures with the deferred aggregate checks otherwise.
|
||||
let (batch, fresh) = batchCrypto.getBatch()
|
||||
|
||||
doAssert batch[].pendingBuffer.len < BatchedCryptoSize
|
||||
|
||||
template aggregate_and_proof: untyped = signedAggregateAndProof.message
|
||||
template aggregate: untyped = aggregate_and_proof.aggregate
|
||||
|
||||
type R = (
|
||||
tuple[slotCheck, aggregatorCheck, aggregateCheck:
|
||||
Future[BatchResult]],
|
||||
CookedSig)
|
||||
# Do the eager steps first to avoid polluting batches with needlessly
|
||||
let
|
||||
aggregatorKey =
|
||||
epochRef.validatorKey(aggregate_and_proof.aggregator_index).orReturnErr(
|
||||
"SignedAggregateAndProof: invalid aggregator index")
|
||||
aggregatorSig = signedAggregateAndProof.signature.load().orReturnErr(
|
||||
"aggregateAndProof: invalid proof signature")
|
||||
slotSig = aggregate_and_proof.selection_proof.load().orReturnErr(
|
||||
"aggregateAndProof: invalid selection signature")
|
||||
aggregateKey = ? aggregateAll(epochRef.dag, attesting_indices)
|
||||
aggregateSig = aggregate.signature.load().orReturnErr(
|
||||
"aggregateAndProof: invalid aggregate signature")
|
||||
|
||||
# Enqueue in the buffer
|
||||
# ------------------------------------------------------
|
||||
let aggregator = epochRef.validatorKey(aggregate_and_proof.aggregator_index)
|
||||
if not aggregator.isSome():
|
||||
return err("scheduleAggregateChecks: invalid aggregator index")
|
||||
block:
|
||||
if (let v = batch
|
||||
.pendingBuffer
|
||||
.addSlotSignature(
|
||||
fork, genesis_validators_root,
|
||||
aggregate.data.slot,
|
||||
aggregator.get(),
|
||||
aggregate_and_proof.selection_proof
|
||||
); v.isErr()):
|
||||
return err(v.error())
|
||||
let
|
||||
aggregatorFut = batchCrypto.withBatch("scheduleAggregateChecks.aggregator"):
|
||||
batch.pendingBuffer.add_aggregate_and_proof_signature(
|
||||
fork, genesis_validators_root, aggregate_and_proof, aggregatorKey,
|
||||
aggregatorSig)
|
||||
slotFut = batchCrypto.withBatch("scheduleAggregateChecks.selection_proof"):
|
||||
batch.pendingBuffer.add_slot_signature(
|
||||
fork, genesis_validators_root, aggregate.data.slot, aggregatorKey,
|
||||
slotSig)
|
||||
aggregateFut = batchCrypto.withBatch("scheduleAggregateChecks.aggregate"):
|
||||
batch.pendingBuffer.add_attestation_signature(
|
||||
fork, genesis_validators_root, aggregate.data, aggregateKey,
|
||||
aggregateSig)
|
||||
|
||||
let futSlot = newFuture[BatchResult](
|
||||
"batch_validation.scheduleAggregateChecks.slotCheck"
|
||||
)
|
||||
batch.resultsBuffer.add(futSlot)
|
||||
ok((aggregatorFut, slotFut, aggregateFut, aggregateSig))
|
||||
|
||||
block:
|
||||
if (let v = batch
|
||||
.pendingBuffer
|
||||
.addAggregateAndProofSignature(
|
||||
fork, genesis_validators_root,
|
||||
aggregate_and_proof,
|
||||
aggregator.get(),
|
||||
signed_aggregate_and_proof.signature
|
||||
); v.isErr()):
|
||||
batchCrypto.scheduleBatch(fresh)
|
||||
return err(v.error())
|
||||
proc scheduleSyncCommitteeMessageCheck*(
|
||||
batchCrypto: ref BatchCrypto,
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, beacon_block_root: Eth2Digest,
|
||||
pubkey: CookedPubKey, signature: ValidatorSig
|
||||
): Result[tuple[fut: Future[BatchResult], sig: CookedSig], cstring] =
|
||||
## Schedule crypto verification of an attestation
|
||||
##
|
||||
## The buffer is processed:
|
||||
## - when eager processing is enabled and the batch is full
|
||||
## - otherwise after 10ms (BatchAttAccumTime)
|
||||
##
|
||||
## This returns an error if crypto sanity checks failed
|
||||
## and a future with the deferred attestation check otherwise.
|
||||
##
|
||||
let
|
||||
sig = signature.load().orReturnErr(
|
||||
"SyncCommitteMessage: cannot load signature")
|
||||
fut = batchCrypto.withBatch("scheduleSyncCommitteeMessageCheck"):
|
||||
batch.pendingBuffer.add_sync_committee_message_signature(
|
||||
fork, genesis_validators_root, slot, beacon_block_root, pubkey, sig)
|
||||
|
||||
let futAggregator = newFuture[BatchResult](
|
||||
"batch_validation.scheduleAggregateChecks.aggregatorCheck"
|
||||
)
|
||||
ok((fut, sig))
|
||||
|
||||
batch.resultsBuffer.add(futAggregator)
|
||||
proc scheduleContributionChecks*(
|
||||
batchCrypto: ref BatchCrypto,
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
signedContributionAndProof: SignedContributionAndProof,
|
||||
subcommitteeIndex: SyncSubcommitteeIndex,
|
||||
dag: ChainDAGRef): Result[tuple[
|
||||
aggregatorFut, proofFut, contributionFut: Future[BatchResult],
|
||||
sig: CookedSig], cstring] =
|
||||
## Schedule crypto verification of all signatures in a
|
||||
## SignedContributionAndProof message
|
||||
##
|
||||
## The buffer is processed:
|
||||
## - when eager processing is enabled and the batch is full
|
||||
## - otherwise after 10ms (BatchAttAccumTime)
|
||||
##
|
||||
## This returns an error if crypto sanity checks failed
|
||||
## and a future with the deferred check otherwise.
|
||||
##
|
||||
template contribution_and_proof: untyped = signedContributionAndProof.message
|
||||
template contribution: untyped = contribution_and_proof.contribution
|
||||
|
||||
let sig = batch
|
||||
.pendingBuffer
|
||||
.addAttestation(
|
||||
fork, genesis_validators_root, epochRef,
|
||||
aggregate
|
||||
)
|
||||
if sig.isErr():
|
||||
batchCrypto.scheduleBatch(fresh)
|
||||
return err(sig.error())
|
||||
# Do the eager steps first to avoid polluting batches with needlessly
|
||||
let
|
||||
aggregatorKey =
|
||||
dag.validatorKey(contribution_and_proof.aggregator_index).orReturnErr(
|
||||
"SignedAggregateAndProof: invalid contributor index")
|
||||
aggregatorSig = signedContributionAndProof.signature.load().orReturnErr(
|
||||
"SignedContributionAndProof: invalid proof signature")
|
||||
proofSig = contribution_and_proof.selection_proof.load().orReturnErr(
|
||||
"SignedContributionAndProof: invalid selection signature")
|
||||
contributionSig = contribution.signature.load().orReturnErr(
|
||||
"SignedContributionAndProof: invalid contribution signature")
|
||||
|
||||
let futAggregate = newFuture[BatchResult](
|
||||
"batch_validation.scheduleAggregateChecks.aggregateCheck"
|
||||
)
|
||||
batch.resultsBuffer.add(futAggregate)
|
||||
contributionKey = ? aggregateAll(
|
||||
dag, dag.syncCommitteeParticipants(contribution.slot, subcommitteeIndex),
|
||||
contribution.aggregation_bits)
|
||||
let
|
||||
aggregatorFut = batchCrypto.withBatch("scheduleContributionAndProofChecks.aggregator"):
|
||||
batch.pendingBuffer.add_contribution_and_proof_signature(
|
||||
fork, genesis_validators_root, contribution_and_proof, aggregatorKey,
|
||||
aggregatorSig)
|
||||
proofFut = batchCrypto.withBatch("scheduleContributionAndProofChecks.selection_proof"):
|
||||
batch.pendingBuffer.add_sync_committee_selection_proof(
|
||||
fork, genesis_validators_root, contribution.slot,
|
||||
contribution.subcommittee_index, aggregatorKey, proofSig)
|
||||
contributionFut = batchCrypto.withBatch("scheduleContributionAndProofChecks.contribution"):
|
||||
batch.pendingBuffer.add_sync_committee_message_signature(
|
||||
fork, genesis_validators_root, contribution.slot,
|
||||
contribution.beacon_block_root, contributionKey, contributionSig)
|
||||
|
||||
batchCrypto.scheduleBatch(fresh)
|
||||
|
||||
return ok(((futSlot, futAggregator, futAggregate), sig.get()))
|
||||
ok((aggregatorFut, proofFut, contributionFut, contributionSig))
|
||||
|
@ -411,7 +411,7 @@ proc syncCommitteeMessageValidator*(
|
||||
self: ref Eth2Processor,
|
||||
syncCommitteeMsg: SyncCommitteeMessage,
|
||||
subcommitteeIdx: SyncSubcommitteeIndex,
|
||||
checkSignature: bool = true): Result[void, ValidationError] =
|
||||
checkSignature: bool = true): Future[Result[void, ValidationError]] {.async.} =
|
||||
let
|
||||
wallTime = self.getCurrentBeaconTime()
|
||||
wallSlot = wallTime.slotOrZero()
|
||||
@ -426,9 +426,9 @@ proc syncCommitteeMessageValidator*(
|
||||
debug "Sync committee message received", delay
|
||||
|
||||
# Now proceed to validation
|
||||
let v = validateSyncCommitteeMessage(self.dag, self.syncCommitteeMsgPool[],
|
||||
syncCommitteeMsg, subcommitteeIdx,
|
||||
wallTime, checkSignature)
|
||||
let v = await validateSyncCommitteeMessage(
|
||||
self.dag, self.batchCrypto, self.syncCommitteeMsgPool[],
|
||||
syncCommitteeMsg, subcommitteeIdx, wallTime, checkSignature)
|
||||
return if v.isOk():
|
||||
trace "Sync committee message validated"
|
||||
let (positions, cookedSig) = v.get()
|
||||
@ -452,7 +452,7 @@ proc syncCommitteeMessageValidator*(
|
||||
proc contributionValidator*(
|
||||
self: ref Eth2Processor,
|
||||
contributionAndProof: SignedContributionAndProof,
|
||||
checkSignature: bool = true): Result[void, ValidationError] =
|
||||
checkSignature: bool = true): Future[Result[void, ValidationError]] {.async.} =
|
||||
let
|
||||
wallTime = self.getCurrentBeaconTime()
|
||||
wallSlot = wallTime.slotOrZero()
|
||||
@ -469,9 +469,9 @@ proc contributionValidator*(
|
||||
debug "Contribution received", delay
|
||||
|
||||
# Now proceed to validation
|
||||
let v = validateContribution(
|
||||
self.dag, self.syncCommitteeMsgPool[], contributionAndProof, wallTime,
|
||||
checkSignature)
|
||||
let v = await validateContribution(
|
||||
self.dag, self.batchCrypto, self.syncCommitteeMsgPool,
|
||||
contributionAndProof, wallTime, checkSignature)
|
||||
|
||||
return if v.isOk():
|
||||
trace "Contribution validated"
|
||||
|
@ -34,6 +34,12 @@ declareCounter beacon_attestations_dropped_queue_full,
|
||||
declareCounter beacon_aggregates_dropped_queue_full,
|
||||
"Number of aggregates dropped because queue is full"
|
||||
|
||||
declareCounter beacon_sync_messages_dropped_queue_full,
|
||||
"Number of sync committee messages dropped because queue is full"
|
||||
|
||||
declareCounter beacon_contributions_dropped_queue_full,
|
||||
"Number of sync committee contributions dropped because queue is full"
|
||||
|
||||
# This result is a little messy in that it returns Result.ok for
|
||||
# ValidationResult.Accept and an err for the others - this helps transport
|
||||
# an error message to callers but could arguably be done in an cleaner way.
|
||||
@ -462,29 +468,25 @@ proc validateAttestation*(
|
||||
attestation.data.target.epoch:
|
||||
return errIgnore("Attestation: Validator has already voted in epoch")
|
||||
|
||||
block:
|
||||
# First pass - without cryptography
|
||||
let v = is_valid_indexed_attestation(
|
||||
fork, genesis_validators_root, epochRef, attestation,
|
||||
{skipBLSValidation})
|
||||
if v.isErr():
|
||||
return checkedReject(v.error)
|
||||
let pubkey = epochRef.validatorKey(validator_index)
|
||||
if pubkey.isNone():
|
||||
# can't happen, in theory, because we checked the aggregator index above
|
||||
return errIgnore("Attestation: cannot find validator pubkey")
|
||||
|
||||
# In the spec, is_valid_indexed_attestation is used to verify the signature -
|
||||
# here, we do a batch verification instead
|
||||
let sig =
|
||||
if checkSignature:
|
||||
# Attestation signatures are batch-verified
|
||||
let deferredCrypto = batchCrypto
|
||||
.scheduleAttestationCheck(
|
||||
fork, genesis_validators_root, epochRef,
|
||||
attestation
|
||||
)
|
||||
.scheduleAttestationCheck(
|
||||
fork, genesis_validators_root, attestation.data,
|
||||
pubkey.get(), attestation.signature)
|
||||
if deferredCrypto.isErr():
|
||||
return checkedReject(deferredCrypto.error)
|
||||
|
||||
let (cryptoFut, sig) = deferredCrypto.get()
|
||||
# Await the crypto check
|
||||
let
|
||||
(cryptoFut, sig) = deferredCrypto.get()
|
||||
|
||||
var x = (await cryptoFut)
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
@ -625,20 +627,35 @@ proc validateAggregate*(
|
||||
genesis_validators_root =
|
||||
getStateField(pool.dag.headState.data, genesis_validators_root)
|
||||
|
||||
let attesting_indices = get_attesting_indices(
|
||||
epochRef, aggregate.data, aggregate.aggregation_bits)
|
||||
|
||||
let deferredCrypto = batchCrypto
|
||||
.scheduleAggregateChecks(
|
||||
fork, genesis_validators_root, epochRef,
|
||||
signed_aggregate_and_proof
|
||||
fork, genesis_validators_root,
|
||||
signed_aggregate_and_proof, epochRef, attesting_indices
|
||||
)
|
||||
if deferredCrypto.isErr():
|
||||
return checkedReject(deferredCrypto.error)
|
||||
|
||||
let
|
||||
(cryptoFuts, sig) = deferredCrypto.get()
|
||||
(aggregatorFut, slotFut, aggregateFut, sig) = deferredCrypto.get()
|
||||
|
||||
block:
|
||||
# [REJECT] The aggregator signature, signed_aggregate_and_proof.signature, is valid.
|
||||
var x = await aggregatorFut
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return checkedReject("Aggregate: invalid aggregator signature")
|
||||
of BatchResult.Timeout:
|
||||
beacon_aggregates_dropped_queue_full.inc()
|
||||
return errIgnore("Aggregate: timeout checking aggregator signature")
|
||||
of BatchResult.Valid:
|
||||
discard
|
||||
|
||||
block:
|
||||
# [REJECT] aggregate_and_proof.selection_proof
|
||||
var x = await cryptoFuts.slotCheck
|
||||
var x = await slotFut
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return checkedReject("Aggregate: invalid slot signature")
|
||||
@ -650,19 +667,7 @@ proc validateAggregate*(
|
||||
|
||||
block:
|
||||
# [REJECT] The aggregator signature, signed_aggregate_and_proof.signature, is valid.
|
||||
var x = await cryptoFuts.aggregatorCheck
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return checkedReject("Aggregate: invalid aggregator signature")
|
||||
of BatchResult.Timeout:
|
||||
beacon_aggregates_dropped_queue_full.inc()
|
||||
return errIgnore("Aggregate: timeout checking aggregator signature")
|
||||
of BatchResult.Valid:
|
||||
discard
|
||||
|
||||
block:
|
||||
# [REJECT] The aggregator signature, signed_aggregate_and_proof.signature, is valid.
|
||||
var x = await cryptoFuts.aggregateCheck
|
||||
var x = await aggregateFut
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return checkedReject("Aggregate: invalid aggregate signature")
|
||||
@ -688,9 +693,6 @@ proc validateAggregate*(
|
||||
pool.nextAttestationEpoch[aggregate_and_proof.aggregator_index].aggregate =
|
||||
aggregate.data.target.epoch + 1
|
||||
|
||||
let attesting_indices = get_attesting_indices(
|
||||
epochRef, aggregate.data, aggregate.aggregation_bits)
|
||||
|
||||
return ok((attesting_indices, sig))
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/p2p-interface.md#attester_slashing
|
||||
@ -772,17 +774,20 @@ proc validateVoluntaryExit*(
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.0-alpha.8/specs/altair/p2p-interface.md#sync_committee_subnet_id
|
||||
proc validateSyncCommitteeMessage*(
|
||||
dag: ChainDAGRef,
|
||||
batchCrypto: ref BatchCrypto,
|
||||
syncCommitteeMsgPool: SyncCommitteeMsgPool,
|
||||
msg: SyncCommitteeMessage,
|
||||
subcommitteeIdx: SyncSubcommitteeIndex,
|
||||
wallTime: BeaconTime,
|
||||
checkSignature: bool):
|
||||
Result[(seq[uint64], CookedSig), ValidationError] =
|
||||
Future[Result[(seq[uint64], CookedSig), ValidationError]] {.async.} =
|
||||
block:
|
||||
# [IGNORE] The signature's slot is for the current slot
|
||||
# (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance)
|
||||
# i.e. sync_committee_message.slot == current_slot.
|
||||
? check_propagation_slot_range(msg.slot, wallTime)
|
||||
let v = check_propagation_slot_range(msg.slot, wallTime)
|
||||
if v.isErr():
|
||||
return err(v.error())
|
||||
|
||||
# [REJECT] The subnet_id is valid for the given validator
|
||||
# i.e. subnet_id in compute_subnets_for_sync_committee(state, sync_committee_message.validator_index).
|
||||
@ -819,41 +824,62 @@ proc validateSyncCommitteeMessage*(
|
||||
if senderPubKey.isNone():
|
||||
return errReject("SyncCommitteeMessage: invalid validator index")
|
||||
|
||||
var cookedSignature = msg.signature.load
|
||||
if cookedSignature.isNone:
|
||||
return errReject("SyncCommitteeMessage: signature fails to load")
|
||||
let sig =
|
||||
if checkSignature:
|
||||
# Attestation signatures are batch-verified
|
||||
let deferredCrypto = batchCrypto
|
||||
.scheduleSyncCommitteeMessageCheck(
|
||||
fork, genesis_validators_root,
|
||||
msg.slot, msg.beacon_block_root,
|
||||
senderPubKey.get(), msg.signature)
|
||||
if deferredCrypto.isErr():
|
||||
return errReject(deferredCrypto.error)
|
||||
|
||||
if checkSignature and
|
||||
not verify_sync_committee_message_signature(epoch,
|
||||
msg.beacon_block_root,
|
||||
fork, genesisValidatorsRoot,
|
||||
senderPubKey.get(),
|
||||
cookedSignature.get):
|
||||
return errReject("SyncCommitteeMessage: signature fails to verify")
|
||||
# Await the crypto check
|
||||
let
|
||||
(cryptoFut, sig) = deferredCrypto.get()
|
||||
|
||||
ok((positionsInSubcommittee, cookedSignature.get()))
|
||||
var x = (await cryptoFut)
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return errReject("SyncCommitteeMessage: invalid signature")
|
||||
of BatchResult.Timeout:
|
||||
beacon_sync_messages_dropped_queue_full.inc()
|
||||
return errIgnore("SyncCommitteeMessage: timeout checking signature")
|
||||
of BatchResult.Valid:
|
||||
sig # keep going only in this case
|
||||
else:
|
||||
let sig = msg.signature.load()
|
||||
if not sig.isSome():
|
||||
return errReject("SyncCommitteeMessage: unable to load signature")
|
||||
sig.get()
|
||||
|
||||
return ok((positionsInSubcommittee, sig))
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/altair/p2p-interface.md#sync_committee_contribution_and_proof
|
||||
proc validateContribution*(
|
||||
dag: ChainDAGRef,
|
||||
syncCommitteeMsgPool: var SyncCommitteeMsgPool,
|
||||
batchCrypto: ref BatchCrypto,
|
||||
syncCommitteeMsgPool: ref SyncCommitteeMsgPool,
|
||||
msg: SignedContributionAndProof,
|
||||
wallTime: BeaconTime,
|
||||
checkSignature: bool):
|
||||
Result[CookedSig, ValidationError] =
|
||||
Future[Result[CookedSig, ValidationError]] {.async.} =
|
||||
|
||||
let
|
||||
syncCommitteeSlot = msg.message.contribution.slot
|
||||
|
||||
# [IGNORE] The contribution's slot is for the current slot
|
||||
# (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance)
|
||||
# i.e. contribution.slot == current_slot.
|
||||
? check_propagation_slot_range(msg.message.contribution.slot, wallTime)
|
||||
let
|
||||
aggregatorPubKey = dag.validatorKey(msg.message.aggregator_index)
|
||||
if aggregatorPubKey.isNone():
|
||||
return errReject("SignedContributionAndProof: invalid aggregator index")
|
||||
block:
|
||||
let v = check_propagation_slot_range(syncCommitteeSlot, wallTime) # [IGNORE]
|
||||
if v.isErr():
|
||||
return err(v.error())
|
||||
|
||||
# [REJECT] The subcommittee index is in the allowed range
|
||||
# i.e. contribution.subcommittee_index < SYNC_COMMITTEE_SUBNET_COUNT.
|
||||
let committeeIdx = msg.message.contribution.subcommittee_index.validateSyncCommitteeIndexOr:
|
||||
let subcommitteeIdx = msg.message.contribution.subcommittee_index.validateSyncCommitteeIndexOr:
|
||||
return errReject("SignedContributionAndProof: subcommittee index too high")
|
||||
|
||||
# [REJECT] contribution_and_proof.selection_proof selects the validator as an aggregator for the slot
|
||||
@ -861,14 +887,13 @@ proc validateContribution*(
|
||||
if not is_sync_committee_aggregator(msg.message.selection_proof):
|
||||
return errReject("SignedContributionAndProof: invalid selection_proof")
|
||||
|
||||
block:
|
||||
# [IGNORE] The sync committee contribution is the first valid contribution
|
||||
# received for the aggregator with index contribution_and_proof.aggregator_index
|
||||
# for the slot contribution.slot and subcommittee index contribution.subcommittee_index
|
||||
# (this requires maintaining a cache of size SYNC_COMMITTEE_SIZE for this
|
||||
# topic that can be flushed after each slot).
|
||||
if syncCommitteeMsgPool.isSeen(msg.message):
|
||||
return errIgnore("SignedContributionAndProof: duplicate contribution")
|
||||
# [IGNORE] The sync committee contribution is the first valid contribution
|
||||
# received for the aggregator with index contribution_and_proof.aggregator_index
|
||||
# for the slot contribution.slot and subcommittee index contribution.subcommittee_index
|
||||
# (this requires maintaining a cache of size SYNC_COMMITTEE_SIZE for this
|
||||
# topic that can be flushed after each slot).
|
||||
if syncCommitteeMsgPool[].isSeen(msg.message):
|
||||
return errIgnore("SignedContributionAndProof: duplicate contribution")
|
||||
|
||||
# [REJECT] The aggregator's validator index is in the declared subcommittee
|
||||
# of the current sync committee.
|
||||
@ -877,70 +902,61 @@ proc validateContribution*(
|
||||
let
|
||||
epoch = msg.message.contribution.slot.epoch
|
||||
fork = dag.forkAtEpoch(epoch)
|
||||
genesisValidatorsRoot = dag.genesisValidatorsRoot
|
||||
genesis_validators_root = dag.genesisValidatorsRoot
|
||||
|
||||
# [REJECT] The aggregator signature, signed_contribution_and_proof.signature, is valid
|
||||
if not verify_signed_contribution_and_proof_signature(msg, fork,
|
||||
genesisValidatorsRoot,
|
||||
aggregatorPubKey.get()):
|
||||
return errReject(
|
||||
"SignedContributionAndProof: aggregator signature fails to verify")
|
||||
|
||||
# [REJECT] The contribution_and_proof.selection_proof is a valid signature of the
|
||||
# SyncAggregatorSelectionData derived from the contribution by the validator with
|
||||
# index contribution_and_proof.aggregator_index.
|
||||
if not verify_selection_proof_signature(msg.message, fork,
|
||||
genesisValidatorsRoot,
|
||||
aggregatorPubKey.get()):
|
||||
return errReject(
|
||||
"SignedContributionAndProof: selection proof signature fails to verify")
|
||||
|
||||
# [REJECT] The aggregate signature is valid for the message beacon_block_root
|
||||
# and aggregate pubkey derived from the participation info in aggregation_bits
|
||||
# for the subcommittee specified by the contribution.subcommittee_index.
|
||||
var
|
||||
committeeAggKey {.noInit.}: AggregatePublicKey
|
||||
initialized = false
|
||||
syncCommitteeSlot = msg.message.contribution.slot + 1
|
||||
|
||||
for validatorIndex in dag.syncCommitteeParticipants(
|
||||
syncCommitteeSlot,
|
||||
committeeIdx,
|
||||
msg.message.contribution.aggregation_bits):
|
||||
let validatorPubKey = dag.validatorKey(validatorIndex)
|
||||
if not validatorPubKey.isSome():
|
||||
# This should never happen (!)
|
||||
warn "Invalid validator index in committee cache",
|
||||
validatorIndex
|
||||
return errIgnore("SignedContributionAndProof: Invalid committee cache")
|
||||
|
||||
if not initialized:
|
||||
initialized = true
|
||||
committeeAggKey.init(validatorPubKey.get())
|
||||
else:
|
||||
committeeAggKey.aggregate(validatorPubKey.get())
|
||||
|
||||
if not initialized:
|
||||
if msg.message.contribution.aggregation_bits.countOnes() == 0:
|
||||
# [REJECT] The contribution has participants
|
||||
# that is, any(contribution.aggregation_bits).
|
||||
return errReject("SignedContributionAndProof: aggregation bits empty")
|
||||
|
||||
let cookedSignature = msg.message.contribution.signature.load
|
||||
if cookedSignature.isNone:
|
||||
return errReject(
|
||||
"SignedContributionAndProof: aggregate signature fails to load")
|
||||
let sig = if checkSignature:
|
||||
let deferredCrypto = batchCrypto.scheduleContributionChecks(
|
||||
fork, genesis_validators_root, msg, subcommitteeIdx, dag)
|
||||
if deferredCrypto.isErr():
|
||||
return errReject(deferredCrypto.error)
|
||||
|
||||
if checkSignature and
|
||||
not verify_sync_committee_message_signature(
|
||||
epoch, msg.message.contribution.beacon_block_root, fork,
|
||||
genesisValidatorsRoot, committeeAggKey.finish, cookedSignature.get):
|
||||
debug "failing_sync_contribution",
|
||||
blk = msg.message.contribution.beacon_block_root,
|
||||
slot = syncCommitteeSlot,
|
||||
subnet = committeeIdx,
|
||||
participants = $(msg.message.contribution.aggregation_bits)
|
||||
let
|
||||
(aggregatorFut, proofFut, contributionFut, sig) = deferredCrypto.get()
|
||||
|
||||
return errReject(
|
||||
"SignedContributionAndProof: aggregate signature fails to verify")
|
||||
block:
|
||||
# [REJECT] The aggregator signature, signed_contribution_and_proof.signature, is valid
|
||||
var x = await aggregatorFut
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return errReject("SignedContributionAndProof: invalid aggregator signature")
|
||||
of BatchResult.Timeout:
|
||||
beacon_contributions_dropped_queue_full.inc()
|
||||
return errIgnore("SignedContributionAndProof: timeout checking aggregator signature")
|
||||
of BatchResult.Valid:
|
||||
discard
|
||||
|
||||
ok(cookedSignature.get)
|
||||
block:
|
||||
var x = await proofFut
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return errReject("SignedContributionAndProof: invalid proof")
|
||||
of BatchResult.Timeout:
|
||||
beacon_contributions_dropped_queue_full.inc()
|
||||
return errIgnore("SignedContributionAndProof: timeout checking proof")
|
||||
of BatchResult.Valid:
|
||||
discard
|
||||
|
||||
block:
|
||||
# [REJECT] The aggregator signature, signed_aggregate_and_proof.signature, is valid.
|
||||
var x = await contributionFut
|
||||
case x
|
||||
of BatchResult.Invalid:
|
||||
return errReject("SignedContributionAndProof: invalid contribution signature")
|
||||
of BatchResult.Timeout:
|
||||
beacon_contributions_dropped_queue_full.inc()
|
||||
return errIgnore("SignedContributionAndProof: timeout checking contribution signature")
|
||||
of BatchResult.Valid:
|
||||
discard
|
||||
sig
|
||||
else:
|
||||
let sig = msg.message.contribution.signature.load()
|
||||
if not sig.isSome():
|
||||
return errReject("SyncCommitteeMessage: unable to load signature")
|
||||
sig.get()
|
||||
|
||||
return ok(sig)
|
||||
|
@ -1056,18 +1056,18 @@ proc installMessageValidators(node: BeaconNode) =
|
||||
for committeeIdx in allSyncSubcommittees():
|
||||
closureScope:
|
||||
let idx = committeeIdx
|
||||
node.network.addValidator(
|
||||
node.network.addAsyncValidator(
|
||||
getSyncCommitteeTopic(digest, idx),
|
||||
# This proc needs to be within closureScope; don't lift out of loop.
|
||||
proc(msg: SyncCommitteeMessage): ValidationResult =
|
||||
toValidationResult(
|
||||
node.processor.syncCommitteeMessageValidator(msg, idx)))
|
||||
proc(msg: SyncCommitteeMessage): Future[ValidationResult] {.async.} =
|
||||
return toValidationResult(
|
||||
await node.processor.syncCommitteeMessageValidator(msg, idx)))
|
||||
|
||||
node.network.addValidator(
|
||||
node.network.addAsyncValidator(
|
||||
getSyncCommitteeContributionAndProofTopic(digest),
|
||||
proc(msg: SignedContributionAndProof): ValidationResult =
|
||||
toValidationResult(
|
||||
node.processor.contributionValidator(msg)))
|
||||
proc(msg: SignedContributionAndProof): Future[ValidationResult] {.async.} =
|
||||
return toValidationResult(
|
||||
await node.processor.contributionValidator(msg)))
|
||||
|
||||
installSyncCommitteeeValidators(node.dag.forkDigests.altair)
|
||||
installSyncCommitteeeValidators(node.dag.forkDigests.merge)
|
||||
|
@ -303,7 +303,7 @@ proc installApiHandlers*(node: SigningNode) =
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
msg = request.syncAggregatorSelectionData
|
||||
cooked = get_sync_aggregator_selection_data_signature(forkInfo.fork,
|
||||
cooked = get_sync_committee_selection_proof(forkInfo.fork,
|
||||
forkInfo.genesisValidatorsRoot, msg.slot, msg.subcommittee_index,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
@ -312,7 +312,7 @@ proc installApiHandlers*(node: SigningNode) =
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
msg = request.syncCommitteeContributionAndProof
|
||||
cooked = get_sync_committee_contribution_and_proof_signature(
|
||||
cooked = get_contribution_and_proof_signature(
|
||||
forkInfo.fork, forkInfo.genesisValidatorsRoot, msg,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
|
@ -7,6 +7,15 @@
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
## Signature production and verification for spec types - for every type of
|
||||
## signature, there are 3 functions:
|
||||
## * `compute_*_signing_root` - reduce message to the data that will be signed
|
||||
## * `get_*_signature` - sign the signing root with a private key
|
||||
## * `verify_*_signature` - verify a signature produced by `get_*_signature`
|
||||
##
|
||||
## See also `signatures_batch` for batch verification versions of these
|
||||
## functions.
|
||||
|
||||
import
|
||||
./datatypes/[phase0, altair, merge], ./helpers, ./eth2_merkleization
|
||||
|
||||
@ -24,7 +33,7 @@ func getDepositMessage(depositData: DepositData): DepositMessage =
|
||||
amount: depositData.amount,
|
||||
withdrawal_credentials: depositData.withdrawal_credentials)
|
||||
|
||||
func compute_slot_root*(
|
||||
func compute_slot_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot
|
||||
): Eth2Digest =
|
||||
let
|
||||
@ -37,21 +46,21 @@ func compute_slot_root*(
|
||||
func get_slot_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
blsSign(privKey, compute_slot_root(fork, genesis_validators_root, slot).data)
|
||||
let signing_root = compute_slot_signing_root(
|
||||
fork, genesis_validators_root, slot)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
proc verify_slot_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
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)
|
||||
let signing_root = compute_slot_signing_root(
|
||||
fork, genesis_validators_root, slot)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
||||
func compute_epoch_root*(
|
||||
func compute_epoch_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch
|
||||
): Eth2Digest =
|
||||
let domain = get_domain(fork, DOMAIN_RANDAO, epoch, genesis_validators_root)
|
||||
@ -61,32 +70,37 @@ func compute_epoch_root*(
|
||||
func get_epoch_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
blsSign(privKey, compute_epoch_root(fork, genesis_validators_root, epoch).data)
|
||||
let signing_root = compute_epoch_signing_root(
|
||||
fork, genesis_validators_root, epoch)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
proc verify_epoch_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch,
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
let
|
||||
domain = get_domain(fork, DOMAIN_RANDAO, epoch, genesis_validators_root)
|
||||
signing_root = compute_signing_root(epoch, domain)
|
||||
let signing_root = compute_epoch_signing_root(
|
||||
fork, genesis_validators_root, epoch)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
||||
func compute_block_root*(
|
||||
func compute_block_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
root: Eth2Digest): Eth2Digest =
|
||||
blck: Eth2Digest | SomeSomeBeaconBlock | BeaconBlockHeader): Eth2Digest =
|
||||
let
|
||||
epoch = compute_epoch_at_slot(slot)
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_BEACON_PROPOSER, epoch, genesis_validators_root)
|
||||
compute_signing_root(root, domain)
|
||||
compute_signing_root(blck, domain)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/phase0/validator.md#signature
|
||||
func get_block_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
root: Eth2Digest, privkey: ValidatorPrivKey): CookedSig =
|
||||
blsSign(privKey, compute_block_root(fork, genesis_validators_root, slot, root).data)
|
||||
let signing_root = compute_block_signing_root(
|
||||
fork, genesis_validators_root, slot, root)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
proc verify_block_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
@ -94,16 +108,14 @@ proc verify_block_signature*(
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
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)
|
||||
signing_root = compute_block_signing_root(
|
||||
fork, genesis_validators_root, slot, blck)
|
||||
|
||||
blsVerify(pubKey, signing_root.data, signature)
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
||||
func compute_aggregate_and_proof_root*(fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
aggregate_and_proof: AggregateAndProof,
|
||||
): Eth2Digest =
|
||||
func compute_aggregate_and_proof_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
aggregate_and_proof: AggregateAndProof): Eth2Digest =
|
||||
let
|
||||
epoch = compute_epoch_at_slot(aggregate_and_proof.aggregate.data.slot)
|
||||
domain = get_domain(
|
||||
@ -114,56 +126,149 @@ func compute_aggregate_and_proof_root*(fork: Fork, genesis_validators_root: Eth2
|
||||
func get_aggregate_and_proof_signature*(fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
aggregate_and_proof: AggregateAndProof,
|
||||
privKey: ValidatorPrivKey): CookedSig =
|
||||
blsSign(privKey, compute_aggregate_and_proof_root(fork, genesis_validators_root,
|
||||
aggregate_and_proof).data)
|
||||
let signing_root = compute_aggregate_and_proof_signing_root(
|
||||
fork, genesis_validators_root, aggregate_and_proof)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
proc verify_aggregate_and_proof_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
aggregate_and_proof: AggregateAndProof,
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
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)
|
||||
let signing_root = compute_aggregate_and_proof_signing_root(
|
||||
fork, genesis_validators_root, aggregate_and_proof)
|
||||
|
||||
blsVerify(pubKey, signing_root.data, signature)
|
||||
|
||||
func compute_attestation_root*(
|
||||
func compute_attestation_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
attestation_data: AttestationData
|
||||
): Eth2Digest =
|
||||
attestation_data: AttestationData): Eth2Digest =
|
||||
let
|
||||
epoch = attestation_data.target.epoch
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_BEACON_ATTESTER, epoch, genesis_validators_root)
|
||||
compute_signing_root(attestation_data, domain)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/validator.md#prepare-sync-committee-message
|
||||
func sync_committee_msg_signing_root*(
|
||||
fork: Fork, epoch: Epoch,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
block_root: Eth2Digest): Eth2Digest =
|
||||
let domain = get_domain(fork, DOMAIN_SYNC_COMMITTEE, epoch, genesis_validators_root)
|
||||
compute_signing_root(block_root, domain)
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/phase0/validator.md#aggregate-signature
|
||||
func get_attestation_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
attestation_data: AttestationData,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = compute_attestation_signing_root(
|
||||
fork, genesis_validators_root, attestation_data)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/validator.md#signature
|
||||
func contribution_and_proof_signing_root*(
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
msg: ContributionAndProof): Eth2Digest =
|
||||
let domain = get_domain(fork, DOMAIN_CONTRIBUTION_AND_PROOF,
|
||||
msg.contribution.slot.epoch,
|
||||
genesis_validators_root)
|
||||
compute_signing_root(msg, domain)
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
proc verify_attestation_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
attestation_data: AttestationData,
|
||||
pubkeys: auto, signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
let signing_root = compute_attestation_signing_root(
|
||||
fork, genesis_validators_root, attestation_data)
|
||||
|
||||
blsFastAggregateVerify(pubkeys, signing_root.data, signature)
|
||||
|
||||
func compute_deposit_signing_root*(
|
||||
version: Version,
|
||||
deposit_message: DepositMessage): Eth2Digest =
|
||||
let
|
||||
# Fork-agnostic domain since deposits are valid across forks
|
||||
domain = compute_domain(DOMAIN_DEPOSIT, version)
|
||||
compute_signing_root(deposit_message, domain)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/phase0/beacon-chain.md#deposits
|
||||
func get_deposit_signature*(preset: RuntimeConfig,
|
||||
deposit: DepositData,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = compute_deposit_signing_root(
|
||||
preset.GENESIS_FORK_VERSION, deposit.getDepositMessage())
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
func get_deposit_signature*(message: DepositMessage, version: Version,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = compute_deposit_signing_root(version, message)
|
||||
|
||||
blsSign(privkey, signing_root.data)
|
||||
|
||||
proc verify_deposit_signature*(preset: RuntimeConfig,
|
||||
deposit: DepositData): bool =
|
||||
let
|
||||
deposit_message = deposit.getDepositMessage()
|
||||
signing_root = compute_deposit_signing_root(
|
||||
preset.GENESIS_FORK_VERSION, deposit_message)
|
||||
|
||||
blsVerify(deposit.pubkey, signing_root.data, deposit.signature)
|
||||
|
||||
func compute_voluntary_exit_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
voluntary_exit: VoluntaryExit): Eth2Digest =
|
||||
let
|
||||
epoch = voluntary_exit.epoch
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_VOLUNTARY_EXIT, epoch, genesis_validators_root)
|
||||
compute_signing_root(voluntary_exit, domain)
|
||||
|
||||
func get_voluntary_exit_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
voluntary_exit: VoluntaryExit,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = compute_voluntary_exit_signing_root(
|
||||
fork, genesis_validators_root, voluntary_exit)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
proc verify_voluntary_exit_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
voluntary_exit: VoluntaryExit,
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
let signing_root = compute_voluntary_exit_signing_root(
|
||||
fork, genesis_validators_root, voluntary_exit)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/validator.md#prepare-sync-committee-message
|
||||
func compute_sync_committee_message_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, beacon_block_root: Eth2Digest): Eth2Digest =
|
||||
let domain = get_domain(
|
||||
fork, DOMAIN_SYNC_COMMITTEE, slot.epoch, genesis_validators_root)
|
||||
compute_signing_root(beacon_block_root, domain)
|
||||
|
||||
func get_sync_committee_message_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, beacon_block_root: Eth2Digest,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = compute_sync_committee_message_signing_root(
|
||||
fork, genesis_validators_root, slot, beacon_block_root)
|
||||
|
||||
blsSign(privkey, signing_root.data)
|
||||
|
||||
proc verify_sync_committee_message_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, beacon_block_root: Eth2Digest,
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
let signing_root = compute_sync_committee_message_signing_root(
|
||||
fork, genesis_validators_root, slot, beacon_block_root)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
||||
proc verify_sync_committee_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, beacon_block_root: Eth2Digest,
|
||||
pubkeys: auto, signature: SomeSig): bool =
|
||||
let signing_root = compute_sync_committee_message_signing_root(
|
||||
fork, genesis_validators_root, slot, beacon_block_root)
|
||||
|
||||
blsFastAggregateVerify(pubkeys, signing_root.data, signature)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/validator.md#aggregation-selection
|
||||
proc sync_committee_selection_proof_signing_root*(
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
slot: Slot,
|
||||
subcommittee_index: uint64): Eth2Digest =
|
||||
func compute_sync_committee_selection_proof_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, subcommittee_index: uint64): Eth2Digest =
|
||||
let
|
||||
domain = get_domain(fork, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF,
|
||||
slot.epoch, genesis_validators_root)
|
||||
@ -172,118 +277,43 @@ proc sync_committee_selection_proof_signing_root*(
|
||||
subcommittee_index: subcommittee_index)
|
||||
compute_signing_root(signing_data, domain)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/phase0/validator.md#aggregate-signature
|
||||
func get_attestation_signature*(
|
||||
func get_sync_committee_selection_proof*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
attestation_data: AttestationData,
|
||||
slot: Slot, subcommittee_index: uint64,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
blsSign(privKey, compute_attestation_root(fork, genesis_validators_root,
|
||||
attestation_data).data)
|
||||
let signing_root = compute_sync_committee_selection_proof_signing_root(
|
||||
fork, genesis_validators_root, slot, subcommittee_index)
|
||||
|
||||
proc verify_attestation_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
attestation_data: AttestationData,
|
||||
pubkeys: auto,
|
||||
signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
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/consensus-specs/blob/v1.1.6/specs/phase0/beacon-chain.md#deposits
|
||||
func get_deposit_signature*(preset: RuntimeConfig,
|
||||
deposit: DepositData,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let
|
||||
deposit_message = deposit.getDepositMessage()
|
||||
# Fork-agnostic domain since deposits are valid across forks
|
||||
domain = compute_domain(DOMAIN_DEPOSIT, preset.GENESIS_FORK_VERSION)
|
||||
signing_root = compute_signing_root(deposit_message, domain)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
func get_deposit_signature*(message: DepositMessage, version: Version,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let
|
||||
domain = compute_domain(DOMAIN_DEPOSIT, version)
|
||||
signing_root = compute_signing_root(message, domain)
|
||||
blsSign(privkey, signing_root.data)
|
||||
|
||||
proc verify_deposit_signature*(preset: RuntimeConfig,
|
||||
deposit: DepositData): bool =
|
||||
let
|
||||
deposit_message = deposit.getDepositMessage()
|
||||
# Fork-agnostic domain since deposits are valid across forks
|
||||
domain = compute_domain(DOMAIN_DEPOSIT, preset.GENESIS_FORK_VERSION)
|
||||
signing_root = compute_signing_root(deposit_message, domain)
|
||||
|
||||
blsVerify(deposit.pubkey, signing_root.data, deposit.signature)
|
||||
|
||||
func get_voluntary_exit_signature*(
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
voluntary_exit: VoluntaryExit,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch, genesis_validators_root)
|
||||
signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
proc verify_voluntary_exit_signature*(
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
voluntary_exit: VoluntaryExit,
|
||||
pubkey: ValidatorPubKey,
|
||||
signature: SomeSig): bool =
|
||||
proc verify_sync_committee_selection_proof*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, subcommittee_index: uint64,
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
withTrust(signature):
|
||||
let
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch, genesis_validators_root)
|
||||
signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
let signing_root = compute_sync_committee_selection_proof_signing_root(
|
||||
fork, genesis_validators_root, slot, subcommittee_index)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
||||
func get_sync_committee_message_signature*(fork: Fork,
|
||||
genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
block_root: Eth2Digest, privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = sync_committee_msg_signing_root(fork, slot.epoch,
|
||||
genesis_validators_root, block_root)
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/validator.md#signature
|
||||
func compute_contribution_and_proof_signing_root*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
msg: ContributionAndProof): Eth2Digest =
|
||||
let domain = get_domain(fork, DOMAIN_CONTRIBUTION_AND_PROOF,
|
||||
msg.contribution.slot.epoch,
|
||||
genesis_validators_root)
|
||||
compute_signing_root(msg, domain)
|
||||
|
||||
proc get_contribution_and_proof_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
msg: ContributionAndProof,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = compute_contribution_and_proof_signing_root(
|
||||
fork, genesis_validators_root, msg)
|
||||
|
||||
blsSign(privkey, signing_root.data)
|
||||
|
||||
func get_sync_aggregator_selection_data_signature*(fork: Fork,
|
||||
genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
subcommittee_index: uint64,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = sync_committee_selection_proof_signing_root(fork,
|
||||
genesis_validators_root, slot, subcommittee_index)
|
||||
blsSign(privkey, signing_root.data)
|
||||
|
||||
proc get_sync_committee_contribution_and_proof_signature*(fork: Fork,
|
||||
genesis_validators_root: Eth2Digest, msg: ContributionAndProof,
|
||||
privkey: ValidatorPrivKey): CookedSig =
|
||||
let signing_root = contribution_and_proof_signing_root(fork,
|
||||
genesis_validators_root, msg)
|
||||
blsSign(privkey, signing_root.data)
|
||||
|
||||
proc verify_sync_committee_message_signature*(
|
||||
epoch: Epoch,
|
||||
beacon_block_root: Eth2Digest,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
pubkey: CookedPubKey,
|
||||
signature: CookedSig): bool =
|
||||
let
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_SYNC_COMMITTEE, epoch, genesis_validators_root)
|
||||
signing_root = compute_signing_root(beacon_block_root, domain)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/validator.md#aggregation-selection
|
||||
proc is_sync_committee_aggregator*(signature: ValidatorSig): bool =
|
||||
@ -292,30 +322,11 @@ proc is_sync_committee_aggregator*(signature: ValidatorSig): bool =
|
||||
modulo = max(1'u64, (SYNC_COMMITTEE_SIZE div SYNC_COMMITTEE_SUBNET_COUNT) div TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE)
|
||||
bytes_to_uint64(signatureDigest.data.toOpenArray(0, 7)) mod modulo == 0
|
||||
|
||||
proc verify_signed_contribution_and_proof_signature*(
|
||||
msg: SignedContributionAndProof,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
pubkey: ValidatorPubKey | CookedPubKey): bool =
|
||||
let
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_CONTRIBUTION_AND_PROOF, msg.message.contribution.slot.epoch, genesis_validators_root)
|
||||
signing_root = compute_signing_root(msg.message, domain)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, msg.signature)
|
||||
|
||||
proc verify_selection_proof_signature*(
|
||||
proc verify_contribution_and_proof_signature*(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
msg: ContributionAndProof,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
pubkey: ValidatorPubKey | CookedPubKey): bool =
|
||||
let
|
||||
slot = msg.contribution.slot
|
||||
domain = get_domain(
|
||||
fork, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, slot.epoch, genesis_validators_root)
|
||||
signing_data = SyncAggregatorSelectionData(
|
||||
slot: slot,
|
||||
subcommittee_index: msg.contribution.subcommittee_index)
|
||||
signing_root = compute_signing_root(signing_data, domain)
|
||||
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||
let signing_root = compute_contribution_and_proof_signing_root(
|
||||
fork, genesis_validators_root, msg)
|
||||
|
||||
blsVerify(pubkey, signing_root.data, msg.selection_proof)
|
||||
blsVerify(pubkey, signing_root.data, signature)
|
||||
|
@ -7,6 +7,11 @@
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
## This module contains signature verification helpers corresponding to those
|
||||
## in signatures.nim, for use with signature sets / batch signature verification
|
||||
## The functions follow the same structure and use the same arguments, except
|
||||
## that the flow is split into separate collection and verification steps.
|
||||
|
||||
import
|
||||
# Status lib
|
||||
blscurve,
|
||||
@ -14,13 +19,10 @@ import
|
||||
taskpools,
|
||||
bearssl,
|
||||
# Internal
|
||||
"."/[helpers, beaconstate, forks],
|
||||
"."/[helpers, beaconstate, forks, signatures],
|
||||
"."/datatypes/[altair, merge, phase0]
|
||||
|
||||
# Otherwise, error.
|
||||
import chronicles
|
||||
|
||||
export altair, phase0, taskpools, bearssl
|
||||
export results, altair, phase0, taskpools, bearssl, signatures
|
||||
|
||||
type
|
||||
TaskPoolPtr* = TaskPool
|
||||
@ -53,35 +55,20 @@ template loadOrExit(signature: ValidatorSig, error: cstring):
|
||||
return err(error) # this exits the calling scope, as templates are inlined.
|
||||
sig.unsafeGet()
|
||||
|
||||
func addSignatureSet[T](
|
||||
sigs: var seq[SignatureSet],
|
||||
pubkey: CookedPubKey,
|
||||
sszObj: T,
|
||||
signature: CookedSig,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
epoch: Epoch,
|
||||
domain: DomainType) =
|
||||
func addSignatureSet(
|
||||
sigs: var seq[SignatureSet], pubkey: CookedPubKey, signing_root: Eth2Digest,
|
||||
signature: CookedSig) =
|
||||
## 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
|
||||
|
||||
sigs.add((
|
||||
blscurve.PublicKey(pubkey),
|
||||
signing_root,
|
||||
signing_root.data,
|
||||
blscurve.Signature(signature)
|
||||
))
|
||||
|
||||
proc aggregateAttesters(
|
||||
validatorIndices: openArray[uint64],
|
||||
validatorIndices: openArray[uint64|ValidatorIndex],
|
||||
validatorKeys: auto,
|
||||
): Result[CookedPubKey, cstring] =
|
||||
if validatorIndices.len == 0:
|
||||
@ -108,158 +95,135 @@ proc aggregateAttesters(
|
||||
|
||||
ok(finish(attestersAgg))
|
||||
|
||||
proc addIndexedAttestation(
|
||||
sigs: var seq[SignatureSet],
|
||||
attestation: IndexedAttestation,
|
||||
proc aggregateAttesters(
|
||||
validatorIndices: openArray[uint64|ValidatorIndex],
|
||||
bits: auto,
|
||||
validatorKeys: auto,
|
||||
state: ForkedHashedBeaconState,
|
||||
): Result[void, cstring] =
|
||||
## 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.
|
||||
): 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
|
||||
# - https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/phase0/beacon-chain.md#is_valid_indexed_attestation
|
||||
return err("aggregateAttesters: no attesting indices")
|
||||
|
||||
let aggPk =
|
||||
? aggregateAttesters(attestation.attesting_indices.asSeq(), validatorKeys)
|
||||
|
||||
sigs.addSignatureSet(
|
||||
aggPK,
|
||||
attestation.data,
|
||||
attestation.signature.loadOrExit(
|
||||
"addIndexedAttestation: cannot load signature"),
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
attestation.data.target.epoch,
|
||||
DOMAIN_BEACON_ATTESTER)
|
||||
ok()
|
||||
|
||||
proc addAttestation(
|
||||
sigs: var seq[SignatureSet],
|
||||
attestation: Attestation,
|
||||
validatorKeys: auto,
|
||||
state: ForkedHashedBeaconState,
|
||||
cache: var StateCache
|
||||
): Result[void, cstring] =
|
||||
var attestersAgg{.noInit.}: AggregatePublicKey
|
||||
|
||||
var inited = false
|
||||
var attestersAgg{.noInit.}: AggregatePublicKey
|
||||
for valIndex in state.get_attesting_indices(
|
||||
attestation.data,
|
||||
attestation.aggregation_bits,
|
||||
cache
|
||||
):
|
||||
if not inited: # first iteration
|
||||
attestersAgg.init(validatorKeys.load(valIndex).get())
|
||||
inited = true
|
||||
else:
|
||||
attestersAgg.aggregate(validatorKeys.load(valIndex).get())
|
||||
for i in 0..<bits.len:
|
||||
if bits[i]:
|
||||
let key = validatorKeys.load(validatorIndices[i])
|
||||
if not key.isSome():
|
||||
return err("aggregateAttesters: invalid attesting index")
|
||||
if inited:
|
||||
attestersAgg.aggregate(key.get())
|
||||
else:
|
||||
attestersAgg = AggregatePublicKey.init(key.get)
|
||||
inited = true
|
||||
|
||||
if not inited:
|
||||
# There were no attesters
|
||||
return err("addAttestation: no attesting indices")
|
||||
return err("aggregateAttesters:no attesting indices")
|
||||
|
||||
let attesters = finish(attestersAgg)
|
||||
|
||||
sigs.addSignatureSet(
|
||||
attesters,
|
||||
attestation.data,
|
||||
attestation.signature.loadOrExit(
|
||||
"addAttestation: cannot load signature"),
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
attestation.data.target.epoch,
|
||||
DOMAIN_BEACON_ATTESTER)
|
||||
|
||||
ok()
|
||||
ok(finish(attestersAgg))
|
||||
|
||||
# Public API
|
||||
# ------------------------------------------------------
|
||||
|
||||
proc addAttestation*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
epochRef: auto,
|
||||
attestation: Attestation
|
||||
): Result[CookedSig, cstring] =
|
||||
## Add an attestation for batched BLS verification
|
||||
## purposes
|
||||
## This only verifies cryptography
|
||||
##
|
||||
## Returns true if the attestation was added to the batching buffer
|
||||
## Returns false if sanity checks failed (non-empty, keys are valid)
|
||||
## In that case the seq[SignatureSet] is unmodified
|
||||
mixin get_attesting_indices, validatorKey
|
||||
# See also: verify_slot_signature
|
||||
proc add_slot_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_slot_signing_root(
|
||||
fork, genesis_validators_root, slot)
|
||||
|
||||
var inited = false
|
||||
var attestersAgg{.noInit.}: AggregatePublicKey
|
||||
for valIndex in epochRef.get_attesting_indices(
|
||||
attestation.data,
|
||||
attestation.aggregation_bits):
|
||||
if not inited: # first iteration
|
||||
attestersAgg.init(epochRef.validatorKey(valIndex).get())
|
||||
inited = true
|
||||
else:
|
||||
attestersAgg.aggregate(epochRef.validatorKey(valIndex).get())
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
if not inited:
|
||||
# There were no attesters
|
||||
return err("addAttestation: no attesting indices")
|
||||
# See also: verify_epoch_signature
|
||||
proc add_epoch_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_epoch_signing_root(
|
||||
fork, genesis_validators_root, epoch)
|
||||
|
||||
let
|
||||
attesters = finish(attestersAgg)
|
||||
cookedSig = attestation.signature.loadOrExit(
|
||||
"addAttestation: cannot load signature")
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
sigs.addSignatureSet(
|
||||
attesters,
|
||||
attestation.data,
|
||||
cookedSig,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
attestation.data.target.epoch,
|
||||
DOMAIN_BEACON_ATTESTER)
|
||||
# See also: verify_block_signature
|
||||
proc add_block_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
blck: Eth2Digest | SomeSomeBeaconBlock | BeaconBlockHeader,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_block_signing_root(
|
||||
fork, genesis_validators_root, slot, blck)
|
||||
|
||||
ok(CookedSig(cookedSig))
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
proc addSlotSignature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot,
|
||||
pubkey: CookedPubKey,
|
||||
signature: ValidatorSig): Result[void, cstring] =
|
||||
let epoch = compute_epoch_at_slot(slot)
|
||||
sigs.addSignatureSet(
|
||||
pubkey,
|
||||
sszObj = slot,
|
||||
signature.loadOrExit("addSlotSignature: cannot load signature"),
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
epoch,
|
||||
DOMAIN_SELECTION_PROOF
|
||||
)
|
||||
# See also: verify_aggregate_and_proof_signature
|
||||
proc add_aggregate_and_proof_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
aggregate_and_proof: AggregateAndProof,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_aggregate_and_proof_signing_root(
|
||||
fork, genesis_validators_root, aggregate_and_proof)
|
||||
|
||||
ok()
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
proc addAggregateAndProofSignature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
aggregate_and_proof: AggregateAndProof,
|
||||
pubkey: CookedPubKey,
|
||||
signature: ValidatorSig
|
||||
): Result[void, cstring] =
|
||||
# See also: verify_attestation_signature
|
||||
proc add_attestation_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
attestation_data: AttestationData,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_attestation_signing_root(
|
||||
fork, genesis_validators_root, attestation_data)
|
||||
|
||||
let epoch = compute_epoch_at_slot(aggregate_and_proof.aggregate.data.slot)
|
||||
sigs.addSignatureSet(
|
||||
pubkey,
|
||||
sszObj = aggregate_and_proof,
|
||||
signature.loadOrExit("addAggregateAndProofSignature: cannot load signature"),
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
epoch,
|
||||
DOMAIN_AGGREGATE_AND_PROOF
|
||||
)
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
ok()
|
||||
# See also: verify_voluntary_exit_signature
|
||||
proc add_voluntary_exit_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
voluntary_exit: VoluntaryExit,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_voluntary_exit_signing_root(
|
||||
fork, genesis_validators_root, voluntary_exit)
|
||||
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
# See also: verify_sync_committee_message_signature
|
||||
proc add_sync_committee_message_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, block_root: Eth2Digest,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_sync_committee_message_signing_root(
|
||||
fork, genesis_validators_root, slot, block_root)
|
||||
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
# See also: verify_sync_committee_selection_proof
|
||||
proc add_sync_committee_selection_proof*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
slot: Slot, subcommittee_index: uint64,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_sync_committee_selection_proof_signing_root(
|
||||
fork, genesis_validators_root, slot, subcommittee_index)
|
||||
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
proc add_contribution_and_proof_signature*(
|
||||
sigs: var seq[SignatureSet],
|
||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||
msg: ContributionAndProof,
|
||||
pubkey: CookedPubKey, signature: CookedSig) =
|
||||
let signing_root = compute_contribution_and_proof_signing_root(
|
||||
fork, genesis_validators_root, msg)
|
||||
|
||||
sigs.addSignatureSet(pubkey, signing_root, signature)
|
||||
|
||||
proc collectSignatureSets*(
|
||||
sigs: var seq[SignatureSet],
|
||||
@ -267,7 +231,9 @@ proc collectSignatureSets*(
|
||||
validatorKeys: auto,
|
||||
state: ForkedHashedBeaconState,
|
||||
cache: var StateCache): Result[void, cstring] =
|
||||
## Collect all signatures in a single signed block.
|
||||
## Collect all signature verifications that process_block would normally do
|
||||
## except deposits, in one go.
|
||||
##
|
||||
## This includes
|
||||
## - Block proposer
|
||||
## - Randao Reaveal
|
||||
@ -275,14 +241,19 @@ proc collectSignatureSets*(
|
||||
## - Attester slashings
|
||||
## - Attestations
|
||||
## - VoluntaryExits
|
||||
## - SyncCommittee (altair+)
|
||||
##
|
||||
## We do not include deposits as they can be invalid per protocol
|
||||
## (secp256k1 signature instead of BLS)
|
||||
## We do not include deposits as they can be invalid while still leaving the
|
||||
## block valid
|
||||
|
||||
# Metadata
|
||||
# ----------------------------------------------------
|
||||
mixin load
|
||||
|
||||
let
|
||||
fork = getStateField(state, fork)
|
||||
genesis_validators_root = getStateField(state, genesis_validators_root)
|
||||
|
||||
let
|
||||
proposer_index = signed_block.message.proposer_index
|
||||
proposer_key = validatorKeys.load(proposer_index)
|
||||
@ -293,27 +264,18 @@ proc collectSignatureSets*(
|
||||
|
||||
# 1. Block proposer
|
||||
# ----------------------------------------------------
|
||||
sigs.addSignatureSet(
|
||||
proposer_key.get(),
|
||||
signed_block.root,
|
||||
signed_block.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load signature"),
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
epoch,
|
||||
DOMAIN_BEACON_PROPOSER)
|
||||
sigs.add_block_signature(
|
||||
fork, genesis_validators_root,
|
||||
signed_block.message.slot, signed_block.root,
|
||||
proposer_key.get(), signed_block.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load signature"))
|
||||
|
||||
# 2. Randao Reveal
|
||||
# ----------------------------------------------------
|
||||
sigs.addSignatureSet(
|
||||
proposer_key.get(),
|
||||
epoch,
|
||||
signed_block.message.body.randao_reveal.loadOrExit(
|
||||
"collectSignatureSets: cannot load randao"),
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
epoch,
|
||||
DOMAIN_RANDAO)
|
||||
sigs.add_epoch_signature(
|
||||
fork, genesis_validators_root, epoch, proposer_key.get(),
|
||||
signed_block.message.body.randao_reveal.loadOrExit(
|
||||
"collectSignatureSets: cannot load randao"))
|
||||
|
||||
# 3. Proposer slashings
|
||||
# ----------------------------------------------------
|
||||
@ -332,41 +294,28 @@ proc collectSignatureSets*(
|
||||
# Proposed block 1
|
||||
block:
|
||||
let
|
||||
header_1 = slashing.signed_header_1
|
||||
key_1 = validatorKeys.load(header_1.message.proposer_index)
|
||||
if not key_1.isSome():
|
||||
header = slashing.signed_header_1
|
||||
key = validatorKeys.load(header.message.proposer_index)
|
||||
if not key.isSome():
|
||||
return err("collectSignatureSets: invalid slashing proposer index 1")
|
||||
|
||||
let epoch1 = header_1.message.slot.compute_epoch_at_slot()
|
||||
sigs.addSignatureSet(
|
||||
key_1.get(),
|
||||
header_1.message,
|
||||
header_1.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load proposer slashing 1 signature"),
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
epoch1,
|
||||
DOMAIN_BEACON_PROPOSER
|
||||
)
|
||||
sigs.add_block_signature(
|
||||
fork, genesis_validators_root, header.message.slot, header.message,
|
||||
key.get(), header.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load proposer slashing 1 signature"))
|
||||
|
||||
# Conflicting block 2
|
||||
block:
|
||||
let
|
||||
header_2 = slashing.signed_header_2
|
||||
key_2 = validatorKeys.load(header_2.message.proposer_index)
|
||||
if not key_2.isSome():
|
||||
header = slashing.signed_header_2
|
||||
key = validatorKeys.load(header.message.proposer_index)
|
||||
if not key.isSome():
|
||||
return err("collectSignatureSets: invalid slashing proposer index 2")
|
||||
let epoch2 = header_2.message.slot.compute_epoch_at_slot()
|
||||
sigs.addSignatureSet(
|
||||
key_2.get(),
|
||||
header_2.message,
|
||||
header_2.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load proposer slashing 2 signature"),
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
epoch2,
|
||||
DOMAIN_BEACON_PROPOSER
|
||||
)
|
||||
|
||||
sigs.add_block_signature(
|
||||
fork, genesis_validators_root, header.message.slot, header.message,
|
||||
key.get(), header.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load proposer slashing 2 signature"))
|
||||
|
||||
# 4. Attester slashings
|
||||
# ----------------------------------------------------
|
||||
@ -383,10 +332,22 @@ proc collectSignatureSets*(
|
||||
template slashing: untyped = signed_block.message.body.attester_slashings[i]
|
||||
|
||||
# Attestation 1
|
||||
? sigs.addIndexedAttestation(slashing.attestation_1, validatorKeys, state)
|
||||
block:
|
||||
let
|
||||
key = ? aggregateAttesters(
|
||||
slashing.attestation_1.attesting_indices.asSeq(), validatorKeys)
|
||||
sig = slashing.attestation_1.signature.loadOrExit("")
|
||||
sigs.add_attestation_signature(
|
||||
fork, genesis_validators_root, slashing.attestation_1.data, key, sig)
|
||||
|
||||
# Conflicting attestation 2
|
||||
? sigs.addIndexedAttestation(slashing.attestation_2, validatorKeys, state)
|
||||
block:
|
||||
let
|
||||
key = ? aggregateAttesters(
|
||||
slashing.attestation_2.attesting_indices.asSeq(), validatorKeys)
|
||||
sig = slashing.attestation_2.signature.loadOrExit("")
|
||||
sigs.add_attestation_signature(
|
||||
fork, genesis_validators_root, slashing.attestation_2.data, key, sig)
|
||||
|
||||
# 5. Attestations
|
||||
# ----------------------------------------------------
|
||||
@ -398,9 +359,17 @@ proc collectSignatureSets*(
|
||||
# don't use "items" for iterating over large type
|
||||
# due to https://github.com/nim-lang/Nim/issues/14421
|
||||
# fixed in 1.4.2
|
||||
? sigs.addAttestation(
|
||||
signed_block.message.body.attestations[i],
|
||||
validatorKeys, state, cache)
|
||||
template attestation: untyped = signed_block.message.body.attestations[i]
|
||||
|
||||
let
|
||||
key = ? aggregateAttesters(
|
||||
get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bits, cache),
|
||||
validatorKeys)
|
||||
sig = attestation.signature.loadOrExit("")
|
||||
|
||||
sigs.add_attestation_signature(
|
||||
fork, genesis_validators_root, attestation.data, key, sig)
|
||||
|
||||
# 6. VoluntaryExits
|
||||
# ----------------------------------------------------
|
||||
@ -417,15 +386,10 @@ proc collectSignatureSets*(
|
||||
if not key.isSome():
|
||||
return err("collectSignatureSets: invalid voluntary exit")
|
||||
|
||||
sigs.addSignatureSet(
|
||||
key.get(),
|
||||
volex.message,
|
||||
volex.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load voluntary exit signature"),
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
volex.message.epoch,
|
||||
DOMAIN_VOLUNTARY_EXIT)
|
||||
sigs.add_voluntary_exit_signature(
|
||||
fork, genesis_validators_root, volex.message, key.get(),
|
||||
volex.signature.loadOrExit(
|
||||
"collectSignatureSets: cannot load voluntary exit signature"))
|
||||
|
||||
block:
|
||||
# 7. SyncAggregate
|
||||
@ -434,42 +398,25 @@ proc collectSignatureSets*(
|
||||
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")
|
||||
if signed_block.message.body.sync_aggregate.sync_committee_bits.countOnes() == 0:
|
||||
if signed_block.message.body.sync_aggregate.sync_committee_signature != ValidatorSig.infinity():
|
||||
return err("collectSignatureSets: empty sync aggregates need signature of point at infinity")
|
||||
else:
|
||||
let
|
||||
attesters = finish(attestersAgg)
|
||||
current_sync_committee =
|
||||
state.data.get_sync_committee_cache(cache).current_sync_committee
|
||||
previous_slot = max(state.data.slot, Slot(1)) - 1
|
||||
beacon_block_root = get_block_root_at_slot(state.data, previous_slot)
|
||||
pubkey = ? aggregateAttesters(
|
||||
current_sync_committee,
|
||||
signed_block.message.body.sync_aggregate.sync_committee_bits,
|
||||
validatorKeys)
|
||||
|
||||
sigs.addSignatureSet(
|
||||
attesters,
|
||||
get_block_root_at_slot(state.data, previous_slot),
|
||||
sigs.add_sync_committee_message_signature(
|
||||
fork, genesis_validators_root, previous_slot, beacon_block_root,
|
||||
pubkey,
|
||||
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)
|
||||
"collectSignatureSets: cannot load signature"))
|
||||
|
||||
ok()
|
||||
|
||||
|
@ -428,15 +428,11 @@ proc process_operations(cfg: RuntimeConfig,
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#sync-committee-processing
|
||||
proc process_sync_aggregate*(
|
||||
state: var (altair.BeaconState | merge.BeaconState),
|
||||
aggregate: SomeSyncAggregate, total_active_balance: Gwei, cache: var StateCache):
|
||||
Result[void, cstring] =
|
||||
aggregate: SomeSyncAggregate, total_active_balance: Gwei,
|
||||
cache: var StateCache):
|
||||
Result[void, cstring] =
|
||||
# Verify sync committee aggregate signature signing over the previous slot
|
||||
# block root
|
||||
let
|
||||
previous_slot = max(state.slot, Slot(1)) - 1
|
||||
domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot))
|
||||
signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain)
|
||||
|
||||
when aggregate.sync_committee_signature isnot TrustedSig:
|
||||
var participant_pubkeys: seq[ValidatorPubKey]
|
||||
for i in 0 ..< state.current_sync_committee.pubkeys.len:
|
||||
@ -445,14 +441,19 @@ proc process_sync_aggregate*(
|
||||
|
||||
# p2p-interface message validators check for empty sync committees, so it
|
||||
# shouldn't run except as part of test suite.
|
||||
if participant_pubkeys.len == 0 and
|
||||
aggregate.sync_committee_signature != default(CookedSig).toValidatorSig():
|
||||
return err("process_sync_aggregate: empty sync aggregates need signature of point at infinity")
|
||||
|
||||
# Empty participants allowed
|
||||
if participant_pubkeys.len > 0 and not blsFastAggregateVerify(
|
||||
participant_pubkeys, signing_root.data, aggregate.sync_committee_signature):
|
||||
return err("process_sync_aggregate: invalid signature")
|
||||
if participant_pubkeys.len == 0:
|
||||
if aggregate.sync_committee_signature != ValidatorSig.infinity():
|
||||
return err("process_sync_aggregate: empty sync aggregates need signature of point at infinity")
|
||||
else:
|
||||
# Empty participants allowed
|
||||
let
|
||||
previous_slot = max(state.slot, Slot(1)) - 1
|
||||
beacon_block_root = get_block_root_at_slot(state, previous_slot)
|
||||
if not verify_sync_committee_signature(
|
||||
state.fork, state.genesis_validators_root, previous_slot,
|
||||
beacon_block_root, participant_pubkeys,
|
||||
aggregate.sync_committee_signature):
|
||||
return err("process_sync_aggregate: invalid signature")
|
||||
|
||||
# Compute participant and proposer rewards
|
||||
let
|
||||
|
@ -25,8 +25,8 @@ proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
|
||||
# TODO: signing_root is recomputed in signBlockProposal just after,
|
||||
# but not for locally attached validators.
|
||||
let signingRoot =
|
||||
compute_attestation_root(fork, vc.beaconGenesis.genesis_validators_root,
|
||||
adata)
|
||||
compute_attestation_signing_root(
|
||||
fork, vc.beaconGenesis.genesis_validators_root, adata)
|
||||
let attestationRoot = adata.hash_tree_root()
|
||||
|
||||
let vindex = validator.index.get()
|
||||
|
@ -45,8 +45,8 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
||||
|
||||
let blockRoot = withBlck(beaconBlock): hash_tree_root(blck)
|
||||
# TODO: signing_root is recomputed in signBlockProposal just after
|
||||
let signing_root = compute_block_root(fork, genesisRoot, slot,
|
||||
blockRoot)
|
||||
let signing_root = compute_block_signing_root(fork, genesisRoot, slot,
|
||||
blockRoot)
|
||||
let notSlashable = vc.attachedValidators
|
||||
.slashingProtection
|
||||
.registerBlock(ValidatorIndex(beaconBlock.proposer_index),
|
||||
|
@ -240,7 +240,7 @@ proc sendSyncCommitteeMessage*(
|
||||
# validation will also register the message with the sync committee
|
||||
# message pool. Notably, although libp2p calls the data handler for
|
||||
# any subscription on the subnet topic, it does not perform validation.
|
||||
let res = node.processor.syncCommitteeMessageValidator(msg, subcommitteeIdx,
|
||||
let res = await node.processor.syncCommitteeMessageValidator(msg, subcommitteeIdx,
|
||||
checkSignature)
|
||||
return
|
||||
if res.isGoodForSending:
|
||||
@ -338,7 +338,7 @@ proc sendSyncCommitteeContribution*(
|
||||
node: BeaconNode,
|
||||
msg: SignedContributionAndProof,
|
||||
checkSignature: bool): Future[SendResult] {.async.} =
|
||||
let res = node.processor.contributionValidator(
|
||||
let res = await node.processor.contributionValidator(
|
||||
msg, checkSignature)
|
||||
|
||||
return
|
||||
@ -501,7 +501,7 @@ proc proposeBlock(node: BeaconNode,
|
||||
withBlck(forkedBlck):
|
||||
let
|
||||
blockRoot = hash_tree_root(blck)
|
||||
signing_root = compute_block_root(
|
||||
signing_root = compute_block_signing_root(
|
||||
fork, genesis_validators_root, slot, blockRoot)
|
||||
|
||||
notSlashable = node.attachedValidators
|
||||
@ -620,7 +620,7 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
||||
let
|
||||
data = makeAttestationData(epochRef, attestationHead, committee_index)
|
||||
# TODO signing_root is recomputed in produceAndSignAttestation/signAttestation just after
|
||||
signing_root = compute_attestation_root(
|
||||
signing_root = compute_attestation_signing_root(
|
||||
fork, genesis_validators_root, data)
|
||||
registered = node.attachedValidators
|
||||
.slashingProtection
|
||||
@ -652,9 +652,9 @@ proc createAndSendSyncCommitteeMessage(node: BeaconNode,
|
||||
genesisValidatorsRoot = node.dag.genesisValidatorsRoot
|
||||
msg =
|
||||
block:
|
||||
let res = await signSyncCommitteeMessage(validator, slot, fork,
|
||||
let res = await signSyncCommitteeMessage(validator, fork,
|
||||
genesisValidatorsRoot,
|
||||
head.root)
|
||||
slot, head.root)
|
||||
if res.isErr():
|
||||
error "Unable to sign committee message using remote signer",
|
||||
validator = shortLog(validator), slot = slot,
|
||||
|
@ -319,19 +319,20 @@ proc signAggregateAndProof*(v: AttachedValidator,
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/validator.md#prepare-sync-committee-message
|
||||
proc signSyncCommitteeMessage*(v: AttachedValidator,
|
||||
slot: Slot, fork: Fork,
|
||||
fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
block_root: Eth2Digest
|
||||
slot: Slot,
|
||||
beacon_block_root: Eth2Digest
|
||||
): Future[SyncCommitteeMessageResult] {.async.} =
|
||||
let signature =
|
||||
case v.kind
|
||||
of ValidatorKind.Local:
|
||||
let signing_root = sync_committee_msg_signing_root(
|
||||
fork, slot.epoch, genesis_validators_root, block_root)
|
||||
blsSign(v.data.privateKey, signing_root.data).toValidatorSig()
|
||||
get_sync_committee_message_signature(
|
||||
fork, genesis_validators_root, slot, beacon_block_root,
|
||||
v.data.privateKey).toValidatorSig()
|
||||
of ValidatorKind.Remote:
|
||||
let res = await signWithRemoteValidator(v, fork, genesis_validators_root,
|
||||
slot, block_root)
|
||||
slot, beacon_block_root)
|
||||
if res.isErr():
|
||||
return SyncCommitteeMessageResult.err(res.error())
|
||||
res.get().toValidatorSig()
|
||||
@ -340,7 +341,7 @@ proc signSyncCommitteeMessage*(v: AttachedValidator,
|
||||
SyncCommitteeMessageResult.ok(
|
||||
SyncCommitteeMessage(
|
||||
slot: slot,
|
||||
beacon_block_root: block_root,
|
||||
beacon_block_root: beacon_block_root,
|
||||
validator_index: uint64(v.index.get()),
|
||||
signature: signature
|
||||
)
|
||||
@ -356,11 +357,9 @@ proc getSyncCommitteeSelectionProof*(v: AttachedValidator,
|
||||
return
|
||||
case v.kind
|
||||
of ValidatorKind.Local:
|
||||
let signing_root = sync_committee_selection_proof_signing_root(
|
||||
fork, genesis_validators_root, slot, subcommittee_index)
|
||||
SignatureResult.ok(
|
||||
blsSign(v.data.privateKey, signing_root.data).toValidatorSig()
|
||||
)
|
||||
SignatureResult.ok(get_sync_committee_selection_proof(
|
||||
fork, genesis_validators_root, slot, subcommittee_index,
|
||||
v.data.privateKey).toValidatorSig())
|
||||
of ValidatorKind.Remote:
|
||||
let res = await signWithRemoteValidator(v, fork, genesis_validators_root,
|
||||
slot, subcommittee_index)
|
||||
@ -376,9 +375,8 @@ proc sign*(v: AttachedValidator, msg: ref SignedContributionAndProof,
|
||||
msg.signature =
|
||||
case v.kind
|
||||
of ValidatorKind.Local:
|
||||
let signing_root = contribution_and_proof_signing_root(
|
||||
fork, genesis_validators_root, msg.message)
|
||||
blsSign(v.data.privateKey, signing_root.data).toValidatorSig()
|
||||
get_contribution_and_proof_signature(
|
||||
fork, genesis_validators_root, msg.message, v.data.privateKey).toValidatorSig()
|
||||
of ValidatorKind.Remote:
|
||||
let res = await signWithRemoteValidator(v, fork, genesis_validators_root,
|
||||
msg.message)
|
||||
|
@ -26,7 +26,7 @@ import
|
||||
../beacon_chain/[beacon_chain_db, beacon_clock],
|
||||
../beacon_chain/eth1/eth1_monitor,
|
||||
../beacon_chain/validators/validator_pool,
|
||||
../beacon_chain/gossip_processing/gossip_validation,
|
||||
../beacon_chain/gossip_processing/[batch_validation, gossip_validation],
|
||||
../beacon_chain/consensus_object_pools/[blockchain_dag, block_quarantine,
|
||||
block_clearance, attestation_pool,
|
||||
sync_committee_msg_pool],
|
||||
@ -90,6 +90,8 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
||||
verifier = BatchVerifier(rng: keys.newRng(), taskpool: taskpool)
|
||||
quarantine = newClone(Quarantine.init())
|
||||
attPool = AttestationPool.init(dag, quarantine)
|
||||
batchCrypto = BatchCrypto.new(
|
||||
keys.newRng(), eager = proc(): bool = true, taskpool)
|
||||
syncCommitteePool = newClone SyncCommitteeMsgPool.init()
|
||||
timers: array[Timers, RunningStat]
|
||||
attesters: RunningStat
|
||||
@ -145,10 +147,8 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
||||
|
||||
let
|
||||
syncCommittee = @(dag.syncCommitteeParticipants(slot + 1))
|
||||
genesisValidatorsRoot = dag.genesisValidatorsRoot
|
||||
genesis_validators_root = dag.genesisValidatorsRoot
|
||||
fork = dag.forkAtEpoch(slot.epoch)
|
||||
signingRoot = sync_committee_msg_signing_root(
|
||||
fork, slot.epoch, genesisValidatorsRoot, dag.head.root)
|
||||
messagesTime = slot.toBeaconTime(seconds(SECONDS_PER_SLOT div 3))
|
||||
contributionsTime = slot.toBeaconTime(seconds(2 * SECONDS_PER_SLOT div 3))
|
||||
|
||||
@ -160,15 +160,17 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
||||
continue
|
||||
|
||||
let
|
||||
validarorPrivKey = MockPrivKeys[validatorIdx]
|
||||
signature = blsSign(validarorPrivKey, signingRoot.data)
|
||||
validatorPrivKey = MockPrivKeys[validatorIdx]
|
||||
signature = get_sync_committee_message_signature(
|
||||
fork, genesis_validators_root, slot, dag.head.root, validatorPrivKey)
|
||||
msg = SyncCommitteeMessage(
|
||||
slot: slot,
|
||||
beacon_block_root: dag.head.root,
|
||||
validator_index: uint64 validatorIdx,
|
||||
signature: signature.toValidatorSig)
|
||||
|
||||
let res = dag.validateSyncCommitteeMessage(
|
||||
let res = waitFor dag.validateSyncCommitteeMessage(
|
||||
batchCrypto,
|
||||
syncCommitteePool[],
|
||||
msg,
|
||||
subcommitteeIdx,
|
||||
@ -188,11 +190,9 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
||||
positions)
|
||||
|
||||
let
|
||||
selectionProofSigningRoot =
|
||||
sync_committee_selection_proof_signing_root(
|
||||
fork, genesisValidatorsRoot, slot, uint64 subcommitteeIdx)
|
||||
selectionProofSig = blsSign(
|
||||
validarorPrivKey, selectionProofSigningRoot.data).toValidatorSig
|
||||
selectionProofSig = get_sync_committee_selection_proof(
|
||||
fork, genesis_validators_root, slot, uint64 subcommitteeIdx,
|
||||
validatorPrivKey).toValidatorSig
|
||||
|
||||
if is_sync_committee_aggregator(selectionProofSig):
|
||||
aggregators.add Aggregator(
|
||||
@ -212,18 +212,18 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
||||
contribution: contribution,
|
||||
selection_proof: aggregator.selectionProof)
|
||||
|
||||
signingRoot = contribution_and_proof_signing_root(
|
||||
fork, genesisValidatorsRoot, contributionAndProof)
|
||||
|
||||
validarorPrivKey =
|
||||
validatorPrivKey =
|
||||
MockPrivKeys[aggregator.validatorIdx.ValidatorIndex]
|
||||
|
||||
signedContributionAndProof = SignedContributionAndProof(
|
||||
message: contributionAndProof,
|
||||
signature: blsSign(validarorPrivKey, signingRoot.data).toValidatorSig)
|
||||
signature: get_contribution_and_proof_signature(
|
||||
fork, genesis_validators_root, contributionAndProof,
|
||||
validatorPrivKey).toValidatorSig)
|
||||
|
||||
res = dag.validateContribution(
|
||||
syncCommitteePool[],
|
||||
res = waitFor dag.validateContribution(
|
||||
batchCrypto,
|
||||
syncCommitteePool,
|
||||
signedContributionAndProof,
|
||||
contributionsTime,
|
||||
false)
|
||||
|
@ -34,8 +34,6 @@ proc compute_aggregate_sync_committee_signature(
|
||||
root =
|
||||
if block_root != ZERO_HASH: block_root
|
||||
else: mockBlockForNextSlot(forked).altairData.message.parent_root
|
||||
signing_root = sync_committee_msg_signing_root(
|
||||
state.fork, state.slot.epoch, state.genesis_validators_root, root)
|
||||
|
||||
var
|
||||
aggregateSig {.noInit.}: AggregateSignature
|
||||
@ -43,7 +41,8 @@ proc compute_aggregate_sync_committee_signature(
|
||||
for validator_index in participants:
|
||||
let
|
||||
privkey = MockPrivKeys[validator_index]
|
||||
signature = blsSign(privkey, signing_root.data)
|
||||
signature = get_sync_committee_message_signature(
|
||||
state.fork, state.genesis_validators_root, state.slot, root, privkey)
|
||||
if not initialized:
|
||||
initialized = true
|
||||
aggregateSig.init(signature)
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import
|
||||
# Standard library
|
||||
std/[json, options, os, strutils, tables],
|
||||
std/[json, os, strutils, tables],
|
||||
# Status libraries
|
||||
stew/[results, endians2], chronicles,
|
||||
eth/keys, taskpools,
|
||||
|
@ -180,11 +180,15 @@ suite "Gossip validation - Extra": # Not based on preset config
|
||||
var cfg = defaultRuntimeConfig
|
||||
cfg.ALTAIR_FORK_EPOCH = (GENESIS_EPOCH + 1).Epoch
|
||||
cfg
|
||||
taskpool = Taskpool.new()
|
||||
quarantine = newClone(Quarantine.init())
|
||||
batchCrypto = BatchCrypto.new(keys.newRng(), eager = proc(): bool = false, taskpool)
|
||||
var
|
||||
verifier = BatchVerifier(rng: keys.newRng(), taskpool: Taskpool.new())
|
||||
|
||||
dag = block:
|
||||
var
|
||||
dag = ChainDAGRef.init(
|
||||
cfg, makeTestDB(num_validators), {})
|
||||
verifier = BatchVerifier(rng: keys.newRng(), taskpool: Taskpool.new())
|
||||
let
|
||||
dag = ChainDAGRef.init(cfg, makeTestDB(num_validators), {})
|
||||
quarantine = newClone(Quarantine.init())
|
||||
var cache = StateCache()
|
||||
for blck in makeTestBlocks(
|
||||
@ -217,13 +221,13 @@ suite "Gossip validation - Extra": # Not based on preset config
|
||||
validator = AttachedValidator(pubKey: pubkey,
|
||||
kind: ValidatorKind.Local, data: privateItem, index: some(index))
|
||||
resMsg = waitFor signSyncCommitteeMessage(
|
||||
validator, slot,
|
||||
state[].data.fork, state[].data.genesis_validators_root, state[].root)
|
||||
validator, state[].data.fork, state[].data.genesis_validators_root, slot,
|
||||
state[].root)
|
||||
msg = resMsg.get()
|
||||
|
||||
syncCommitteeMsgPool = newClone(SyncCommitteeMsgPool.init())
|
||||
res = validateSyncCommitteeMessage(
|
||||
dag, syncCommitteeMsgPool[], msg, subcommitteeIdx,
|
||||
res = waitFor validateSyncCommitteeMessage(
|
||||
dag, batchCrypto, syncCommitteeMsgPool[], msg, subcommitteeIdx,
|
||||
slot.toBeaconTime(), true)
|
||||
(positions, cookedSig) = res.get()
|
||||
|
||||
@ -257,5 +261,5 @@ suite "Gossip validation - Extra": # Not based on preset config
|
||||
|
||||
# Same message twice should be ignored
|
||||
validateSyncCommitteeMessage(
|
||||
dag, syncCommitteeMsgPool[], msg, subcommitteeIdx,
|
||||
state[].data.slot.toBeaconTime(), true).isErr()
|
||||
dag, batchCrypto, syncCommitteeMsgPool[], msg, subcommitteeIdx,
|
||||
state[].data.slot.toBeaconTime(), true).waitFor().isErr()
|
||||
|
@ -10,7 +10,7 @@
|
||||
import
|
||||
std/random,
|
||||
unittest2,
|
||||
../beacon_chain/spec/[crypto, signatures],
|
||||
../beacon_chain/spec/[crypto, helpers, signatures],
|
||||
./testblockutil
|
||||
|
||||
suite "Message signatures":
|
||||
@ -175,69 +175,69 @@ suite "Message signatures":
|
||||
|
||||
test "Sync committee message signatures":
|
||||
let
|
||||
epoch = default(Epoch)
|
||||
slot = default(Slot)
|
||||
epoch = slot.epoch
|
||||
block_root = default(Eth2Digest)
|
||||
|
||||
check:
|
||||
# Matching public/private keys and genesis validator roots
|
||||
verify_sync_committee_message_signature(
|
||||
epoch, block_root, fork0, genesis_validators_root0, load(pubkey0).get,
|
||||
blsSign(privkey0, sync_committee_msg_signing_root(
|
||||
fork0, epoch, genesis_validators_root0, block_root).data))
|
||||
fork0, genesis_validators_root0, slot, block_root, load(pubkey0).get,
|
||||
get_sync_committee_message_signature(
|
||||
fork0, genesis_validators_root0, slot, block_root, privkey0).toValidatorSig())
|
||||
|
||||
# Mismatched public/private keys
|
||||
not verify_sync_committee_message_signature(
|
||||
epoch, block_root, fork0, genesis_validators_root0, load(pubkey0).get,
|
||||
blsSign(privkey1, sync_committee_msg_signing_root(
|
||||
fork0, epoch, genesis_validators_root0, block_root).data))
|
||||
|
||||
fork0, genesis_validators_root0, slot, block_root, load(pubkey0).get,
|
||||
get_sync_committee_message_signature(
|
||||
fork0, genesis_validators_root0, slot, block_root, privkey1).toValidatorSig())
|
||||
# Mismatched forks
|
||||
not verify_sync_committee_message_signature(
|
||||
epoch, block_root, fork0, genesis_validators_root0, load(pubkey0).get,
|
||||
blsSign(privkey0, sync_committee_msg_signing_root(
|
||||
fork1, epoch, genesis_validators_root0, block_root).data))
|
||||
fork0, genesis_validators_root0, slot, block_root, load(pubkey0).get,
|
||||
get_sync_committee_message_signature(
|
||||
fork1, genesis_validators_root0, slot, block_root, privkey0).toValidatorSig())
|
||||
|
||||
# Mismatched genesis validator roots
|
||||
not verify_sync_committee_message_signature(
|
||||
epoch, block_root, fork0, genesis_validators_root0, load(pubkey0).get,
|
||||
blsSign(privkey0, sync_committee_msg_signing_root(
|
||||
fork0, epoch, genesis_validators_root1, block_root).data))
|
||||
fork0, genesis_validators_root0, slot, block_root, load(pubkey0).get,
|
||||
get_sync_committee_message_signature(
|
||||
fork0, genesis_validators_root1, slot, block_root, privkey0).toValidatorSig())
|
||||
|
||||
test "Sync committee signed contribution and proof signatures":
|
||||
let signed_contribution_and_proof = default(SignedContributionAndProof)
|
||||
let contribution_and_proof = default(ContributionAndProof)
|
||||
|
||||
check:
|
||||
# Matching public/private keys and genesis validator roots
|
||||
verify_signed_contribution_and_proof_signature(
|
||||
SignedContributionAndProof(signature: blsSign(
|
||||
privkey0, contribution_and_proof_signing_root(
|
||||
fork0, genesis_validators_root0,
|
||||
signed_contribution_and_proof.message).data).toValidatorSig),
|
||||
fork0, genesis_validators_root0, load(pubkey0).get)
|
||||
verify_contribution_and_proof_signature(
|
||||
fork0, genesis_validators_root0, contribution_and_proof,
|
||||
load(pubkey0).get,
|
||||
get_contribution_and_proof_signature(
|
||||
fork0, genesis_validators_root0,
|
||||
contribution_and_proof, privkey0).toValidatorSig)
|
||||
|
||||
# Mismatched public/private keys
|
||||
not verify_signed_contribution_and_proof_signature(
|
||||
SignedContributionAndProof(signature: blsSign(
|
||||
privkey1, contribution_and_proof_signing_root(
|
||||
fork0, genesis_validators_root0,
|
||||
signed_contribution_and_proof.message).data).toValidatorSig),
|
||||
fork0, genesis_validators_root0, load(pubkey0).get)
|
||||
not verify_contribution_and_proof_signature(
|
||||
fork0, genesis_validators_root0, contribution_and_proof,
|
||||
load(pubkey0).get,
|
||||
get_contribution_and_proof_signature(
|
||||
fork0, genesis_validators_root0,
|
||||
contribution_and_proof, privkey1).toValidatorSig)
|
||||
|
||||
# Mismatched forks
|
||||
not verify_signed_contribution_and_proof_signature(
|
||||
SignedContributionAndProof(signature: blsSign(
|
||||
privkey0, contribution_and_proof_signing_root(
|
||||
fork1, genesis_validators_root0,
|
||||
signed_contribution_and_proof.message).data).toValidatorSig),
|
||||
fork0, genesis_validators_root0, load(pubkey0).get)
|
||||
not verify_contribution_and_proof_signature(
|
||||
fork0, genesis_validators_root0, contribution_and_proof,
|
||||
load(pubkey0).get,
|
||||
get_contribution_and_proof_signature(
|
||||
fork1, genesis_validators_root0,
|
||||
contribution_and_proof, privkey0).toValidatorSig)
|
||||
|
||||
# Mismatched genesis validator roots
|
||||
not verify_signed_contribution_and_proof_signature(
|
||||
SignedContributionAndProof(signature: blsSign(
|
||||
privkey0, contribution_and_proof_signing_root(
|
||||
fork0, genesis_validators_root0,
|
||||
signed_contribution_and_proof.message).data).toValidatorSig),
|
||||
fork0, genesis_validators_root1, load(pubkey0).get)
|
||||
not verify_contribution_and_proof_signature(
|
||||
fork0, genesis_validators_root0, contribution_and_proof,
|
||||
load(pubkey0).get,
|
||||
get_contribution_and_proof_signature(
|
||||
fork0, genesis_validators_root1,
|
||||
contribution_and_proof, privkey0).toValidatorSig)
|
||||
|
||||
test "Sync committee selection proof signatures":
|
||||
let
|
||||
@ -246,45 +246,29 @@ suite "Message signatures":
|
||||
|
||||
check:
|
||||
# Matching public/private keys and genesis validator roots
|
||||
verify_selection_proof_signature(
|
||||
ContributionAndProof(
|
||||
contribution: SyncCommitteeContribution(
|
||||
slot: slot, subcommittee_index: subcommittee_index),
|
||||
selection_proof: blsSign(
|
||||
privkey0, sync_committee_selection_proof_signing_root(
|
||||
fork0, genesis_validators_root0, slot,
|
||||
subcommittee_index).data).toValidatorSig),
|
||||
fork0, genesis_validators_root0, load(pubkey0).get)
|
||||
verify_sync_committee_selection_proof(
|
||||
fork0, genesis_validators_root0, slot, subcommittee_index,
|
||||
load(pubkey0).get, get_sync_committee_selection_proof(
|
||||
fork0, genesis_validators_root0, slot,
|
||||
subcommittee_index, privkey0).toValidatorSig)
|
||||
|
||||
# Mismatched public/private keys
|
||||
not verify_selection_proof_signature(
|
||||
ContributionAndProof(
|
||||
contribution: SyncCommitteeContribution(
|
||||
slot: slot, subcommittee_index: subcommittee_index),
|
||||
selection_proof: blsSign(
|
||||
privkey1, sync_committee_selection_proof_signing_root(
|
||||
fork0, genesis_validators_root0, slot,
|
||||
subcommittee_index).data).toValidatorSig),
|
||||
fork0, genesis_validators_root0, load(pubkey0).get)
|
||||
not verify_sync_committee_selection_proof(
|
||||
fork0, genesis_validators_root0, slot, subcommittee_index,
|
||||
load(pubkey0).get, get_sync_committee_selection_proof(
|
||||
fork0, genesis_validators_root0, slot,
|
||||
subcommittee_index, privkey1).toValidatorSig)
|
||||
|
||||
# Mismatched forks
|
||||
not verify_selection_proof_signature(
|
||||
ContributionAndProof(
|
||||
contribution: SyncCommitteeContribution(
|
||||
slot: slot, subcommittee_index: subcommittee_index),
|
||||
selection_proof: blsSign(
|
||||
privkey0, sync_committee_selection_proof_signing_root(
|
||||
fork0, genesis_validators_root0, slot,
|
||||
subcommittee_index).data).toValidatorSig),
|
||||
fork1, genesis_validators_root0, load(pubkey0).get)
|
||||
not verify_sync_committee_selection_proof(
|
||||
fork0, genesis_validators_root0, slot, subcommittee_index,
|
||||
load(pubkey0).get, get_sync_committee_selection_proof(
|
||||
fork1, genesis_validators_root0, slot,
|
||||
subcommittee_index, privkey0).toValidatorSig)
|
||||
|
||||
# Mismatched genesis validator roots
|
||||
not verify_selection_proof_signature(
|
||||
ContributionAndProof(
|
||||
contribution: SyncCommitteeContribution(
|
||||
slot: slot, subcommittee_index: subcommittee_index),
|
||||
selection_proof: blsSign(
|
||||
privkey1, sync_committee_selection_proof_signing_root(
|
||||
fork0, genesis_validators_root0, slot,
|
||||
subcommittee_index).data).toValidatorSig),
|
||||
fork0, genesis_validators_root1, load(pubkey0).get)
|
||||
not verify_sync_committee_selection_proof(
|
||||
fork0, genesis_validators_root0, slot, subcommittee_index,
|
||||
load(pubkey0).get, get_sync_committee_selection_proof(
|
||||
fork0, genesis_validators_root1, slot,
|
||||
subcommittee_index, privkey0).toValidatorSig)
|
||||
|
@ -62,17 +62,14 @@ suite "Sync committee pool":
|
||||
subcommittee1 = SyncSubcommitteeIndex(0)
|
||||
subcommittee2 = SyncSubcommitteeIndex(1)
|
||||
|
||||
sig1 = blsSign(privkey1, sync_committee_msg_signing_root(
|
||||
fork, root1Slot.epoch, genesisValidatorsRoot, root1).data)
|
||||
|
||||
sig2 = blsSign(privkey2, sync_committee_msg_signing_root(
|
||||
fork, root2Slot.epoch, genesisValidatorsRoot, root1).data)
|
||||
|
||||
sig3 = blsSign(privkey3, sync_committee_msg_signing_root(
|
||||
fork, root3Slot.epoch, genesisValidatorsRoot, root1).data)
|
||||
|
||||
sig4 = blsSign(privkey4, sync_committee_msg_signing_root(
|
||||
fork, root3Slot.epoch, genesisValidatorsRoot, root2).data)
|
||||
sig1 = get_sync_committee_message_signature(
|
||||
fork, genesisValidatorsRoot, root1Slot, root1, privkey1)
|
||||
sig2 = get_sync_committee_message_signature(
|
||||
fork, genesisValidatorsRoot, root2Slot, root2, privkey1)
|
||||
sig3 = get_sync_committee_message_signature(
|
||||
fork, genesisValidatorsRoot, root3Slot, root3, privkey1)
|
||||
sig4 = get_sync_committee_message_signature(
|
||||
fork, genesisValidatorsRoot, root3Slot, root2, privkey1)
|
||||
|
||||
# Inserting sync committee messages
|
||||
#
|
||||
|
@ -346,7 +346,7 @@ proc makeSyncAggregate(
|
||||
fork, genesis_validators_root,
|
||||
slot, latest_block_root,
|
||||
MockPrivKeys[validatorIdx])
|
||||
selectionProofSig = get_sync_aggregator_selection_data_signature(
|
||||
selectionProofSig = get_sync_committee_selection_proof(
|
||||
fork, genesis_validators_root,
|
||||
slot, subcommitteeIdx.uint64,
|
||||
MockPrivKeys[validatorIdx])
|
||||
@ -371,7 +371,7 @@ proc makeSyncAggregate(
|
||||
aggregator_index: uint64 aggregator.validatorIdx,
|
||||
contribution: contribution,
|
||||
selection_proof: aggregator.selectionProof)
|
||||
contributionSig = get_sync_committee_contribution_and_proof_signature(
|
||||
contributionSig = get_contribution_and_proof_signature(
|
||||
fork, genesis_validators_root,
|
||||
contributionAndProof,
|
||||
MockPrivKeys[aggregator.validatorIdx])
|
||||
|
Loading…
x
Reference in New Issue
Block a user