nimbus-eth2/beacon_chain/consensus_object_pools/blob_quarantine.nim

103 lines
3.8 KiB
Nim

# beacon_chain
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [].}
import
std/tables,
../spec/helpers
from std/sequtils import mapIt
from std/strutils import join
const
MaxBlobs = 3 * SLOTS_PER_EPOCH * MAX_BLOBS_PER_BLOCK
## Same limit as `MaxOrphans` in `block_quarantine`;
## blobs may arrive before an orphan is tagged `blobless`
type
BlobQuarantine* = object
blobs*:
OrderedTable[(Eth2Digest, BlobIndex, KzgCommitment), ref BlobSidecar]
onBlobSidecarCallback*: OnBlobSidecarCallback
BlobFetchRecord* = object
block_root*: Eth2Digest
indices*: seq[BlobIndex]
OnBlobSidecarCallback = proc(data: BlobSidecar) {.gcsafe, raises: [].}
func shortLog*(x: seq[BlobIndex]): string =
"<" & x.mapIt($it).join(", ") & ">"
func shortLog*(x: seq[BlobFetchRecord]): string =
"[" & x.mapIt(shortLog(it.block_root) & shortLog(it.indices)).join(", ") & "]"
func put*(quarantine: var BlobQuarantine, blobSidecar: ref BlobSidecar) =
if quarantine.blobs.lenu64 >= MaxBlobs:
# FIFO if full. For example, sync manager and request manager can race to
# put blobs in at the same time, so one gets blob insert -> block resolve
# -> blob insert sequence, which leaves garbage blobs.
#
# This also therefore automatically garbage-collects otherwise valid garbage
# blobs which are correctly signed, point to either correct block roots or a
# block root which isn't ever seen, and then are for any reason simply never
# used.
var oldest_blob_key: (Eth2Digest, BlobIndex, KzgCommitment)
for k in quarantine.blobs.keys:
oldest_blob_key = k
break
quarantine.blobs.del oldest_blob_key
let block_root = hash_tree_root(blobSidecar.signed_block_header.message)
discard quarantine.blobs.hasKeyOrPut(
(block_root, blobSidecar.index, blobSidecar.kzg_commitment), blobSidecar)
func hasBlob*(
quarantine: BlobQuarantine,
slot: Slot,
proposer_index: uint64,
index: BlobIndex): bool =
for blob_sidecar in quarantine.blobs.values:
template block_header: untyped = blob_sidecar.signed_block_header.message
if block_header.slot == slot and
block_header.proposer_index == proposer_index and
blob_sidecar.index == index:
return true
false
func popBlobs*(
quarantine: var BlobQuarantine, digest: Eth2Digest,
blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock):
seq[ref BlobSidecar] =
var r: seq[ref BlobSidecar] = @[]
for idx, kzg_commitment in blck.message.body.blob_kzg_commitments:
var b: ref BlobSidecar
if quarantine.blobs.pop((digest, BlobIndex idx, kzg_commitment), b):
r.add(b)
r
func hasBlobs*(quarantine: BlobQuarantine,
blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): bool =
for idx, kzg_commitment in blck.message.body.blob_kzg_commitments:
if (blck.root, BlobIndex idx, kzg_commitment) notin quarantine.blobs:
return false
true
func blobFetchRecord*(quarantine: BlobQuarantine,
blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): BlobFetchRecord =
var indices: seq[BlobIndex]
for i in 0..<len(blck.message.body.blob_kzg_commitments):
let idx = BlobIndex(i)
if not quarantine.blobs.hasKey(
(blck.root, idx, blck.message.body.blob_kzg_commitments[i])):
indices.add(idx)
BlobFetchRecord(block_root: blck.root, indices: indices)
func init*(
T: type BlobQuarantine, onBlobSidecarCallback: OnBlobSidecarCallback): T =
T(onBlobSidecarCallback: onBlobSidecarCallback)