9
9
10
10
import
11
11
# Standard libraries
12
- std/ [algorithm, deques, sequtils, tables, options],
12
+ std/ [algorithm, deques, sequtils, sets, tables, options],
13
13
# Status libraries
14
- chronicles, stew/ [byteutils], json_serialization/ std/ sets,
14
+ chronicles, stew/ [byteutils], json_serialization/ std/ sets as jsonSets ,
15
15
# Internal
16
16
./ spec/ [beaconstate, datatypes, crypto, digest, helpers],
17
17
./ block_pool, ./ block_pools/ candidate_chains, ./ beacon_node_types,
18
18
./ fork_choice/ fork_choice
19
19
20
- export beacon_node_types
20
+ export beacon_node_types, sets
21
21
22
22
logScope: topics = " attpool"
23
23
@@ -39,7 +39,7 @@ proc init*(T: type AttestationPool, blockPool: BlockPool): T =
39
39
doAssert blockPool.heads.len == 1 , " Init only supports a single history"
40
40
41
41
var blocks: seq [BlockRef]
42
- var cur = blockPool.head.blck
42
+ var cur = blockPool.head
43
43
while cur != blockPool.finalizedHead.blck:
44
44
blocks.add cur
45
45
cur = cur.parent
@@ -65,65 +65,11 @@ proc init*(T: type AttestationPool, blockPool: BlockPool): T =
65
65
finalized_root = shortlog(blockPool.finalizedHead.blck.root)
66
66
67
67
T(
68
- mapSlotsToAttestations: initDeque[AttestationsSeen](),
69
68
blockPool: blockPool,
70
69
unresolved: initTable[Eth2Digest, UnresolvedAttestation](),
71
70
forkChoice: forkChoice
72
71
)
73
72
74
- proc slotIndex(
75
- pool: var AttestationPool, state: BeaconState, attestationSlot: Slot): int =
76
- ## Grow and garbage collect pool, returning the deque index of the slot
77
-
78
- # We keep a sliding window of attestations, roughly from the last finalized
79
- # epoch to now, because these are the attestations that may affect the voting
80
- # outcome. Some of these attestations will already have been added to blocks,
81
- # while others are fresh off the network.
82
- # TODO only the latest vote of each validator counts. Can we use that somehow?
83
- logScope: pcs = "atp_slot_maintenance"
84
-
85
- doAssert attestationSlot >= pool.startingSlot,
86
- """
87
- We should have checked in addResolved that attestation is newer than
88
- finalized_slot and we never prune things before that, per below condition!
89
- """ &
90
- ", attestationSlot: " & $shortLog(attestationSlot) &
91
- ", startingSlot: " & $shortLog(pool.startingSlot)
92
-
93
- if pool.mapSlotsToAttestations.len == 0:
94
- # Because the first attestations may arrive in any order, we'll make sure
95
- # to start counting at the last finalized epoch start slot - anything
96
- # earlier than that is thrown out by the above check
97
- info "First attestation!",
98
- attestationSlot = shortLog(attestationSlot)
99
- pool.startingSlot =
100
- state.finalized_checkpoint.epoch.compute_start_slot_at_epoch()
101
-
102
- if pool.startingSlot + pool.mapSlotsToAttestations.lenu64 <= attestationSlot:
103
- trace "Growing attestation pool",
104
- attestationSlot = shortLog(attestationSlot),
105
- startingSlot = shortLog(pool.startingSlot)
106
-
107
- # Make sure there's a pool entry for every slot, even when there's a gap
108
- while pool.startingSlot + pool.mapSlotsToAttestations.lenu64 <= attestationSlot:
109
- pool.mapSlotsToAttestations.addLast(AttestationsSeen())
110
-
111
- if pool.startingSlot <
112
- state.finalized_checkpoint.epoch.compute_start_slot_at_epoch():
113
- debug "Pruning attestation pool",
114
- startingSlot = shortLog(pool.startingSlot),
115
- finalizedSlot = shortLog(
116
- state.finalized_checkpoint
117
- .epoch.compute_start_slot_at_epoch())
118
-
119
- # TODO there should be a better way to remove a whole epoch of stuff..
120
- while pool.startingSlot <
121
- state.finalized_checkpoint.epoch.compute_start_slot_at_epoch():
122
- pool.mapSlotsToAttestations.popFirst()
123
- pool.startingSlot += 1
124
-
125
- int (attestationSlot - pool.startingSlot)
126
-
127
73
func processAttestation(
128
74
pool: var AttestationPool, participants: HashSet[ValidatorIndex],
129
75
block_root: Eth2Digest, target_epoch: Epoch) =
@@ -137,61 +83,56 @@ func addUnresolved(pool: var AttestationPool, attestation: Attestation) =
137
83
attestation: attestation,
138
84
)
139
85
140
- proc addResolved(pool: var AttestationPool, blck: BlockRef, attestation: Attestation) =
141
- logScope:
142
- attestation = shortLog(attestation)
143
-
144
- doAssert blck.root == attestation.data.beacon_block_root
145
-
146
- # TODO Which state should we use to validate the attestation? It seems
147
- # reasonable to involve the head being voted for as well as the intended
148
- # slot of the attestation - double-check this with spec
86
+ func candidateIdx(pool: AttestationPool, slot: Slot): Option[uint64 ] =
87
+ if slot >= pool.startingSlot and
88
+ slot < (pool.startingSlot + pool.candidates.lenu64):
89
+ some(slot mod pool.candidates.lenu64)
90
+ else:
91
+ none(uint64 )
149
92
150
- # TODO: filter valid attestation as much as possible before state rewind
151
- # TODO: the below check does not respect the inclusion delay
152
- # we should use isValidAttestationSlot instead
153
- if blck.slot > attestation.data.slot:
154
- notice "Invalid attestation (too new!)",
155
- blockSlot = shortLog(blck.slot)
93
+ proc updateCurrent(pool: var AttestationPool, wallSlot: Slot) =
94
+ if wallSlot + 1 < pool.candidates.lenu64:
156
95
return
157
96
158
- if attestation.data.slot < pool.startingSlot:
159
- # It can happen that attestations in blocks for example are included even
160
- # though they no longer are relevant for finalization - let's clear
161
- # these out
162
- debug "Old attestation",
163
- startingSlot = pool.startingSlot
97
+ if pool.startingSlot + pool.candidates.lenu64 - 1 > wallSlot:
98
+ error "Current slot older than attestation pool view, clock reset?",
99
+ poolSlot = pool.startingSlot, wallSlot
164
100
return
165
101
166
- # if not isValidAttestationSlot(attestation.data.slot, blck.slot):
167
- # # Logging in isValidAttestationSlot
168
- # return
102
+ # As time passes we'll clear out any old attestations as they are no longer
103
+ # viable to be included in blocks
169
104
170
- # Check that the attestation is indeed valid
171
- if (let v = check_attestation_slot_target(attestation.data); v.isErr):
172
- debug "Invalid attestation", err = v.error
173
- return
105
+ let newWallSlot = wallSlot + 1 - pool.candidates.lenu64
106
+ for i in pool.startingSlot..newWallSlot:
107
+ pool.candidates[i.uint64 mod pool.candidates.lenu6 4] = AttestationsSeen()
174
108
175
- # Get a temporary state at the (block, slot) targeted by the attestation
176
- updateStateData(
177
- pool.blockPool, pool.blockPool.tmpState,
178
- BlockSlot(blck: blck, slot: attestation.data.slot),
179
- true)
109
+ pool.startingSlot = newWallSlot
180
110
181
- template state(): BeaconState = pool.blockPool.tmpState.data.data
111
+ proc addResolved(
112
+ pool: var AttestationPool, blck: BlockRef, attestation: Attestation,
113
+ wallSlot: Slot) =
114
+ # Add an attestation whose parent we know
115
+ logScope:
116
+ attestation = shortLog(attestation)
182
117
183
- # TODO inefficient data structures..
118
+ updateCurrent(pool, wallSlot)
119
+
120
+ doAssert blck.root == attestation.data.beacon_block_root
121
+
122
+ let candidateIdx = pool.candidateIdx(attestation.data.slot)
123
+ if candidateIdx.isNone:
124
+ debug " Attestation slot out of range" ,
125
+ startingSlot = pool.startingSlot
126
+ return
184
127
185
- var cache = getEpochCache(blck, state)
186
128
let
187
- attestationSlot = attestation.data.slot
188
- idx = pool.slotIndex(state, attestationSlot)
189
- attestationsSeen = addr pool.mapSlotsToAttestations[idx]
129
+ epochRef = pool.blockPool.dag.getEpochRef(blck, attestation.data.target.epoch)
130
+ attestationsSeen = addr pool.candidates[candidateIdx.get]
190
131
validation = Validation(
191
132
aggregation_bits: attestation.aggregation_bits,
192
133
aggregate_signature: attestation.signature)
193
134
participants = get_attesting_indices(
194
- state , attestation.data, validation.aggregation_bits, cache )
135
+ epochRef , attestation.data, validation.aggregation_bits)
195
136
196
137
var found = false
197
138
for a in attestationsSeen.attestations.mitems():
@@ -226,7 +167,6 @@ proc addResolved(pool: var AttestationPool, blck: BlockRef, attestation: Attesta
226
167
info " Attestation resolved" ,
227
168
attestation = shortLog(attestation),
228
169
validations = a.validations.len(),
229
- current_epoch = get_current_epoch(state),
230
170
blockSlot = shortLog(blck.slot)
231
171
232
172
found = true
@@ -244,11 +184,12 @@ proc addResolved(pool: var AttestationPool, blck: BlockRef, attestation: Attesta
244
184
245
185
info " Attestation resolved" ,
246
186
attestation = shortLog(attestation),
247
- current_epoch = get_current_epoch(state),
248
187
validations = 1 ,
249
188
blockSlot = shortLog(blck.slot)
250
189
251
- proc addAttestation*(pool: var AttestationPool, attestation: Attestation) =
190
+ proc addAttestation* (pool: var AttestationPool,
191
+ attestation: Attestation,
192
+ wallSlot: Slot) =
252
193
# # Add a verified attestation to the fork choice context
253
194
logScope: pcs = " atp_add_attestation"
254
195
@@ -261,15 +202,14 @@ proc addAttestation*(pool: var AttestationPool, attestation: Attestation) =
261
202
pool.addUnresolved(attestation)
262
203
return
263
204
264
- pool.addResolved(blck, attestation)
205
+ pool.addResolved(blck, attestation, wallSlot )
265
206
266
207
proc addForkChoice* (pool: var AttestationPool,
267
208
state: BeaconState,
268
209
blckRef: BlockRef,
269
210
blck: BeaconBlock,
270
211
wallSlot: Slot) =
271
212
# # Add a verified block to the fork choice context
272
- ## The current justifiedState of the block pool is used as reference
273
213
let state = pool.forkChoice.process_block(
274
214
pool.blockPool, state, blckRef, blck, wallSlot)
275
215
@@ -288,29 +228,17 @@ proc getAttestationsForSlot*(pool: AttestationPool, newBlockSlot: Slot):
288
228
newBlockSlot = shortLog(newBlockSlot)
289
229
return none(AttestationsSeen)
290
230
291
- if pool.mapSlotsToAttestations.len == 0: # startingSlot not set yet!
292
- info "No attestations found (pool empty)",
293
- newBlockSlot = shortLog(newBlockSlot)
294
- return none(AttestationsSeen)
295
-
296
231
let
297
- # TODO in theory we could include attestations from other slots also, but
298
- # we're currently not tracking which attestations have already been included
299
- # in blocks on the fork we're aiming for.. this is a conservative approach
300
- # that's guaranteed to not include any duplicates, because it's the first
301
- # time the attestations are up for inclusion!
302
232
attestationSlot = newBlockSlot - MIN_ATTESTATION_INCLUSION_DELAY
233
+ candidateIdx = pool.candidateIdx(attestationSlot)
303
234
304
- if attestationSlot < pool.startingSlot or
305
- attestationSlot >= pool.startingSlot + pool.mapSlotsToAttestations.lenu64:
235
+ if candidateIdx.isNone:
306
236
info " No attestations matching the slot range" ,
307
237
attestationSlot = shortLog(attestationSlot),
308
- startingSlot = shortLog(pool.startingSlot),
309
- endingSlot = shortLog(pool.startingSlot + pool.mapSlotsToAttestations.lenu64)
238
+ startingSlot = shortLog(pool.startingSlot)
310
239
return none(AttestationsSeen)
311
240
312
- let slotDequeIdx = int (attestationSlot - pool.startingSlot)
313
- some(pool.mapSlotsToAttestations[slotDequeIdx])
241
+ some(pool.candidates[candidateIdx.get()])
314
242
315
243
proc getAttestationsForBlock* (pool: AttestationPool,
316
244
state: BeaconState): seq [Attestation] =
@@ -337,8 +265,7 @@ proc getAttestationsForBlock*(pool: AttestationPool,
337
265
# addResolved, too, the new attestations get added to the end, while in
338
266
# these functions, it's reading from the beginning, et cetera. This all
339
267
# needs a single unified strategy.
340
- const LOOKBACK_WINDOW = 3
341
- for i in max(1, newBlockSlot.int64 - LOOKBACK_WINDOW) .. newBlockSlot.int64 :
268
+ for i in max(1 , newBlockSlot.int64 - ATTESTATION_LOOKBACK.int64 ) .. newBlockSlot.int64 :
342
269
let maybeSlotData = getAttestationsForSlot(pool, i.Slot)
343
270
if maybeSlotData.isSome:
344
271
insert(attestations, maybeSlotData.get.attestations)
@@ -390,7 +317,7 @@ proc getAttestationsForBlock*(pool: AttestationPool,
390
317
attestationSlot = newBlockSlot - 1
391
318
return
392
319
393
- proc resolve* (pool: var AttestationPool) =
320
+ proc resolve* (pool: var AttestationPool, wallSlot: Slot ) =
394
321
# # Check attestations in our unresolved deque
395
322
# # if they can be integrated to the fork choice
396
323
logScope: pcs = " atp_resolve"
@@ -412,7 +339,7 @@ proc resolve*(pool: var AttestationPool) =
412
339
pool.unresolved.del(k)
413
340
414
341
for a in resolved:
415
- pool.addResolved(a.blck, a.attestation)
342
+ pool.addResolved(a.blck, a.attestation, wallSlot )
416
343
417
344
proc selectHead* (pool: var AttestationPool, wallSlot: Slot): BlockRef =
418
345
let newHead = pool.forkChoice.find_head(wallSlot, pool.blockPool)
0 commit comments