mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-22 11:18:25 +00:00
electra attestation updates (#6295)
* electra attestation updates In Electra, we have two attestation formats: on-chain and on-network - the former combines all committees of a slot in a single committee bit list. This PR makes a number of cleanups to move towards fixing this - attestation packing however still needs to be fixed as it currently creates attestations with a single committee only which is very inefficient. * more attestations in the blocks * signing and aggregation fixes * tool fix * test, import
This commit is contained in:
parent
826bf4c3ee
commit
045c4cf185
@ -5,6 +5,11 @@ AllTests-mainnet
|
||||
+ ancestorSlot OK
|
||||
```
|
||||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||
## Attestation pool electra processing [Preset: mainnet]
|
||||
```diff
|
||||
+ Can add and retrieve simple electra attestations [Preset: mainnet] OK
|
||||
```
|
||||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||
## Attestation pool processing [Preset: mainnet]
|
||||
```diff
|
||||
+ Attestation from different branch [Preset: mainnet] OK
|
||||
@ -1020,4 +1025,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
|
||||
OK: 9/9 Fail: 0/9 Skip: 0/9
|
||||
|
||||
---TOTAL---
|
||||
OK: 685/690 Fail: 0/690 Skip: 5/690
|
||||
OK: 686/691 Fail: 0/691 Skip: 5/691
|
||||
|
@ -42,31 +42,25 @@ type
|
||||
## be further combined.
|
||||
aggregation_bits: CVBType
|
||||
aggregate_signature: AggregateSignature
|
||||
|
||||
Phase0Validation = Validation[CommitteeValidatorsBits]
|
||||
ElectraValidation = Validation[ElectraCommitteeValidatorsBits]
|
||||
|
||||
Phase0AttestationEntry = object
|
||||
AttestationEntry[CVBType] = object
|
||||
## Each entry holds the known signatures for a particular, distinct vote
|
||||
## For electra+, the data has been changed to hold the committee index
|
||||
data: AttestationData
|
||||
committee_len: int
|
||||
singles: Table[int, CookedSig] ## \
|
||||
## On the attestation subnets, only attestations with a single vote are
|
||||
## allowed - these can be collected separately to top up aggregates with -
|
||||
## here we collect them by mapping index in committee to a vote
|
||||
aggregates: seq[Phase0Validation]
|
||||
aggregates: seq[Validation[CVBType]]
|
||||
|
||||
ElectraAttestationEntry = object
|
||||
## Each entry holds the known signatures for a particular, distinct vote
|
||||
data: AttestationData
|
||||
committee_bits: AttestationCommitteeBits
|
||||
committee_len: int
|
||||
singles: Table[int, CookedSig] ## \
|
||||
## On the attestation subnets, only attestations with a single vote are
|
||||
## allowed - these can be collected separately to top up aggregates with -
|
||||
## here we collect them by mapping index in committee to a vote
|
||||
aggregates: seq[ElectraValidation]
|
||||
Phase0AttestationEntry = AttestationEntry[CommitteeValidatorsBits]
|
||||
ElectraAttestationEntry = AttestationEntry[ElectraCommitteeValidatorsBits]
|
||||
|
||||
AttestationTable[AEType] = Table[Eth2Digest, AEType]
|
||||
AttestationTable[CVBType] = Table[Eth2Digest, AttestationEntry[CVBType]]
|
||||
## Depending on the world view of the various validators, they may have
|
||||
## voted on different states - this map keeps track of each vote keyed by
|
||||
## getAttestationCandidateKey()
|
||||
@ -79,12 +73,12 @@ type
|
||||
## are tracked separately in the fork choice.
|
||||
|
||||
phase0Candidates: array[ATTESTATION_LOOKBACK.int,
|
||||
AttestationTable[Phase0AttestationEntry]] ## \
|
||||
AttestationTable[CommitteeValidatorsBits]] ## \
|
||||
## We keep one item per slot such that indexing matches slot number
|
||||
## together with startingSlot
|
||||
|
||||
electraCandidates: array[ATTESTATION_LOOKBACK.int,
|
||||
AttestationTable[ElectraAttestationEntry]] ## \
|
||||
AttestationTable[ElectraCommitteeValidatorsBits]] ## \
|
||||
## We keep one item per slot such that indexing matches slot number
|
||||
## together with startingSlot
|
||||
|
||||
@ -249,7 +243,7 @@ func oneIndex(
|
||||
return Opt.none(int)
|
||||
res
|
||||
|
||||
func toAttestation(entry: Phase0AttestationEntry, validation: Phase0Validation):
|
||||
func toAttestation(entry: AttestationEntry, validation: Phase0Validation):
|
||||
phase0.Attestation =
|
||||
phase0.Attestation(
|
||||
aggregation_bits: validation.aggregation_bits,
|
||||
@ -258,17 +252,24 @@ func toAttestation(entry: Phase0AttestationEntry, validation: Phase0Validation):
|
||||
)
|
||||
|
||||
func toElectraAttestation(
|
||||
entry: ElectraAttestationEntry, validation: ElectraValidation):
|
||||
entry: AttestationEntry, validation: ElectraValidation):
|
||||
electra.Attestation =
|
||||
var committee_bits: AttestationCommitteeBits
|
||||
committee_bits[int(entry.data.index)] = true
|
||||
|
||||
electra.Attestation(
|
||||
aggregation_bits: validation.aggregation_bits,
|
||||
committee_bits: entry.committee_bits,
|
||||
data: entry.data,
|
||||
committee_bits: committee_bits,
|
||||
data: AttestationData(
|
||||
slot: entry.data.slot,
|
||||
index: 0,
|
||||
beacon_block_root: entry.data.beacon_block_root,
|
||||
source: entry.data.source,
|
||||
target: entry.data.target),
|
||||
signature: validation.aggregate_signature.finish().toValidatorSig()
|
||||
)
|
||||
|
||||
func updateAggregates(
|
||||
entry: var (Phase0AttestationEntry | ElectraAttestationEntry)) =
|
||||
func updateAggregates(entry: var AttestationEntry) =
|
||||
# Upgrade the list of aggregates to ensure that there is at least one
|
||||
# aggregate (assuming there are singles) and all aggregates have all
|
||||
# singles incorporated
|
||||
@ -334,7 +335,7 @@ func updateAggregates(
|
||||
inc i
|
||||
|
||||
func covers(
|
||||
entry: Phase0AttestationEntry | ElectraAttestationEntry,
|
||||
entry: AttestationEntry,
|
||||
bits: CommitteeValidatorsBits | ElectraCommitteeValidatorsBits): bool =
|
||||
for i in 0..<entry.aggregates.len():
|
||||
if bits.isSubsetOf(entry.aggregates[i].aggregation_bits):
|
||||
@ -342,7 +343,7 @@ func covers(
|
||||
false
|
||||
|
||||
proc addAttestation(
|
||||
entry: var (Phase0AttestationEntry | ElectraAttestationEntry),
|
||||
entry: var AttestationEntry,
|
||||
attestation: phase0.Attestation | electra.Attestation,
|
||||
signature: CookedSig): bool =
|
||||
logScope:
|
||||
@ -374,13 +375,7 @@ proc addAttestation(
|
||||
entry.aggregates.keepItIf(
|
||||
not it.aggregation_bits.isSubsetOf(attestation.aggregation_bits))
|
||||
|
||||
# If it's not one of the correct ones, compile-time error anyway
|
||||
when entry is ElectraAttestationEntry:
|
||||
type ValidationType = ElectraValidation
|
||||
else:
|
||||
type ValidationType = Phase0Validation
|
||||
|
||||
entry.aggregates.add(ValidationType(
|
||||
entry.aggregates.add(Validation[typeof(entry).CVBType](
|
||||
aggregation_bits: attestation.aggregation_bits,
|
||||
aggregate_signature: AggregateSignature.init(signature)))
|
||||
|
||||
@ -431,19 +426,19 @@ proc addAttestation*(
|
||||
template committee_bits(_: phase0.Attestation): auto =
|
||||
const res = default(AttestationCommitteeBits)
|
||||
res
|
||||
let candidate_key = getAttestationCandidateKey(
|
||||
attestation.data, attestation.committee_bits)
|
||||
|
||||
# TODO withValue is an abomination but hard to use anything else too without
|
||||
# creating an unnecessary AttestationEntry on the hot path and avoiding
|
||||
# multiple lookups
|
||||
template addAttToPool(attCandidates: untyped, entry: untyped) =
|
||||
attCandidates[candidateIdx.get()].withValue(candidate_key, entry) do:
|
||||
let attestation_data_root = hash_tree_root(entry.data)
|
||||
|
||||
attCandidates[candidateIdx.get()].withValue(attestation_data_root, entry) do:
|
||||
if not addAttestation(entry[], attestation, signature):
|
||||
return
|
||||
do:
|
||||
if not addAttestation(
|
||||
attCandidates[candidateIdx.get()].mgetOrPut(candidate_key, entry),
|
||||
attCandidates[candidateIdx.get()].mgetOrPut(attestation_data_root, entry),
|
||||
attestation, signature):
|
||||
# Returns from overall function, not only template
|
||||
return
|
||||
@ -461,8 +456,16 @@ proc addAttestation*(
|
||||
pool.onPhase0AttestationAdded(attestation)
|
||||
|
||||
template addAttToPool(_: electra.Attestation) {.used.} =
|
||||
let
|
||||
committee_index = get_committee_index_one(attestation.committee_bits).expect("TODO")
|
||||
data = AttestationData(
|
||||
slot: attestation.data.slot,
|
||||
index: uint64 committee_index,
|
||||
beacon_block_root: attestation.data.beacon_block_root,
|
||||
source: attestation.data.source,
|
||||
target: attestation.data.target)
|
||||
let newAttEntry = ElectraAttestationEntry(
|
||||
data: attestation.data, committee_bits: attestation.committee_bits,
|
||||
data: data,
|
||||
committee_len: attestation.aggregation_bits.len)
|
||||
addAttToPool(pool.electraCandidates, newAttEntry)
|
||||
pool.addForkChoiceVotes(
|
||||
@ -560,7 +563,7 @@ iterator attestations*(
|
||||
|
||||
type
|
||||
AttestationCacheKey = (Slot, uint64)
|
||||
AttestationCache = Table[AttestationCacheKey, CommitteeValidatorsBits] ##\
|
||||
AttestationCache[CVBType] = Table[AttestationCacheKey, CVBType] ##\
|
||||
## Cache for quick lookup during beacon block construction of attestations
|
||||
## which have already been included, and therefore should be skipped.
|
||||
|
||||
@ -571,7 +574,7 @@ func getAttestationCacheKey(ad: AttestationData): AttestationCacheKey =
|
||||
|
||||
func add(
|
||||
attCache: var AttestationCache, data: AttestationData,
|
||||
aggregation_bits: CommitteeValidatorsBits) =
|
||||
aggregation_bits: CommitteeValidatorsBits | ElectraCommitteeValidatorsBits) =
|
||||
let key = data.getAttestationCacheKey()
|
||||
attCache.withValue(key, v) do:
|
||||
v[].incl(aggregation_bits)
|
||||
@ -611,7 +614,7 @@ func init(
|
||||
let committee = get_beacon_committee(
|
||||
state.data, slot, committee_index, cache)
|
||||
var
|
||||
validator_bits = CommitteeValidatorsBits.init(committee.len)
|
||||
validator_bits = typeof(result).B.init(committee.len)
|
||||
for index_in_committee, validator_index in committee:
|
||||
if participation_bitmap[validator_index] != 0:
|
||||
# If any flag got set, there was an attestation from this validator.
|
||||
@ -626,7 +629,7 @@ func init(
|
||||
|
||||
func score(
|
||||
attCache: var AttestationCache, data: AttestationData,
|
||||
aggregation_bits: CommitteeValidatorsBits): int =
|
||||
aggregation_bits: CommitteeValidatorsBits | ElectraCommitteeValidatorsBits): int =
|
||||
# The score of an attestation is loosely based on how many new votes it brings
|
||||
# to the state - a more accurate score function would also look at inclusion
|
||||
# distance and effective balance.
|
||||
@ -635,12 +638,12 @@ func score(
|
||||
key = data.getAttestationCacheKey()
|
||||
bitsScore = aggregation_bits.countOnes()
|
||||
|
||||
attCache.withValue(key, value):
|
||||
doAssert aggregation_bits.len() == value[].len(),
|
||||
attCache.withValue(key, xxx):
|
||||
doAssert aggregation_bits.len() == xxx[].len(),
|
||||
"check_attestation ensures committee length"
|
||||
|
||||
# How many votes were in the attestation minues the votes that are the same
|
||||
return bitsScore - aggregation_bits.countOverlap(value[])
|
||||
return bitsScore - aggregation_bits.countOverlap(xxx[])
|
||||
|
||||
# Not found in cache - fresh vote meaning all attestations count
|
||||
bitsScore
|
||||
@ -648,7 +651,7 @@ func score(
|
||||
proc check_attestation_compatible*(
|
||||
dag: ChainDAGRef,
|
||||
state: ForkyHashedBeaconState,
|
||||
attestation: SomeAttestation): Result[void, cstring] =
|
||||
attestation: SomeAttestation | electra.Attestation | electra.TrustedAttestation): Result[void, cstring] =
|
||||
let
|
||||
targetEpoch = attestation.data.target.epoch
|
||||
compatibleRoot = state.dependent_root(targetEpoch.get_previous_epoch)
|
||||
@ -685,7 +688,7 @@ proc getAttestationsForBlock*(pool: var AttestationPool,
|
||||
candidates: seq[tuple[
|
||||
score: int, slot: Slot, entry: ptr Phase0AttestationEntry,
|
||||
validation: int]]
|
||||
attCache = AttestationCache.init(state, cache)
|
||||
attCache = AttestationCache[CommitteeValidatorsBits].init(state, cache)
|
||||
|
||||
for i in 0..<ATTESTATION_LOOKBACK:
|
||||
if i > maxAttestationSlot: # Around genesis..
|
||||
@ -820,9 +823,6 @@ proc getAttestationsForBlock*(pool: var AttestationPool,
|
||||
proc getElectraAttestationsForBlock*(
|
||||
pool: var AttestationPool, state: electra.HashedBeaconState,
|
||||
cache: var StateCache): seq[electra.Attestation] =
|
||||
## Retrieve attestations that may be added to a new block at the slot of the
|
||||
## given state
|
||||
## https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/validator.md#attestations
|
||||
let newBlockSlot = state.data.slot.uint64
|
||||
|
||||
if newBlockSlot < MIN_ATTESTATION_INCLUSION_DELAY:
|
||||
@ -836,8 +836,9 @@ proc getElectraAttestationsForBlock*(
|
||||
|
||||
var
|
||||
candidates: seq[tuple[
|
||||
score: int, slot: Slot, entry: ptr ElectraAttestationEntry, validation: int]]
|
||||
attCache = AttestationCache.init(state, cache)
|
||||
score: int, slot: Slot, entry: ptr ElectraAttestationEntry,
|
||||
validation: int]]
|
||||
attCache = AttestationCache[ElectraCommitteeValidatorsBits].init(state, cache)
|
||||
|
||||
for i in 0..<ATTESTATION_LOOKBACK:
|
||||
if i > maxAttestationSlot: # Around genesis..
|
||||
@ -853,24 +854,25 @@ proc getElectraAttestationsForBlock*(
|
||||
break
|
||||
|
||||
for _, entry in pool.electraCandidates[candidateIdx.get()].mpairs():
|
||||
entry.updateAggregates() # TODO doesn't handle electra ones
|
||||
entry.updateAggregates()
|
||||
|
||||
for j in 0..<entry.aggregates.len():
|
||||
let attestation = entry.toElectraAttestation(entry.aggregates[j])
|
||||
|
||||
# Filter out attestations that were created with a different shuffling.
|
||||
# As we don't re-check signatures, this needs to be done separately
|
||||
#if not pool.dag.check_attestation_compatible(state, attestation).isOk():
|
||||
# continue
|
||||
if not pool.dag.check_attestation_compatible(state, attestation).isOk():
|
||||
continue
|
||||
|
||||
# Attestations are checked based on the state that we're adding the
|
||||
# attestation to - there might have been a fork between when we first
|
||||
# saw the attestation and the time that we added it
|
||||
if not check_attestation(
|
||||
state.data, attestation, {skipBlsValidation}, cache).isOk():
|
||||
state.data, attestation, {skipBlsValidation}, cache, false).isOk():
|
||||
continue
|
||||
|
||||
let score = 1
|
||||
let score = attCache.score(
|
||||
entry.data, entry.aggregates[j].aggregation_bits)
|
||||
if score == 0:
|
||||
# 0 score means the attestation would not bring any votes - discard
|
||||
# it early
|
||||
@ -897,24 +899,22 @@ proc getElectraAttestationsForBlock*(
|
||||
# then re-score the other candidates.
|
||||
var
|
||||
prevEpoch = state.data.get_previous_epoch()
|
||||
prevEpochSpace =
|
||||
when not (state is phase0.HashedBeaconState):
|
||||
MAX_ATTESTATIONS_ELECTRA
|
||||
else:
|
||||
state.data.previous_epoch_attestations.maxLen -
|
||||
state.data.previous_epoch_attestations.len()
|
||||
prevEpochSpace = MAX_ATTESTATIONS_ELECTRA
|
||||
|
||||
var res: seq[electra.Attestation]
|
||||
let totalCandidates = candidates.len()
|
||||
while candidates.len > 0 and res.lenu64() < MAX_ATTESTATIONS_ELECTRA:
|
||||
while candidates.len > 0 and res.lenu64() <
|
||||
MAX_ATTESTATIONS_ELECTRA * MAX_COMMITTEES_PER_SLOT:
|
||||
let entryCacheKey = block:
|
||||
# Find the candidate with the highest score - slot is used as a
|
||||
# tie-breaker so that more recent attestations are added first
|
||||
let
|
||||
candidate =
|
||||
# Fast path for when all remaining candidates fit
|
||||
if candidates.lenu64 < MAX_ATTESTATIONS_ELECTRA: candidates.len - 1
|
||||
else: maxIndex(candidates)
|
||||
if candidates.lenu64 < MAX_ATTESTATIONS_ELECTRA * MAX_COMMITTEES_PER_SLOT:
|
||||
candidates.len - 1
|
||||
else:
|
||||
maxIndex(candidates)
|
||||
(_, _, entry, j) = candidates[candidate]
|
||||
|
||||
candidates.del(candidate) # careful, `del` reorders candidates
|
||||
@ -929,7 +929,7 @@ proc getElectraAttestationsForBlock*(
|
||||
|
||||
# Update cache so that the new votes are taken into account when updating
|
||||
# the score below
|
||||
#attCache.add(entry[].data, entry[].aggregates[j].aggregation_bits)
|
||||
attCache.add(entry[].data, entry[].aggregates[j].aggregation_bits)
|
||||
|
||||
entry[].data.getAttestationCacheKey
|
||||
|
||||
@ -941,21 +941,43 @@ proc getElectraAttestationsForBlock*(
|
||||
if it.entry[].data.getAttestationCacheKey != entryCacheKey:
|
||||
continue
|
||||
|
||||
it.score = 1
|
||||
it.score = attCache.score(
|
||||
it.entry[].data,
|
||||
it.entry[].aggregates[it.validation].aggregation_bits)
|
||||
|
||||
candidates.keepItIf:
|
||||
# Only keep candidates that might add coverage
|
||||
it.score > 0
|
||||
|
||||
# TODO sort candidates by score - or really, rewrite the whole loop above ;)
|
||||
var res2: seq[electra.Attestation]
|
||||
var perBlock: Table[(Eth2Digest, Slot), seq[electra.Attestation]]
|
||||
|
||||
for a in res:
|
||||
let key = (a.data.beacon_block_root, a.data.slot)
|
||||
perBlock.mGetOrPut(key, newSeq[electra.Attestation](0)).add(a)
|
||||
|
||||
for a in perBlock.values():
|
||||
# TODO this will create on-chain aggregates that contain only one
|
||||
# committee index - this is obviously wrong but fixing requires
|
||||
# a more significant rewrite - we should combine the best aggregates
|
||||
# for each beacon block root
|
||||
let x = compute_on_chain_aggregate(a).valueOr:
|
||||
continue
|
||||
|
||||
res2.add(x)
|
||||
if res2.lenu64 == MAX_ATTESTATIONS_ELECTRA:
|
||||
break
|
||||
|
||||
let
|
||||
packingDur = Moment.now() - startPackingTick
|
||||
|
||||
debug "Packed attestations for block",
|
||||
newBlockSlot, packingDur, totalCandidates, attestations = res.len()
|
||||
newBlockSlot, packingDur, totalCandidates, attestations = res2.len()
|
||||
attestation_pool_block_attestation_packing_time.set(
|
||||
packingDur.toFloatSeconds())
|
||||
|
||||
res
|
||||
res2
|
||||
|
||||
proc getElectraAttestationsForBlock*(
|
||||
pool: var AttestationPool, state: ForkedHashedBeaconState,
|
||||
|
@ -90,7 +90,7 @@ func compatible_with_shuffling*(
|
||||
iterator get_attesting_indices*(shufflingRef: ShufflingRef,
|
||||
slot: Slot,
|
||||
committee_index: CommitteeIndex,
|
||||
bits: CommitteeValidatorsBits):
|
||||
bits: CommitteeValidatorsBits | ElectraCommitteeValidatorsBits):
|
||||
ValidatorIndex =
|
||||
if not bits.compatible_with_shuffling(shufflingRef, slot, committee_index):
|
||||
trace "get_attesting_indices: inconsistent aggregation and committee length"
|
||||
@ -104,34 +104,26 @@ iterator get_attesting_indices*(shufflingRef: ShufflingRef,
|
||||
iterator get_attesting_indices*(shufflingRef: ShufflingRef,
|
||||
slot: Slot,
|
||||
committee_bits: AttestationCommitteeBits,
|
||||
aggregation_bits: ElectraCommitteeValidatorsBits):
|
||||
aggregation_bits: ElectraCommitteeValidatorsBits, on_chain: static bool):
|
||||
ValidatorIndex =
|
||||
debugRaiseAssert "compatible with shuffling? needs checking"
|
||||
#if not aggregation_bits.compatible_with_shuffling(shufflingRef, slot, committee_index):
|
||||
if false:
|
||||
trace "get_attesting_indices: inconsistent aggregation and committee length"
|
||||
when on_chain:
|
||||
var pos = 0
|
||||
for committee_index in get_committee_indices(committee_bits):
|
||||
for _, validator_index in get_beacon_committee(
|
||||
shufflingRef, slot, committee_index):
|
||||
|
||||
if aggregation_bits[pos]:
|
||||
yield validator_index
|
||||
pos += 1
|
||||
else:
|
||||
debugComment "replace this implementation with actual iterator, after checking on conditions re repeat vals, ordering, etc; this is almost direct transcription of spec link algorithm in one of the places it doesn't make sense"
|
||||
## Return the set of attesting indices corresponding to ``aggregation_bits``
|
||||
## and ``committee_bits``.
|
||||
var output: HashSet[ValidatorIndex]
|
||||
let committee_indices = toSeq(committee_bits.oneIndices)
|
||||
var committee_offset = 0
|
||||
for index in committee_indices:
|
||||
let committee = get_beacon_committee(shufflingRef, slot, index.CommitteeIndex)
|
||||
var committee_attesters: HashSet[ValidatorIndex]
|
||||
for i, index in committee:
|
||||
if aggregation_bits[committee_offset + i]:
|
||||
committee_attesters.incl index
|
||||
output.incl committee_attesters
|
||||
|
||||
committee_offset += len(committee)
|
||||
|
||||
for validatorIndex in output:
|
||||
yield validatorIndex
|
||||
let committee_index = get_committee_index_one(committee_bits)
|
||||
for validator_index in get_attesting_indices(
|
||||
shufflingRef, slot, committee_index, aggregation_bits, on_chain):
|
||||
yield validator_index
|
||||
|
||||
iterator get_attesting_indices*(
|
||||
dag: ChainDAGRef, attestation: phase0.TrustedAttestation): ValidatorIndex =
|
||||
dag: ChainDAGRef, attestation: phase0.TrustedAttestation,
|
||||
on_chain: static bool = true): ValidatorIndex =
|
||||
block: # `return` is not allowed in an inline iterator
|
||||
let
|
||||
slot =
|
||||
@ -182,8 +174,8 @@ iterator get_attesting_indices*(
|
||||
yield validator
|
||||
|
||||
iterator get_attesting_indices*(
|
||||
dag: ChainDAGRef, attestation: electra.TrustedAttestation): ValidatorIndex =
|
||||
debugRaiseAssert "bad duplication, mostly to avoid the get_attesting_index call from potentially getting screwed up in deployment version"
|
||||
dag: ChainDAGRef, attestation: electra.TrustedAttestation,
|
||||
on_chain: static bool): ValidatorIndex =
|
||||
block: # `return` is not allowed in an inline iterator
|
||||
let
|
||||
slot =
|
||||
@ -220,13 +212,14 @@ iterator get_attesting_indices*(
|
||||
break
|
||||
|
||||
for validator in get_attesting_indices(
|
||||
shufflingRef, slot, attestation.committee_bits, attestation.aggregation_bits):
|
||||
shufflingRef, slot, attestation.committee_bits,
|
||||
attestation.aggregation_bits, on_chain):
|
||||
yield validator
|
||||
|
||||
func get_attesting_indices_one*(shufflingRef: ShufflingRef,
|
||||
slot: Slot,
|
||||
committee_index: CommitteeIndex,
|
||||
bits: CommitteeValidatorsBits):
|
||||
bits: CommitteeValidatorsBits | ElectraCommitteeValidatorsBits):
|
||||
Option[ValidatorIndex] =
|
||||
# A variation on get_attesting_indices that returns the validator index only
|
||||
# if only one validator index is set
|
||||
@ -239,16 +232,20 @@ func get_attesting_indices_one*(shufflingRef: ShufflingRef,
|
||||
|
||||
func get_attesting_indices_one*(shufflingRef: ShufflingRef,
|
||||
slot: Slot,
|
||||
committee_indices: AttestationCommitteeBits,
|
||||
aggregation_bits: ElectraCommitteeValidatorsBits):
|
||||
Option[ValidatorIndex] =
|
||||
committee_bits: AttestationCommitteeBits,
|
||||
aggregation_bits: ElectraCommitteeValidatorsBits,
|
||||
on_chain: static bool):
|
||||
Opt[ValidatorIndex] =
|
||||
# A variation on get_attesting_indices that returns the validator index only
|
||||
# if only one validator index is set
|
||||
var res = none(ValidatorIndex)
|
||||
static: doAssert not on_chain, "only on_chain supported"
|
||||
|
||||
var res = Opt.none(ValidatorIndex)
|
||||
let committee_index = ? get_committee_index_one(committee_bits)
|
||||
for validator_index in get_attesting_indices(
|
||||
shufflingRef, slot, committee_indices, aggregation_bits):
|
||||
if res.isSome(): return none(ValidatorIndex)
|
||||
res = some(validator_index)
|
||||
shufflingRef, slot, committee_index, aggregation_bits):
|
||||
if res.isSome(): return Opt.none(ValidatorIndex)
|
||||
res = Opt.some(validator_index)
|
||||
res
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.6/specs/phase0/beacon-chain.md#get_attesting_indices
|
||||
@ -263,8 +260,11 @@ func get_attesting_indices*(shufflingRef: ShufflingRef,
|
||||
func get_attesting_indices*(shufflingRef: ShufflingRef,
|
||||
slot: Slot,
|
||||
committee_index: CommitteeIndex,
|
||||
bits: ElectraCommitteeValidatorsBits):
|
||||
bits: ElectraCommitteeValidatorsBits,
|
||||
on_chain: static bool):
|
||||
seq[ValidatorIndex] =
|
||||
static: doAssert not on_chain, "only on_chain supported"
|
||||
|
||||
for idx in get_attesting_indices(shufflingRef, slot, committee_index, bits):
|
||||
result.add(idx)
|
||||
|
||||
|
@ -285,7 +285,7 @@ proc process_block*(self: var ForkChoice,
|
||||
|
||||
for attestation in blck.body.attestations:
|
||||
if attestation.data.beacon_block_root in self.backend:
|
||||
for validator_index in dag.get_attesting_indices(attestation):
|
||||
for validator_index in dag.get_attesting_indices(attestation, true):
|
||||
self.backend.process_attestation(
|
||||
validator_index,
|
||||
attestation.data.beacon_block_root,
|
||||
|
@ -603,7 +603,7 @@ proc storeBlock(
|
||||
src, wallTime, trustedBlock.message)
|
||||
|
||||
for attestation in trustedBlock.message.body.attestations:
|
||||
for validator_index in dag.get_attesting_indices(attestation):
|
||||
for validator_index in dag.get_attesting_indices(attestation, true):
|
||||
vm[].registerAttestationInBlock(attestation.data, validator_index,
|
||||
trustedBlock.message.slot)
|
||||
|
||||
|
@ -879,7 +879,8 @@ proc validateAttestation*(
|
||||
let
|
||||
fork = pool.dag.forkAtEpoch(attestation.data.slot.epoch)
|
||||
attesting_index = get_attesting_indices_one(
|
||||
shufflingRef, slot, attestation.committee_bits, attestation.aggregation_bits)
|
||||
shufflingRef, slot, attestation.committee_bits,
|
||||
attestation.aggregation_bits, false)
|
||||
|
||||
# The number of aggregation bits matches the committee size, which ensures
|
||||
# this condition holds.
|
||||
@ -1158,7 +1159,7 @@ proc validateAggregate*(
|
||||
let
|
||||
fork = pool.dag.forkAtEpoch(aggregate.data.slot.epoch)
|
||||
attesting_indices = get_attesting_indices(
|
||||
shufflingRef, slot, committee_index, aggregate.aggregation_bits)
|
||||
shufflingRef, slot, committee_index, aggregate.aggregation_bits, false)
|
||||
|
||||
let
|
||||
sig =
|
||||
|
@ -596,24 +596,16 @@ iterator get_attesting_indices_iter*(
|
||||
aggregation_bits: ElectraCommitteeValidatorsBits,
|
||||
committee_bits: auto,
|
||||
cache: var StateCache): ValidatorIndex =
|
||||
debugComment "replace this implementation with actual iterator, after checking on conditions re repeat vals, ordering, etc; this is almost direct transcription of spec link algorithm in one of the places it doesn't make sense"
|
||||
## Return the set of attesting indices corresponding to ``aggregation_bits``
|
||||
## and ``committee_bits``.
|
||||
var output: HashSet[ValidatorIndex]
|
||||
let committee_indices = toSeq(committee_bits.oneIndices)
|
||||
var committee_offset = 0
|
||||
for index in committee_indices:
|
||||
let committee = get_beacon_committee(state, data.slot, index.CommitteeIndex, cache)
|
||||
var committee_attesters: HashSet[ValidatorIndex]
|
||||
for i, index in committee:
|
||||
if aggregation_bits[committee_offset + i]:
|
||||
committee_attesters.incl index
|
||||
output.incl committee_attesters
|
||||
var pos = 0
|
||||
for committee_index in get_committee_indices(committee_bits):
|
||||
for _, validator_index in get_beacon_committee(
|
||||
state, data.slot, committee_index, cache):
|
||||
|
||||
committee_offset += len(committee)
|
||||
|
||||
for validatorIndex in output:
|
||||
yield validatorIndex
|
||||
if aggregation_bits[pos]:
|
||||
yield validator_index
|
||||
pos += 1
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.6/specs/phase0/beacon-chain.md#get_attesting_indices
|
||||
func get_attesting_indices*(
|
||||
@ -886,7 +878,7 @@ func get_base_reward(
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.6/specs/phase0/beacon-chain.md#attestations
|
||||
proc check_attestation*(
|
||||
state: ForkyBeaconState, attestation: SomeAttestation, flags: UpdateFlags,
|
||||
cache: var StateCache): Result[void, cstring] =
|
||||
cache: var StateCache, on_chain: static bool = true): Result[void, cstring] =
|
||||
## Check that an attestation follows the rules of being included in the state
|
||||
## at the current slot. When acting as a proposer, the same rules need to
|
||||
## be followed!
|
||||
@ -921,7 +913,7 @@ proc check_attestation*(
|
||||
proc check_attestation*(
|
||||
state: electra.BeaconState,
|
||||
attestation: electra.Attestation | electra.TrustedAttestation,
|
||||
flags: UpdateFlags, cache: var StateCache): Result[void, cstring] =
|
||||
flags: UpdateFlags, cache: var StateCache, on_chain: static bool): Result[void, cstring] =
|
||||
## Check that an attestation follows the rules of being included in the state
|
||||
## at the current slot. When acting as a proposer, the same rules need to
|
||||
## be followed!
|
||||
@ -937,17 +929,26 @@ proc check_attestation*(
|
||||
if not (data.index == 0):
|
||||
return err("Electra attestation data index not 0")
|
||||
|
||||
var participants_count = 0
|
||||
debugComment "cache doesn't know about forks"
|
||||
for index in attestation.committee_bits.oneIndices:
|
||||
if not (index.uint64 < get_committee_count_per_slot(
|
||||
state, data.target.epoch, cache)):
|
||||
return err("foo")
|
||||
let committee = get_beacon_committee(state, data.slot, index.CommitteeIndex, cache)
|
||||
participants_count += len(committee)
|
||||
when on_chain:
|
||||
var participants_count = 0'u64
|
||||
debugComment "cache doesn't know about forks"
|
||||
for index in attestation.committee_bits.oneIndices:
|
||||
if not (index.uint64 < get_committee_count_per_slot(
|
||||
state, data.target.epoch, cache)):
|
||||
return err("attestation wrong committee index len")
|
||||
participants_count +=
|
||||
get_beacon_committee_len(state, data.slot, index.CommitteeIndex, cache)
|
||||
|
||||
if not (len(attestation.aggregation_bits) == participants_count):
|
||||
return err("")
|
||||
if not (lenu64(attestation.aggregation_bits) == participants_count):
|
||||
return err("attestation wrong aggregation bit length")
|
||||
else:
|
||||
let
|
||||
committee_index = get_committee_index_one(attestation.committee_bits).valueOr:
|
||||
return err("Network attestation without single committee index")
|
||||
|
||||
if not (lenu64(attestation.aggregation_bits) ==
|
||||
get_beacon_committee_len(state, data.slot, committee_index, cache)):
|
||||
return err("attestation wrong aggregation bit length")
|
||||
|
||||
if epoch == get_current_epoch(state):
|
||||
if not (data.source == state.current_justified_checkpoint):
|
||||
@ -1100,7 +1101,7 @@ proc process_attestation*(
|
||||
attestation: electra.Attestation | electra.TrustedAttestation,
|
||||
flags: UpdateFlags, base_reward_per_increment: Gwei,
|
||||
cache: var StateCache): Result[Gwei, cstring] =
|
||||
? check_attestation(state, attestation, flags, cache)
|
||||
? check_attestation(state, attestation, flags, cache, true)
|
||||
|
||||
let proposer_index = get_beacon_proposer_index(state, cache).valueOr:
|
||||
return err("process_attestation: no beacon proposer index and probably no active validators")
|
||||
|
@ -731,6 +731,7 @@ iterator getValidatorIndices*(attester_slashing: AttesterSlashing | TrustedAttes
|
||||
func shortLog*(v: electra.Attestation | electra.TrustedAttestation): auto =
|
||||
(
|
||||
aggregation_bits: v.aggregation_bits,
|
||||
committee_bits: v.committee_bits,
|
||||
data: shortLog(v.data),
|
||||
signature: shortLog(v.signature)
|
||||
)
|
||||
|
@ -8,7 +8,9 @@
|
||||
|
||||
# Helpers and functions pertaining to managing the validator set
|
||||
|
||||
import ./helpers
|
||||
import
|
||||
std/algorithm,
|
||||
"."/[crypto, helpers]
|
||||
export helpers
|
||||
|
||||
const
|
||||
@ -541,3 +543,58 @@ func compute_subscribed_subnet(node_id: UInt256, epoch: Epoch, index: uint64):
|
||||
iterator compute_subscribed_subnets*(node_id: UInt256, epoch: Epoch): SubnetId =
|
||||
for index in 0'u64 ..< SUBNETS_PER_NODE:
|
||||
yield compute_subscribed_subnet(node_id, epoch, index)
|
||||
|
||||
iterator get_committee_indices*(bits: AttestationCommitteeBits): CommitteeIndex =
|
||||
for index, b in bits:
|
||||
if b:
|
||||
yield CommitteeIndex.init(uint64(index)).valueOr:
|
||||
break # Too many bits! Shouldn't happen
|
||||
|
||||
func get_committee_index_one*(bits: AttestationCommitteeBits): Opt[CommitteeIndex] =
|
||||
var res = Opt.none(CommitteeIndex)
|
||||
for committee_index in get_committee_indices(bits):
|
||||
if res.isSome(): return Opt.none(CommitteeIndex)
|
||||
res = Opt.some(committee_index)
|
||||
res
|
||||
|
||||
proc compute_on_chain_aggregate*(
|
||||
network_aggregates: openArray[electra.Attestation]): Opt[electra.Attestation] =
|
||||
# aggregates = sorted(network_aggregates, key=lambda a: get_committee_indices(a.committee_bits)[0])
|
||||
let aggregates = network_aggregates.sortedByIt(it.committee_bits.get_committee_index_one().expect("just one"))
|
||||
|
||||
let data = aggregates[0].data
|
||||
|
||||
var agg: AggregateSignature
|
||||
var committee_bits: AttestationCommitteeBits
|
||||
|
||||
var totalLen = 0
|
||||
for i, a in aggregates:
|
||||
totalLen += a.aggregation_bits.len
|
||||
|
||||
var aggregation_bits = ElectraCommitteeValidatorsBits.init(totalLen)
|
||||
var pos = 0
|
||||
for i, a in aggregates:
|
||||
let
|
||||
committee_index = ? get_committee_index_one(a.committee_bits)
|
||||
first = pos == 0
|
||||
|
||||
for b in a.aggregation_bits:
|
||||
aggregation_bits[pos] = b
|
||||
pos += 1
|
||||
|
||||
let sig = ? a.signature.load() # Expensive
|
||||
if first:
|
||||
agg = AggregateSignature.init(sig)
|
||||
else:
|
||||
agg.aggregate(sig)
|
||||
|
||||
committee_bits[int(committee_index)] = true
|
||||
|
||||
let signature = agg.finish()
|
||||
|
||||
ok electra.Attestation(
|
||||
aggregation_bits: aggregation_bits,
|
||||
data: data,
|
||||
committee_bits: committee_bits,
|
||||
signature: signature.toValidatorSig(),
|
||||
)
|
||||
|
@ -501,7 +501,7 @@ proc makeBeaconBlockForHeadAndSlot*(
|
||||
let
|
||||
attestations =
|
||||
when PayloadType.kind == ConsensusFork.Electra:
|
||||
default(seq[electra.Attestation])
|
||||
node.attestationPool[].getElectraAttestationsForBlock(state[], cache)
|
||||
else:
|
||||
node.attestationPool[].getAttestationsForBlock(state[], cache)
|
||||
exits = withState(state[]):
|
||||
|
@ -381,7 +381,7 @@ func collectFromAttestations(
|
||||
doAssert base_reward_per_increment > 0.Gwei
|
||||
for attestation in forkyBlck.message.body.attestations:
|
||||
doAssert check_attestation(
|
||||
forkyState.data, attestation, {}, cache).isOk
|
||||
forkyState.data, attestation, {}, cache, true).isOk
|
||||
let proposerReward =
|
||||
if attestation.data.target.epoch == get_current_epoch(forkyState.data):
|
||||
get_proposer_reward(
|
||||
|
@ -23,7 +23,7 @@ import
|
||||
../beacon_chain/spec/[beaconstate, helpers, state_transition, validator],
|
||||
../beacon_chain/beacon_clock,
|
||||
# Test utilities
|
||||
./testutil, ./testdbutil, ./testblockutil
|
||||
./testutil, ./testdbutil, ./testblockutil, ./consensus_spec/fixtures_utils
|
||||
|
||||
from std/sequtils import toSeq
|
||||
from ./testbcutil import addHeadBlock
|
||||
@ -49,6 +49,8 @@ func combine(tgt: var phase0.Attestation, src: phase0.Attestation) =
|
||||
|
||||
func loadSig(a: phase0.Attestation): CookedSig =
|
||||
a.signature.load.get()
|
||||
func loadSig(a: electra.Attestation): CookedSig =
|
||||
a.signature.load.get()
|
||||
|
||||
proc pruneAtFinalization(dag: ChainDAGRef, attPool: AttestationPool) =
|
||||
if dag.needStateCachesAndForkChoicePruning():
|
||||
@ -729,4 +731,127 @@ suite "Attestation pool processing" & preset():
|
||||
epochRef, blckRef, unrealized, signedBlock.message,
|
||||
blckRef.slot.start_beacon_time)
|
||||
|
||||
doAssert: b10Add_clone.error == VerifierError.Duplicate
|
||||
doAssert: b10Add_clone.error == VerifierError.Duplicate
|
||||
|
||||
suite "Attestation pool electra processing" & preset():
|
||||
## For now just test that we can compile and execute block processing with
|
||||
## mock data.
|
||||
|
||||
setup:
|
||||
# Genesis state that results in 6 members per committee
|
||||
let rng = HmacDrbgContext.new()
|
||||
var
|
||||
validatorMonitor = newClone(ValidatorMonitor.init())
|
||||
cfg = genesisTestRuntimeConfig(ConsensusFork.Electra)
|
||||
dag = init(
|
||||
ChainDAGRef, cfg,
|
||||
makeTestDB(SLOTS_PER_EPOCH * 6, cfg = cfg),
|
||||
validatorMonitor, {})
|
||||
taskpool = Taskpool.new()
|
||||
verifier = BatchVerifier.init(rng, taskpool)
|
||||
quarantine = newClone(Quarantine.init())
|
||||
pool = newClone(AttestationPool.init(dag, quarantine))
|
||||
state = newClone(dag.headState)
|
||||
cache = StateCache()
|
||||
info = ForkedEpochInfo()
|
||||
# Slot 0 is a finalized slot - won't be making attestations for it..
|
||||
check:
|
||||
process_slots(
|
||||
dag.cfg, state[], getStateField(state[], slot) + 1, cache, info,
|
||||
{}).isOk()
|
||||
|
||||
|
||||
test "Can add and retrieve simple electra attestations" & preset():
|
||||
let
|
||||
# Create an attestation for slot 1!
|
||||
bc0 = get_beacon_committee(
|
||||
state[], getStateField(state[], slot), 0.CommitteeIndex, cache)
|
||||
attestation = makeElectraAttestation(
|
||||
state[], state[].latest_block_root, bc0[0], cache)
|
||||
|
||||
pool[].addAttestation(
|
||||
attestation, @[bc0[0]], attestation.loadSig,
|
||||
attestation.data.slot.start_beacon_time)
|
||||
|
||||
check:
|
||||
process_slots(
|
||||
defaultRuntimeConfig, state[],
|
||||
getStateField(state[], slot) + MIN_ATTESTATION_INCLUSION_DELAY, cache,
|
||||
info, {}).isOk()
|
||||
|
||||
let attestations = pool[].getElectraAttestationsForBlock(state[], cache)
|
||||
|
||||
check:
|
||||
attestations.len == 1
|
||||
|
||||
let
|
||||
root1 = addTestBlock(
|
||||
state[], cache, electraAttestations = attestations,
|
||||
nextSlot = false).electraData.root
|
||||
bc1 = get_beacon_committee(
|
||||
state[], getStateField(state[], slot), 0.CommitteeIndex, cache)
|
||||
att1 = makeElectraAttestation(state[], root1, bc1[0], cache)
|
||||
|
||||
check:
|
||||
withState(state[]): forkyState.latest_block_root == root1
|
||||
|
||||
process_slots(
|
||||
defaultRuntimeConfig, state[],
|
||||
getStateField(state[], slot) + MIN_ATTESTATION_INCLUSION_DELAY, cache,
|
||||
info, {}).isOk()
|
||||
|
||||
withState(state[]): forkyState.latest_block_root == root1
|
||||
|
||||
check:
|
||||
# shouldn't include already-included attestations
|
||||
pool[].getElectraAttestationsForBlock(state[], cache) == []
|
||||
|
||||
pool[].addAttestation(
|
||||
att1, @[bc1[0]], att1.loadSig, att1.data.slot.start_beacon_time)
|
||||
|
||||
check:
|
||||
# but new ones should go in
|
||||
pool[].getElectraAttestationsForBlock(state[], cache).len() == 1
|
||||
|
||||
let
|
||||
att2 = makeElectraAttestation(state[], root1, bc1[1], cache)
|
||||
pool[].addAttestation(
|
||||
att2, @[bc1[1]], att2.loadSig, att2.data.slot.start_beacon_time)
|
||||
|
||||
let
|
||||
combined = pool[].getElectraAttestationsForBlock(state[], cache)
|
||||
|
||||
check:
|
||||
# New attestations should be combined with old attestations
|
||||
combined.len() == 1
|
||||
combined[0].aggregation_bits.countOnes() == 2
|
||||
|
||||
pool[].addAttestation(
|
||||
combined[0], @[bc1[1], bc1[0]], combined[0].loadSig,
|
||||
combined[0].data.slot.start_beacon_time)
|
||||
|
||||
check:
|
||||
# readding the combined attestation shouldn't have an effect
|
||||
pool[].getElectraAttestationsForBlock(state[], cache).len() == 1
|
||||
|
||||
let
|
||||
# Someone votes for a different root
|
||||
att3 = makeElectraAttestation(state[], ZERO_HASH, bc1[2], cache)
|
||||
pool[].addAttestation(
|
||||
att3, @[bc1[2]], att3.loadSig, att3.data.slot.start_beacon_time)
|
||||
|
||||
check:
|
||||
# We should now get both attestations for the block, but the aggregate
|
||||
# should be the one with the most votes
|
||||
pool[].getElectraAttestationsForBlock(state[], cache).len() == 2
|
||||
# pool[].getAggregatedAttestation(2.Slot, 0.CommitteeIndex).
|
||||
# get().aggregation_bits.countOnes() == 2
|
||||
# pool[].getAggregatedAttestation(2.Slot, hash_tree_root(att2.data)).
|
||||
# get().aggregation_bits.countOnes() == 2
|
||||
|
||||
let
|
||||
# Someone votes for a different root
|
||||
att4 = makeElectraAttestation(state[], ZERO_HASH, bc1[2], cache)
|
||||
pool[].addAttestation(
|
||||
att4, @[bc1[2]], att3.loadSig, att3.data.slot.start_beacon_time)
|
||||
|
||||
|
@ -158,6 +158,7 @@ proc addTestBlock*(
|
||||
cache: var StateCache,
|
||||
eth1_data: Eth1Data = Eth1Data(),
|
||||
attestations: seq[phase0.Attestation] = newSeq[phase0.Attestation](),
|
||||
electraAttestations: seq[electra.Attestation] = newSeq[electra.Attestation](),
|
||||
deposits: seq[Deposit] = newSeq[Deposit](),
|
||||
sync_aggregate: SyncAggregate = SyncAggregate.init(),
|
||||
graffiti: GraffitiBytes = default(GraffitiBytes),
|
||||
@ -221,7 +222,7 @@ proc addTestBlock*(
|
||||
block_hash: eth1_data.block_hash),
|
||||
graffiti,
|
||||
when consensusFork == ConsensusFork.Electra:
|
||||
default(seq[electra.Attestation])
|
||||
electraAttestations
|
||||
else:
|
||||
attestations,
|
||||
deposits,
|
||||
@ -248,6 +249,7 @@ proc makeTestBlock*(
|
||||
cache: var StateCache,
|
||||
eth1_data = Eth1Data(),
|
||||
attestations = newSeq[phase0.Attestation](),
|
||||
electraAttestations = newSeq[electra.Attestation](),
|
||||
deposits = newSeq[Deposit](),
|
||||
sync_aggregate = SyncAggregate.init(),
|
||||
graffiti = default(GraffitiBytes),
|
||||
@ -259,7 +261,8 @@ proc makeTestBlock*(
|
||||
let tmpState = assignClone(state)
|
||||
addTestBlock(
|
||||
tmpState[], cache, eth1_data,
|
||||
attestations, deposits, sync_aggregate, graffiti, cfg = cfg)
|
||||
attestations, electraAttestations, deposits, sync_aggregate, graffiti,
|
||||
cfg = cfg)
|
||||
|
||||
func makeAttestationData*(
|
||||
state: ForkyBeaconState, slot: Slot, committee_index: CommitteeIndex,
|
||||
@ -290,7 +293,7 @@ func makeAttestationData*(
|
||||
func makeAttestationSig(
|
||||
fork: Fork, genesis_validators_root: Eth2Digest, data: AttestationData,
|
||||
committee: openArray[ValidatorIndex],
|
||||
bits: CommitteeValidatorsBits): ValidatorSig =
|
||||
bits: CommitteeValidatorsBits | ElectraCommitteeValidatorsBits): ValidatorSig =
|
||||
let signing_root = compute_attestation_signing_root(
|
||||
fork, genesis_validators_root, data)
|
||||
|
||||
@ -402,6 +405,78 @@ func makeFullAttestations*(
|
||||
|
||||
result.add attestation
|
||||
|
||||
func makeElectraAttestation(
|
||||
state: ForkedHashedBeaconState, beacon_block_root: Eth2Digest,
|
||||
committee: seq[ValidatorIndex], slot: Slot, committee_index: CommitteeIndex,
|
||||
validator_index: ValidatorIndex, cache: var StateCache,
|
||||
flags: UpdateFlags = {}): electra.Attestation =
|
||||
let
|
||||
index_in_committee = committee.find(validator_index)
|
||||
data = makeAttestationData(state, slot, CommitteeIndex(0), beacon_block_root)
|
||||
|
||||
doAssert index_in_committee != -1, "find_beacon_committee should guarantee this"
|
||||
|
||||
var aggregation_bits = ElectraCommitteeValidatorsBits.init(committee.len)
|
||||
aggregation_bits.setBit index_in_committee
|
||||
|
||||
let sig = if skipBlsValidation in flags:
|
||||
ValidatorSig()
|
||||
else:
|
||||
makeAttestationSig(
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root),
|
||||
data, committee, aggregation_bits)
|
||||
|
||||
var committee_bits: AttestationCommitteeBits
|
||||
committee_bits[int committee_index] = true
|
||||
|
||||
electra.Attestation(
|
||||
data: data,
|
||||
committee_bits: committee_bits,
|
||||
aggregation_bits: aggregation_bits,
|
||||
signature: sig
|
||||
)
|
||||
|
||||
func makeElectraAttestation*(
|
||||
state: ForkedHashedBeaconState, beacon_block_root: Eth2Digest,
|
||||
validator_index: ValidatorIndex, cache: var StateCache): electra.Attestation =
|
||||
let (committee, slot, index) =
|
||||
find_beacon_committee(state, validator_index, cache)
|
||||
makeElectraAttestation(state, beacon_block_root, committee, slot, index,
|
||||
validator_index, cache)
|
||||
|
||||
func makeFullElectraAttestations*(
|
||||
state: ForkedHashedBeaconState, beacon_block_root: Eth2Digest, slot: Slot,
|
||||
cache: var StateCache,
|
||||
flags: UpdateFlags = {}): seq[electra.Attestation] =
|
||||
# Create attestations in which the full committee participates for each shard
|
||||
# that should be attested to during a particular slot
|
||||
let committees_per_slot = get_committee_count_per_slot(
|
||||
state, slot.epoch, cache)
|
||||
for committee_index in get_committee_indices(committees_per_slot):
|
||||
let
|
||||
committee = get_beacon_committee(state, slot, committee_index, cache)
|
||||
data = makeAttestationData(state, slot, CommitteeIndex(0), beacon_block_root)
|
||||
var
|
||||
committee_bits: AttestationCommitteeBits
|
||||
|
||||
committee_bits[int committee_index] = true
|
||||
|
||||
doAssert committee.len() >= 1
|
||||
var attestation = electra.Attestation(
|
||||
aggregation_bits: ElectraCommitteeValidatorsBits.init(committee.len),
|
||||
committee_bits: committee_bits,
|
||||
data: data)
|
||||
for i in 0..<committee.len:
|
||||
attestation.aggregation_bits.setBit(i)
|
||||
|
||||
attestation.signature = makeAttestationSig(
|
||||
getStateField(state, fork),
|
||||
getStateField(state, genesis_validators_root), data, committee,
|
||||
attestation.aggregation_bits)
|
||||
|
||||
result.add attestation
|
||||
|
||||
proc makeSyncAggregate(
|
||||
state: ForkedHashedBeaconState,
|
||||
syncCommitteeRatio: float,
|
||||
|
@ -27,8 +27,10 @@ proc makeTestDB*(
|
||||
cfg = defaultRuntimeConfig): BeaconChainDB =
|
||||
# Blob support requires DENEB_FORK_EPOCH != FAR_FUTURE_EPOCH
|
||||
var cfg = cfg
|
||||
cfg.CAPELLA_FORK_EPOCH = 90000.Epoch
|
||||
cfg.DENEB_FORK_EPOCH = 100000.Epoch
|
||||
if cfg.CAPELLA_FORK_EPOCH == FAR_FUTURE_EPOCH:
|
||||
cfg.CAPELLA_FORK_EPOCH = 90000.Epoch
|
||||
if cfg.DENEB_FORK_EPOCH == FAR_FUTURE_EPOCH:
|
||||
cfg.DENEB_FORK_EPOCH = 100000.Epoch
|
||||
|
||||
var genState = (ref ForkedHashedBeaconState)(
|
||||
kind: ConsensusFork.Phase0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user