mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-10 14:26:26 +00:00
Add blobSidecar RPCs (#4677)
* Add blob_sidecars_by_range RPC * Add blob_sidecars_by_root RPC
This commit is contained in:
parent
14f4100e1b
commit
0a87889eaf
@ -71,6 +71,7 @@ type
|
||||
slot: Slot
|
||||
|
||||
BlockRootsList* = List[Eth2Digest, Limit MAX_REQUEST_BLOCKS]
|
||||
BlobIdentifierList* = List[BlobIdentifier, Limit (MAX_REQUEST_BLOBS_SIDECARS * MAX_BLOBS_PER_BLOCK)]
|
||||
|
||||
template readChunkPayload*(
|
||||
conn: Connection, peer: Peer, MsgType: type ForkySignedBeaconBlock):
|
||||
@ -155,6 +156,24 @@ proc readChunkPayload*(
|
||||
else:
|
||||
return neterr InvalidContextBytes
|
||||
|
||||
proc readChunkPayload*(
|
||||
conn: Connection, peer: Peer, MsgType: type (ref BlobSidecar)):
|
||||
Future[NetRes[MsgType]] {.async.} =
|
||||
var contextBytes: ForkDigest
|
||||
try:
|
||||
await conn.readExactly(addr contextBytes, sizeof contextBytes)
|
||||
except CatchableError:
|
||||
return neterr UnexpectedEOF
|
||||
|
||||
if contextBytes == peer.network.forkDigests.eip4844:
|
||||
let res = await readChunkPayload(conn, peer, BlobSidecar)
|
||||
if res.isOk:
|
||||
return ok newClone(res.get)
|
||||
else:
|
||||
return err(res.error)
|
||||
else:
|
||||
return neterr InvalidContextBytes
|
||||
|
||||
proc readChunkPayload*(
|
||||
conn: Connection, peer: Peer, MsgType: type SomeForkedLightClientObject):
|
||||
Future[NetRes[MsgType]] {.async.} =
|
||||
@ -517,6 +536,129 @@ p2pProtocol BeaconSync(version = 1,
|
||||
debug "BlobsSidecar range request done",
|
||||
peer, startSlot, count
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/deneb/p2p-interface.md#blobsidecarsbyroot-v1
|
||||
proc blobSidecarsByRoot(
|
||||
peer: Peer,
|
||||
blobIds: BlobIdentifierList,
|
||||
response: MultipleChunksResponse[
|
||||
ref BlobSidecar, Limit(MAX_REQUEST_BLOBS_SIDECARS * MAX_BLOBS_PER_BLOCK)])
|
||||
{.async, libp2pProtocol("blob_sidecars_by_root", 1).} =
|
||||
# TODO Semantically, this request should return a non-ref, but doing so
|
||||
# runs into extreme inefficiency due to the compiler introducing
|
||||
# hidden copies - in future nim versions with move support, this should
|
||||
# be revisited
|
||||
# TODO This code is more complicated than it needs to be, since the type
|
||||
# of the multiple chunks response is not actually used in this server
|
||||
# implementation (it's used to derive the signature of the client
|
||||
# function, not in the code below!)
|
||||
# TODO although you can't tell from this function definition, a magic
|
||||
# client call that returns `seq[ref BlobSidecar]` will
|
||||
# will be generated by the libp2p macro - we guarantee that seq items
|
||||
# are `not-nil` in the implementation
|
||||
trace "got blobs range request", peer, len = blobIds.len
|
||||
if blobIds.len == 0:
|
||||
raise newException(InvalidInputsError, "No blobs requested")
|
||||
|
||||
let
|
||||
dag = peer.networkState.dag
|
||||
count = blobIds.len
|
||||
|
||||
var
|
||||
found = 0
|
||||
bytes: seq[byte]
|
||||
|
||||
for i in 0..<count:
|
||||
let blockRef = dag.getBlockRef(blobIds[i].block_root).valueOr:
|
||||
continue
|
||||
let index = blobIds[i].index
|
||||
if dag.db.getBlobSidecarSZ(blockRef.bid.root, index, bytes):
|
||||
let uncompressedLen = uncompressedLenFramed(bytes).valueOr:
|
||||
warn "Cannot read blob size, database corrupt?",
|
||||
bytes = bytes.len(), blck = shortLog(blockRef), blobindex = index
|
||||
continue
|
||||
|
||||
peer.awaitQuota(blockResponseCost, "blob_sidecars_by_root/1")
|
||||
peer.network.awaitQuota(blockResponseCost, "blob_sidecars_by_root/1")
|
||||
|
||||
await response.writeBytesSZ(
|
||||
uncompressedLen, bytes,
|
||||
peer.networkState.forkDigestAtEpoch(blockRef.slot.epoch).data)
|
||||
inc found
|
||||
|
||||
debug "Blob root request done",
|
||||
peer, roots = blobIds.len, count, found
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/deneb/p2p-interface.md#blobsidecarsbyrange-v1
|
||||
proc blobSidecarsByRange(
|
||||
peer: Peer,
|
||||
startSlot: Slot,
|
||||
reqCount: uint64,
|
||||
response: MultipleChunksResponse[ref BlobSidecar, Limit(MAX_REQUEST_BLOBS_SIDECARS * MAX_BLOBS_PER_BLOCK)])
|
||||
{.async, libp2pProtocol("blob_sidecars_by_range", 1).} =
|
||||
# TODO This code is more complicated than it needs to be, since the type
|
||||
# of the multiple chunks response is not actually used in this server
|
||||
# implementation (it's used to derive the signature of the client
|
||||
# function, not in the code below!)
|
||||
# TODO although you can't tell from this function definition, a magic
|
||||
# client call that returns `seq[ref BlobSidecar]` will
|
||||
# will be generated by the libp2p macro - we guarantee that seq items
|
||||
# are `not-nil` in the implementation
|
||||
|
||||
trace "got blobs range request", peer, startSlot, count = reqCount
|
||||
if reqCount == 0:
|
||||
raise newException(InvalidInputsError, "Empty range requested")
|
||||
|
||||
let
|
||||
dag = peer.networkState.dag
|
||||
epochBoundary =
|
||||
if MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS >= dag.head.slot.epoch:
|
||||
GENESIS_EPOCH
|
||||
else:
|
||||
dag.head.slot.epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS
|
||||
|
||||
if startSlot.epoch < epochBoundary:
|
||||
raise newException(ResourceUnavailableError, BlobsOutOfRange)
|
||||
|
||||
var blockIds: array[MAX_REQUEST_BLOBS_SIDECARS, BlockId]
|
||||
let
|
||||
count = int min(reqCount, blockIds.lenu64)
|
||||
endIndex = count - 1
|
||||
startIndex =
|
||||
dag.getBlockRange(startSlot, 1, blockIds.toOpenArray(0, endIndex))
|
||||
|
||||
var
|
||||
found = 0
|
||||
bytes: seq[byte]
|
||||
|
||||
for i in startIndex..endIndex:
|
||||
for j in 0..<MAX_BLOBS_PER_BLOCK:
|
||||
if dag.db.getBlobSidecarSZ(blockIds[i].root, BlobIndex(j), bytes):
|
||||
# In general, there is not much intermediate time between post-merge
|
||||
# blocks all being optimistic and none of them being optimistic. The
|
||||
# EL catches up, tells the CL the head is verified, and that's it.
|
||||
if blockIds[i].slot.epoch >= dag.cfg.BELLATRIX_FORK_EPOCH and
|
||||
dag.is_optimistic(dag.head.root):
|
||||
continue
|
||||
|
||||
let uncompressedLen = uncompressedLenFramed(bytes).valueOr:
|
||||
warn "Cannot read blobs sidecar size, database corrupt?",
|
||||
bytes = bytes.len(), blck = shortLog(blockIds[i])
|
||||
continue
|
||||
|
||||
# TODO extract from libp2pProtocol
|
||||
peer.awaitQuota(blockResponseCost, "blobs_sidecars_by_range/1")
|
||||
peer.network.awaitQuota(blockResponseCost, "blobs_sidecars_by_range/1")
|
||||
|
||||
await response.writeBytesSZ(
|
||||
uncompressedLen, bytes,
|
||||
peer.networkState.forkDigestAtEpoch(blockIds[i].slot.epoch).data)
|
||||
inc found
|
||||
else:
|
||||
break
|
||||
|
||||
debug "BlobSidecar range request done",
|
||||
peer, startSlot, found
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/p2p-interface.md#getlightclientbootstrap
|
||||
proc lightClientBootstrap(
|
||||
peer: Peer,
|
||||
|
Loading…
x
Reference in New Issue
Block a user