2021-01-25 18:45:48 +00:00
|
|
|
# beacon_chain
|
2024-01-06 14:26:56 +00:00
|
|
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
2021-01-25 18:45:48 +00:00
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2023-01-20 14:14:37 +00:00
|
|
|
{.push raises: [].}
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-12-09 12:56:54 +00:00
|
|
|
## 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.
|
|
|
|
|
2021-01-25 18:45:48 +00:00
|
|
|
import
|
|
|
|
# Status lib
|
|
|
|
blscurve,
|
2024-01-16 22:37:14 +00:00
|
|
|
stew/byteutils,
|
|
|
|
results,
|
2021-12-06 09:49:01 +00:00
|
|
|
taskpools,
|
2022-06-21 08:29:16 +00:00
|
|
|
bearssl/rand,
|
2021-01-25 18:45:48 +00:00
|
|
|
# Internal
|
2021-12-09 12:56:54 +00:00
|
|
|
"."/[helpers, beaconstate, forks, signatures],
|
2022-01-06 11:25:35 +00:00
|
|
|
"."/datatypes/[altair, bellatrix, phase0]
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2022-06-21 08:29:16 +00:00
|
|
|
export results, rand, altair, phase0, taskpools, signatures
|
2021-12-06 09:49:01 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
BatchVerifier* = object
|
2023-08-03 08:36:45 +00:00
|
|
|
sigVerifCache*: BatchedBLSVerifierCache
|
|
|
|
## A cache for batch BLS signature verification contexts
|
|
|
|
rng*: ref HmacDrbgContext
|
|
|
|
## A reference to the Nimbus application-wide RNG
|
2024-12-11 11:12:56 +00:00
|
|
|
taskpool*: Taskpool
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2023-08-03 08:36:45 +00:00
|
|
|
proc init*(
|
|
|
|
T: type BatchVerifier, rng: ref HmacDrbgContext,
|
2024-12-11 11:12:56 +00:00
|
|
|
taskpool: Taskpool): BatchVerifier =
|
2023-08-03 08:36:45 +00:00
|
|
|
BatchVerifier(
|
|
|
|
sigVerifCache: BatchedBLSVerifierCache.init(taskpool),
|
|
|
|
rng: rng,
|
|
|
|
taskpool: taskpool,
|
|
|
|
)
|
|
|
|
|
|
|
|
proc new*(
|
|
|
|
T: type BatchVerifier, rng: ref HmacDrbgContext,
|
2024-12-11 11:12:56 +00:00
|
|
|
taskpool: Taskpool): ref BatchVerifier =
|
2023-08-03 08:36:45 +00:00
|
|
|
(ref BatchVerifier)(
|
|
|
|
sigVerifCache: BatchedBLSVerifierCache.init(taskpool),
|
|
|
|
rng: rng,
|
|
|
|
taskpool: taskpool,
|
|
|
|
)
|
|
|
|
|
2021-01-25 18:45:48 +00:00
|
|
|
func `$`*(s: SignatureSet): string =
|
|
|
|
"(pubkey: 0x" & s.pubkey.toHex() &
|
|
|
|
", signing_root: 0x" & s.message.toHex() &
|
|
|
|
", signature: 0x" & s.signature.toHex() & ')'
|
|
|
|
|
|
|
|
# Important:
|
|
|
|
# - Due to lazy loading, when we do crypto verification
|
|
|
|
# and only then state-transition verification,
|
|
|
|
# there is no guarantee that pubkeys and signatures received are valid
|
|
|
|
# unlike when Nimbus did eager loading which ensured they were correct beforehand
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
func init(T: type SignatureSet,
|
|
|
|
pubkey: CookedPubKey, signing_root: Eth2Digest,
|
|
|
|
signature: CookedSig): T =
|
2021-01-25 18:45:48 +00:00
|
|
|
## Add a new signature set triplet (pubkey, message, signature)
|
|
|
|
## to a collection of signature sets for batch verification.
|
2021-12-29 14:28:40 +00:00
|
|
|
(
|
2021-06-01 11:13:40 +00:00
|
|
|
blscurve.PublicKey(pubkey),
|
2021-12-09 12:56:54 +00:00
|
|
|
signing_root.data,
|
2021-04-26 20:39:44 +00:00
|
|
|
blscurve.Signature(signature)
|
2021-12-29 14:28:40 +00:00
|
|
|
)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2023-02-02 19:05:30 +00:00
|
|
|
func aggregateAttesters(
|
2021-12-09 12:56:54 +00:00
|
|
|
validatorIndices: openArray[uint64|ValidatorIndex],
|
2021-06-10 07:37:02 +00:00
|
|
|
validatorKeys: auto,
|
2021-06-01 11:13:40 +00:00
|
|
|
): Result[CookedPubKey, cstring] =
|
|
|
|
if validatorIndices.len == 0:
|
|
|
|
# Aggregation spec requires non-empty collection
|
|
|
|
# - https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04
|
2023-05-09 18:16:49 +00:00
|
|
|
# Consensus specs require at least one attesting index in attestation
|
2024-10-09 06:37:35 +00:00
|
|
|
# - https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/phase0/beacon-chain.md#is_valid_indexed_attestation
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("aggregateAttesters: no attesting indices")
|
|
|
|
|
2021-06-10 07:37:02 +00:00
|
|
|
let
|
2023-01-11 12:29:21 +00:00
|
|
|
firstKey = validatorKeys.load(validatorIndices[0]).valueOr:
|
|
|
|
return err("aggregateAttesters: invalid attesting index")
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2022-02-16 22:24:44 +00:00
|
|
|
var attestersAgg{.noinit.}: AggregatePublicKey
|
2021-06-10 07:37:02 +00:00
|
|
|
|
2023-01-11 12:29:21 +00:00
|
|
|
attestersAgg.init(firstKey)
|
2021-06-01 11:13:40 +00:00
|
|
|
for i in 1 ..< validatorIndices.len:
|
2023-01-11 12:29:21 +00:00
|
|
|
let key = validatorKeys.load(validatorIndices[i]).valueOr:
|
2021-06-01 11:13:40 +00:00
|
|
|
return err("aggregateAttesters: invalid attesting index")
|
2023-01-11 12:29:21 +00:00
|
|
|
attestersAgg.aggregate(key)
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(finish(attestersAgg))
|
2021-04-02 14:36:43 +00:00
|
|
|
|
2023-02-02 19:05:30 +00:00
|
|
|
func aggregateAttesters(
|
2021-12-09 12:56:54 +00:00
|
|
|
validatorIndices: openArray[uint64|ValidatorIndex],
|
|
|
|
bits: auto,
|
2021-06-10 07:37:02 +00:00
|
|
|
validatorKeys: auto,
|
2021-12-09 12:56:54 +00:00
|
|
|
): Result[CookedPubKey, cstring] =
|
|
|
|
if validatorIndices.len == 0:
|
|
|
|
# Aggregation spec requires non-empty collection
|
|
|
|
# - https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04
|
2023-05-09 18:16:49 +00:00
|
|
|
# Consensus specs require at least one attesting index in attestation
|
2024-01-20 11:19:47 +00:00
|
|
|
# - https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.6/specs/phase0/beacon-chain.md#is_valid_indexed_attestation
|
2021-12-09 12:56:54 +00:00
|
|
|
return err("aggregateAttesters: no attesting indices")
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2022-02-16 22:24:44 +00:00
|
|
|
var attestersAgg{.noinit.}: AggregatePublicKey
|
2021-06-11 17:51:46 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
var inited = false
|
2021-12-09 12:56:54 +00:00
|
|
|
for i in 0..<bits.len:
|
|
|
|
if bits[i]:
|
2023-01-11 12:29:21 +00:00
|
|
|
let key = validatorKeys.load(validatorIndices[i]).valueOr:
|
2021-12-09 12:56:54 +00:00
|
|
|
return err("aggregateAttesters: invalid attesting index")
|
|
|
|
if inited:
|
2023-01-11 12:29:21 +00:00
|
|
|
attestersAgg.aggregate(key)
|
2021-12-09 12:56:54 +00:00
|
|
|
else:
|
2023-01-11 12:29:21 +00:00
|
|
|
attestersAgg = AggregatePublicKey.init(key)
|
2021-12-09 12:56:54 +00:00
|
|
|
inited = true
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
if not inited:
|
2021-12-09 12:56:54 +00:00
|
|
|
return err("aggregateAttesters:no attesting indices")
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-12-09 12:56:54 +00:00
|
|
|
ok(finish(attestersAgg))
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-04-02 14:36:43 +00:00
|
|
|
# Public API
|
|
|
|
# ------------------------------------------------------
|
|
|
|
|
2021-12-09 12:56:54 +00:00
|
|
|
# See also: verify_slot_signature
|
2023-02-02 19:05:30 +00:00
|
|
|
func slot_signature_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_slot_signing_root(
|
|
|
|
fork, genesis_validators_root, slot)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
|
|
|
# See also: verify_epoch_signature
|
2023-02-02 19:05:30 +00:00
|
|
|
func epoch_signature_set*(
|
2021-12-29 14:28:40 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest, epoch: Epoch,
|
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_epoch_signing_root(
|
|
|
|
fork, genesis_validators_root, epoch)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
|
|
|
# See also: verify_block_signature
|
2023-02-02 19:05:30 +00:00
|
|
|
func block_signature_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
2022-01-30 16:51:04 +00:00
|
|
|
blck: Eth2Digest | SomeForkyBeaconBlock | BeaconBlockHeader,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_block_signing_root(
|
|
|
|
fork, genesis_validators_root, slot, blck)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
|
|
|
# See also: verify_aggregate_and_proof_signature
|
2023-02-02 19:05:30 +00:00
|
|
|
func aggregate_and_proof_signature_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
2024-12-23 16:10:23 +00:00
|
|
|
aggregate_and_proof: phase0.AggregateAndProof | electra.AggregateAndProof,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_aggregate_and_proof_signing_root(
|
|
|
|
fork, genesis_validators_root, aggregate_and_proof)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
|
|
|
# See also: verify_attestation_signature
|
2023-02-02 19:05:30 +00:00
|
|
|
func attestation_signature_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
attestation_data: AttestationData,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_attestation_signing_root(
|
|
|
|
fork, genesis_validators_root, attestation_data)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
|
|
|
# See also: verify_voluntary_exit_signature
|
2023-02-02 19:05:30 +00:00
|
|
|
func voluntary_exit_signature_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
voluntary_exit: VoluntaryExit,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_voluntary_exit_signing_root(
|
|
|
|
fork, genesis_validators_root, voluntary_exit)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
|
|
|
# See also: verify_sync_committee_message_signature
|
2023-02-02 19:05:30 +00:00
|
|
|
func sync_committee_message_signature_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
slot: Slot, block_root: Eth2Digest,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_sync_committee_message_signing_root(
|
|
|
|
fork, genesis_validators_root, slot, block_root)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
|
|
|
# See also: verify_sync_committee_selection_proof
|
2023-02-02 19:05:30 +00:00
|
|
|
func sync_committee_selection_proof_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
2022-05-10 10:03:40 +00:00
|
|
|
slot: Slot, subcommittee_index: SyncSubcommitteeIndex,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_sync_committee_selection_proof_signing_root(
|
|
|
|
fork, genesis_validators_root, slot, subcommittee_index)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-12-09 12:56:54 +00:00
|
|
|
|
2023-02-02 19:05:30 +00:00
|
|
|
func contribution_and_proof_signature_set*(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
msg: ContributionAndProof,
|
2021-12-29 14:28:40 +00:00
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
2021-12-09 12:56:54 +00:00
|
|
|
let signing_root = compute_contribution_and_proof_signing_root(
|
|
|
|
fork, genesis_validators_root, msg)
|
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
2021-04-26 20:39:44 +00:00
|
|
|
|
2023-02-17 13:35:12 +00:00
|
|
|
func bls_to_execution_change_signature_set*(
|
|
|
|
genesisFork: Fork, genesis_validators_root: Eth2Digest,
|
|
|
|
msg: BLSToExecutionChange,
|
|
|
|
pubkey: CookedPubKey, signature: CookedSig): SignatureSet =
|
|
|
|
let signing_root = compute_bls_to_execution_change_signing_root(
|
|
|
|
genesisFork, genesis_validators_root, msg)
|
|
|
|
|
|
|
|
SignatureSet.init(pubkey, signing_root, signature)
|
|
|
|
|
2024-10-30 05:38:53 +00:00
|
|
|
proc collectProposerSignatureSet*(
|
|
|
|
sigs: var seq[SignatureSet],
|
|
|
|
blocks: openArray[ForkedSignedBeaconBlock],
|
|
|
|
validatorKeys: openArray[ImmutableValidatorData2],
|
|
|
|
fork: Fork,
|
|
|
|
genesis_validators_root: Eth2Digest
|
|
|
|
): Result[void, string] =
|
|
|
|
mixin load
|
|
|
|
|
|
|
|
for forkedBlock in blocks:
|
|
|
|
let item =
|
|
|
|
withBlck(forkedBlock):
|
|
|
|
let
|
|
|
|
proposerKey =
|
|
|
|
validatorKeys.load(forkyBlck.message.proposer_index).valueOr:
|
|
|
|
let msg = "collectProposerSignatureSet: " &
|
|
|
|
"invalid proposer index (" &
|
|
|
|
$forkyBlck.message.proposer_index & ")"
|
|
|
|
return err(msg)
|
|
|
|
signature =
|
|
|
|
forkyBlck.signature.load().valueOr:
|
|
|
|
let msg = "collectProposerSignatureSet: " &
|
|
|
|
"cannot load signature (" &
|
|
|
|
$ forkyBlck.signature & ")"
|
|
|
|
return err(msg)
|
|
|
|
block_signature_set(
|
|
|
|
fork, genesis_validators_root,
|
|
|
|
forkyBlck.message.slot, forkyBlck.root,
|
|
|
|
proposerKey, signature)
|
|
|
|
sigs.add(item)
|
|
|
|
ok()
|
|
|
|
|
2023-02-17 13:35:12 +00:00
|
|
|
proc collectSignatureSets*(
|
2021-01-25 18:45:48 +00:00
|
|
|
sigs: var seq[SignatureSet],
|
2021-11-05 07:34:34 +00:00
|
|
|
signed_block: ForkySignedBeaconBlock,
|
2023-12-01 14:44:45 +00:00
|
|
|
validatorKeys: openArray[ImmutableValidatorData2],
|
2021-06-11 17:51:46 +00:00
|
|
|
state: ForkedHashedBeaconState,
|
2023-02-17 13:35:12 +00:00
|
|
|
genesis_fork: Fork,
|
fix EIP-7044 implementation when using batch verification (#5953)
In #5120, EIP-7044 support got added to the state transition function to
force `CAPELLA_FORK_VERSION` to be used when validiting `VoluntaryExit`
messages, irrespective of their `epoch`.
In #5637, similar logic was added when batch verifying BLS signatures,
which is used during gossip validation (libp2p gossipsub, and req/resp).
However, that logic did not match the one introduced in #5120, and only
uses `CAPELLA_FORK_VERSION` when a `VoluntaryExit`'s `epoch` was set to
a value `>= CAPELLA_FORK_EPOCH`. Otherwise, `BELLATRIX_FORK_VERSION`
would still be used when validating `VoluntaryExit`, e.g., with `epoch`
set to `0`, as is the case in this Holesky block:
- https://holesky.beaconcha.in/slot/1076985#voluntary-exits
Extracting the correct logic from #5120 into a function, and reusing it
when verifying BLS signatures fixes this issue, and also leverages the
exhaustive EF test suite that covers the (correct) #5120 logic.
This fix only affects networks that have EIP-7044 applied (post-Deneb).
Without the fix, Deneb blocks with a `VoluntaryExit` with `epoch` set to
`< CAPELLA_FORK_EPOCH` incorrectly fail to validate despite being valid.
Incorrect blocks that contain a malicious `VoluntaryExit` with `epoch`
set to `< CAPELLA_FORK_EPOCH` and signed using `BELLATRIX_FORK_VERSION`
_would_ pass the BLS verification stage, but subsequently fail the state
transition logic. Such blocks would still correctly be labeled invalid.
2024-02-25 14:25:26 +00:00
|
|
|
capella_fork_version: Version,
|
2021-06-01 11:13:40 +00:00
|
|
|
cache: var StateCache): Result[void, cstring] =
|
2021-12-09 12:56:54 +00:00
|
|
|
## Collect all signature verifications that process_block would normally do
|
|
|
|
## except deposits, in one go.
|
|
|
|
##
|
2021-01-25 18:45:48 +00:00
|
|
|
## This includes
|
|
|
|
## - Block proposer
|
|
|
|
## - Randao Reaveal
|
|
|
|
## - Proposer slashings
|
|
|
|
## - Attester slashings
|
|
|
|
## - Attestations
|
|
|
|
## - VoluntaryExits
|
2023-02-17 13:35:12 +00:00
|
|
|
## - SyncCommittee (Altair+)
|
|
|
|
## - BLS to execution changes (Capella+)
|
2021-01-25 18:45:48 +00:00
|
|
|
##
|
2021-12-09 12:56:54 +00:00
|
|
|
## We do not include deposits as they can be invalid while still leaving the
|
|
|
|
## block valid
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# Metadata
|
|
|
|
# ----------------------------------------------------
|
2021-06-10 07:37:02 +00:00
|
|
|
mixin load
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2021-12-09 12:56:54 +00:00
|
|
|
let
|
|
|
|
fork = getStateField(state, fork)
|
|
|
|
genesis_validators_root = getStateField(state, genesis_validators_root)
|
2021-01-25 18:45:48 +00:00
|
|
|
proposer_index = signed_block.message.proposer_index
|
2023-01-11 12:29:21 +00:00
|
|
|
proposer_key = validatorKeys.load(proposer_index).valueOr:
|
|
|
|
return err("collectSignatureSets: invalid proposer index")
|
|
|
|
epoch = signed_block.message.slot.epoch()
|
2021-01-25 18:45:48 +00:00
|
|
|
|
2023-02-17 13:35:12 +00:00
|
|
|
doAssert genesis_fork.previous_version == genesis_fork.current_version
|
|
|
|
|
2021-01-25 18:45:48 +00:00
|
|
|
# 1. Block proposer
|
|
|
|
# ----------------------------------------------------
|
2021-12-29 14:28:40 +00:00
|
|
|
sigs.add block_signature_set(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork, genesis_validators_root,
|
|
|
|
signed_block.message.slot, signed_block.root,
|
2023-01-11 12:29:21 +00:00
|
|
|
proposer_key, signed_block.signature.load.valueOr do:
|
|
|
|
return err("collectSignatureSets: cannot load signature"))
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 2. Randao Reveal
|
|
|
|
# ----------------------------------------------------
|
2021-12-29 14:28:40 +00:00
|
|
|
sigs.add epoch_signature_set(
|
2023-01-11 12:29:21 +00:00
|
|
|
fork, genesis_validators_root, epoch, proposer_key,
|
|
|
|
signed_block.message.body.randao_reveal.load().valueOr do:
|
|
|
|
return err("collectSignatureSets: cannot load randao"))
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 3. Proposer slashings
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_PROPOSER_SLASHINGS proposer slashings.
|
|
|
|
for i in 0 ..< signed_block.message.body.proposer_slashings.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
|
|
|
|
|
|
|
# Alias
|
|
|
|
template slashing: untyped = signed_block.message.body.proposer_slashings[i]
|
|
|
|
|
|
|
|
# Proposed block 1
|
|
|
|
block:
|
2021-06-10 07:37:02 +00:00
|
|
|
let
|
2021-12-09 12:56:54 +00:00
|
|
|
header = slashing.signed_header_1
|
2023-01-11 12:29:21 +00:00
|
|
|
key = validatorKeys.load(header.message.proposer_index).valueOr:
|
|
|
|
return err("collectSignatureSets: invalid slashing proposer index 1")
|
2021-06-01 11:13:40 +00:00
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
sigs.add block_signature_set(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork, genesis_validators_root, header.message.slot, header.message,
|
2023-01-11 12:29:21 +00:00
|
|
|
key, header.signature.load().valueOr do:
|
|
|
|
return err(
|
|
|
|
"collectSignatureSets: cannot load proposer slashing 1 signature"))
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# Conflicting block 2
|
|
|
|
block:
|
2021-06-10 07:37:02 +00:00
|
|
|
let
|
2021-12-09 12:56:54 +00:00
|
|
|
header = slashing.signed_header_2
|
2023-01-11 12:29:21 +00:00
|
|
|
key = validatorKeys.load(header.message.proposer_index).valueOr:
|
|
|
|
return err("collectSignatureSets: invalid slashing proposer index 2")
|
2021-12-09 12:56:54 +00:00
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
sigs.add block_signature_set(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork, genesis_validators_root, header.message.slot, header.message,
|
2023-01-11 12:29:21 +00:00
|
|
|
key, header.signature.load().valueOr do:
|
|
|
|
return err(
|
|
|
|
"collectSignatureSets: cannot load proposer slashing 2 signature"))
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 4. Attester slashings
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_ATTESTER_SLASHINGS attester slashings.
|
|
|
|
for i in 0 ..< signed_block.message.body.attester_slashings.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
|
|
|
|
|
|
|
# Alias
|
|
|
|
template slashing: untyped = signed_block.message.body.attester_slashings[i]
|
|
|
|
|
|
|
|
# Attestation 1
|
2021-12-09 12:56:54 +00:00
|
|
|
block:
|
|
|
|
let
|
|
|
|
key = ? aggregateAttesters(
|
|
|
|
slashing.attestation_1.attesting_indices.asSeq(), validatorKeys)
|
2023-01-11 12:29:21 +00:00
|
|
|
sig = slashing.attestation_1.signature.load().valueOr:
|
|
|
|
return err("Invalid attestation slashing signature 1")
|
2021-12-29 14:28:40 +00:00
|
|
|
sigs.add attestation_signature_set(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork, genesis_validators_root, slashing.attestation_1.data, key, sig)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# Conflicting attestation 2
|
2021-12-09 12:56:54 +00:00
|
|
|
block:
|
|
|
|
let
|
|
|
|
key = ? aggregateAttesters(
|
|
|
|
slashing.attestation_2.attesting_indices.asSeq(), validatorKeys)
|
2023-01-11 12:29:21 +00:00
|
|
|
sig = slashing.attestation_2.signature.load().valueOr:
|
|
|
|
return err("Invalid attestation slashing signature 2")
|
2021-12-29 14:28:40 +00:00
|
|
|
sigs.add attestation_signature_set(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork, genesis_validators_root, slashing.attestation_2.data, key, sig)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 5. Attestations
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_ATTESTATIONS attestations.
|
|
|
|
for i in 0 ..< signed_block.message.body.attestations.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
2021-12-09 12:56:54 +00:00
|
|
|
template attestation: untyped = signed_block.message.body.attestations[i]
|
|
|
|
|
2024-04-28 00:52:14 +00:00
|
|
|
when typeof(signed_block).kind < ConsensusFork.Electra:
|
|
|
|
let
|
|
|
|
key = ? aggregateAttesters(
|
|
|
|
get_attesting_indices(
|
|
|
|
state, attestation.data, attestation.aggregation_bits, cache),
|
|
|
|
validatorKeys)
|
|
|
|
else:
|
|
|
|
let
|
|
|
|
key = ? aggregateAttesters(
|
|
|
|
get_attesting_indices(
|
|
|
|
state, attestation.data, attestation.aggregation_bits,
|
|
|
|
attestation.committee_bits, cache),
|
|
|
|
validatorKeys)
|
2021-12-09 12:56:54 +00:00
|
|
|
let
|
2023-01-11 12:29:21 +00:00
|
|
|
sig = attestation.signature.load().valueOr:
|
|
|
|
return err("Invalid attestation signature")
|
2021-12-09 12:56:54 +00:00
|
|
|
|
2021-12-29 14:28:40 +00:00
|
|
|
sigs.add attestation_signature_set(
|
2021-12-09 12:56:54 +00:00
|
|
|
fork, genesis_validators_root, attestation.data, key, sig)
|
2021-01-25 18:45:48 +00:00
|
|
|
|
|
|
|
# 6. VoluntaryExits
|
|
|
|
# ----------------------------------------------------
|
|
|
|
# Denial-of-service:
|
|
|
|
# SSZ deserialization guarantees that blocks received from random sources
|
|
|
|
# including peer or RPC
|
|
|
|
# have at most MAX_VOLUNTARY_EXITS voluntary exits.
|
fix EIP-7044 implementation when using batch verification (#5953)
In #5120, EIP-7044 support got added to the state transition function to
force `CAPELLA_FORK_VERSION` to be used when validiting `VoluntaryExit`
messages, irrespective of their `epoch`.
In #5637, similar logic was added when batch verifying BLS signatures,
which is used during gossip validation (libp2p gossipsub, and req/resp).
However, that logic did not match the one introduced in #5120, and only
uses `CAPELLA_FORK_VERSION` when a `VoluntaryExit`'s `epoch` was set to
a value `>= CAPELLA_FORK_EPOCH`. Otherwise, `BELLATRIX_FORK_VERSION`
would still be used when validating `VoluntaryExit`, e.g., with `epoch`
set to `0`, as is the case in this Holesky block:
- https://holesky.beaconcha.in/slot/1076985#voluntary-exits
Extracting the correct logic from #5120 into a function, and reusing it
when verifying BLS signatures fixes this issue, and also leverages the
exhaustive EF test suite that covers the (correct) #5120 logic.
This fix only affects networks that have EIP-7044 applied (post-Deneb).
Without the fix, Deneb blocks with a `VoluntaryExit` with `epoch` set to
`< CAPELLA_FORK_EPOCH` incorrectly fail to validate despite being valid.
Incorrect blocks that contain a malicious `VoluntaryExit` with `epoch`
set to `< CAPELLA_FORK_EPOCH` and signed using `BELLATRIX_FORK_VERSION`
_would_ pass the BLS verification stage, but subsequently fail the state
transition logic. Such blocks would still correctly be labeled invalid.
2024-02-25 14:25:26 +00:00
|
|
|
if signed_block.message.body.voluntary_exits.len > 0:
|
|
|
|
let voluntary_exit_fork = withConsensusFork(state.kind):
|
|
|
|
consensusFork.voluntary_exit_signature_fork(fork, capella_fork_version)
|
|
|
|
for i in 0 ..< signed_block.message.body.voluntary_exits.len:
|
|
|
|
# don't use "items" for iterating over large type
|
|
|
|
# due to https://github.com/nim-lang/Nim/issues/14421
|
|
|
|
# fixed in 1.4.2
|
|
|
|
template volex: untyped = signed_block.message.body.voluntary_exits[i]
|
|
|
|
let key = validatorKeys.load(volex.message.validator_index).valueOr:
|
|
|
|
return err("collectSignatureSets: invalid voluntary exit")
|
|
|
|
|
|
|
|
sigs.add voluntary_exit_signature_set(
|
|
|
|
# https://eips.ethereum.org/EIPS/eip-7044
|
2024-03-14 06:26:36 +00:00
|
|
|
# https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/deneb/beacon-chain.md#modified-process_voluntary_exit
|
fix EIP-7044 implementation when using batch verification (#5953)
In #5120, EIP-7044 support got added to the state transition function to
force `CAPELLA_FORK_VERSION` to be used when validiting `VoluntaryExit`
messages, irrespective of their `epoch`.
In #5637, similar logic was added when batch verifying BLS signatures,
which is used during gossip validation (libp2p gossipsub, and req/resp).
However, that logic did not match the one introduced in #5120, and only
uses `CAPELLA_FORK_VERSION` when a `VoluntaryExit`'s `epoch` was set to
a value `>= CAPELLA_FORK_EPOCH`. Otherwise, `BELLATRIX_FORK_VERSION`
would still be used when validating `VoluntaryExit`, e.g., with `epoch`
set to `0`, as is the case in this Holesky block:
- https://holesky.beaconcha.in/slot/1076985#voluntary-exits
Extracting the correct logic from #5120 into a function, and reusing it
when verifying BLS signatures fixes this issue, and also leverages the
exhaustive EF test suite that covers the (correct) #5120 logic.
This fix only affects networks that have EIP-7044 applied (post-Deneb).
Without the fix, Deneb blocks with a `VoluntaryExit` with `epoch` set to
`< CAPELLA_FORK_EPOCH` incorrectly fail to validate despite being valid.
Incorrect blocks that contain a malicious `VoluntaryExit` with `epoch`
set to `< CAPELLA_FORK_EPOCH` and signed using `BELLATRIX_FORK_VERSION`
_would_ pass the BLS verification stage, but subsequently fail the state
transition logic. Such blocks would still correctly be labeled invalid.
2024-02-25 14:25:26 +00:00
|
|
|
voluntary_exit_fork, genesis_validators_root, volex.message, key,
|
|
|
|
volex.signature.load.valueOr do:
|
|
|
|
return err(
|
|
|
|
"collectSignatureSets: cannot load voluntary exit signature"))
|
2021-01-25 18:45:48 +00:00
|
|
|
|
Speed up altair block processing 2x (#3115)
* Speed up altair block processing >2x
Like #3089, this PR drastially speeds up historical REST queries and
other long state replays.
* cache sync committee validator indices
* use ~80mb less memory for validator pubkey mappings
* batch-verify sync aggregate signature (fixes #2985)
* document sync committee hack with head block vs sync message block
* add batch signature verification failure tests
Before:
```
../env.sh nim c -d:release -r ncli_db --db:mainnet_0/db bench --start-slot:-1000
All time are ms
Average, StdDev, Min, Max, Samples, Test
Validation is turned off meaning that no BLS operations are performed
5830.675, 0.000, 5830.675, 5830.675, 1, Initialize DB
0.481, 1.878, 0.215, 59.167, 981, Load block from database
8422.566, 0.000, 8422.566, 8422.566, 1, Load state from database
6.996, 1.678, 0.042, 14.385, 969, Advance slot, non-epoch
93.217, 8.318, 84.192, 122.209, 32, Advance slot, epoch
20.513, 23.665, 11.510, 201.561, 981, Apply block, no slot processing
0.000, 0.000, 0.000, 0.000, 0, Database load
0.000, 0.000, 0.000, 0.000, 0, Database store
```
After:
```
7081.422, 0.000, 7081.422, 7081.422, 1, Initialize DB
0.553, 2.122, 0.175, 66.692, 981, Load block from database
5439.446, 0.000, 5439.446, 5439.446, 1, Load state from database
6.829, 1.575, 0.043, 12.156, 969, Advance slot, non-epoch
94.716, 2.749, 88.395, 100.026, 32, Advance slot, epoch
11.636, 23.766, 4.889, 205.250, 981, Apply block, no slot processing
0.000, 0.000, 0.000, 0.000, 0, Database load
0.000, 0.000, 0.000, 0.000, 0, Database store
```
* add comment
2021-11-24 12:43:50 +00:00
|
|
|
block:
|
2022-09-07 16:45:46 +00:00
|
|
|
when signed_block is phase0.SignedBeaconBlock:
|
|
|
|
discard
|
|
|
|
else:
|
|
|
|
# 7. SyncAggregate
|
|
|
|
# ----------------------------------------------------
|
|
|
|
withState(state):
|
2023-03-11 00:35:52 +00:00
|
|
|
when consensusFork >= ConsensusFork.Altair:
|
2023-05-17 04:55:55 +00:00
|
|
|
if signed_block.message.body.sync_aggregate.sync_committee_bits.isZeros:
|
2022-09-07 16:45:46 +00:00
|
|
|
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
|
|
|
|
current_sync_committee =
|
2022-09-10 06:12:07 +00:00
|
|
|
forkyState.data.get_sync_committee_cache(cache).current_sync_committee
|
|
|
|
previous_slot = max(forkyState.data.slot, Slot(1)) - 1
|
|
|
|
beacon_block_root = get_block_root_at_slot(forkyState.data, previous_slot)
|
2022-09-07 16:45:46 +00:00
|
|
|
pubkey = ? aggregateAttesters(
|
|
|
|
current_sync_committee,
|
|
|
|
signed_block.message.body.sync_aggregate.sync_committee_bits,
|
|
|
|
validatorKeys)
|
|
|
|
|
|
|
|
sigs.add sync_committee_message_signature_set(
|
|
|
|
fork, genesis_validators_root, previous_slot, beacon_block_root,
|
|
|
|
pubkey,
|
2023-01-11 12:29:21 +00:00
|
|
|
signed_block.message.body.sync_aggregate.sync_committee_signature.load().valueOr do:
|
|
|
|
return err("collectSignatureSets: cannot load signature"))
|
Speed up altair block processing 2x (#3115)
* Speed up altair block processing >2x
Like #3089, this PR drastially speeds up historical REST queries and
other long state replays.
* cache sync committee validator indices
* use ~80mb less memory for validator pubkey mappings
* batch-verify sync aggregate signature (fixes #2985)
* document sync committee hack with head block vs sync message block
* add batch signature verification failure tests
Before:
```
../env.sh nim c -d:release -r ncli_db --db:mainnet_0/db bench --start-slot:-1000
All time are ms
Average, StdDev, Min, Max, Samples, Test
Validation is turned off meaning that no BLS operations are performed
5830.675, 0.000, 5830.675, 5830.675, 1, Initialize DB
0.481, 1.878, 0.215, 59.167, 981, Load block from database
8422.566, 0.000, 8422.566, 8422.566, 1, Load state from database
6.996, 1.678, 0.042, 14.385, 969, Advance slot, non-epoch
93.217, 8.318, 84.192, 122.209, 32, Advance slot, epoch
20.513, 23.665, 11.510, 201.561, 981, Apply block, no slot processing
0.000, 0.000, 0.000, 0.000, 0, Database load
0.000, 0.000, 0.000, 0.000, 0, Database store
```
After:
```
7081.422, 0.000, 7081.422, 7081.422, 1, Initialize DB
0.553, 2.122, 0.175, 66.692, 981, Load block from database
5439.446, 0.000, 5439.446, 5439.446, 1, Load state from database
6.829, 1.575, 0.043, 12.156, 969, Advance slot, non-epoch
94.716, 2.749, 88.395, 100.026, 32, Advance slot, epoch
11.636, 23.766, 4.889, 205.250, 981, Apply block, no slot processing
0.000, 0.000, 0.000, 0.000, 0, Database load
0.000, 0.000, 0.000, 0.000, 0, Database store
```
* add comment
2021-11-24 12:43:50 +00:00
|
|
|
|
2023-02-17 13:35:12 +00:00
|
|
|
block:
|
|
|
|
# 8. BLS to execution changes
|
2023-09-27 15:10:28 +00:00
|
|
|
when typeof(signed_block).kind >= ConsensusFork.Capella:
|
2023-02-17 13:35:12 +00:00
|
|
|
withState(state):
|
2023-03-11 00:35:52 +00:00
|
|
|
when consensusFork >= ConsensusFork.Capella:
|
2023-02-17 13:35:12 +00:00
|
|
|
for bls_change in signed_block.message.body.bls_to_execution_changes:
|
|
|
|
let sig = bls_change.signature.load.valueOr:
|
|
|
|
return err("collectSignatureSets: cannot load BLS to execution change signature")
|
|
|
|
|
|
|
|
# Otherwise, expensive loadWithCache can be spammed with irrelevant pubkeys
|
|
|
|
? check_bls_to_execution_change(
|
|
|
|
genesis_fork, forkyState.data, bls_change, {skipBlsValidation})
|
|
|
|
|
|
|
|
let validator_pubkey =
|
|
|
|
bls_change.message.from_bls_pubkey.loadWithCache.valueOr:
|
|
|
|
return err("collectSignatureSets: cannot load BLS to execution change pubkey")
|
|
|
|
|
|
|
|
sigs.add bls_to_execution_change_signature_set(
|
|
|
|
genesis_fork, genesis_validators_root, bls_change.message,
|
|
|
|
validator_pubkey, sig)
|
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
ok()
|
2021-12-06 09:49:01 +00:00
|
|
|
|
|
|
|
proc batchVerify*(verifier: var BatchVerifier, sigs: openArray[SignatureSet]): bool =
|
2022-06-21 08:29:16 +00:00
|
|
|
let bytes = verifier.rng[].generate(array[32, byte])
|
2023-08-03 08:36:45 +00:00
|
|
|
|
|
|
|
verifier.taskpool.batchVerify(verifier.sigVerifCache, sigs, bytes)
|