portal_bridge history: fully support gossip of block by number (#2853)

+ some minor cleanup & reuse of code
This commit is contained in:
Kim De Mey 2024-11-20 20:07:36 +07:00 committed by GitHub
parent 01ca415721
commit 3ea5c531d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 80 additions and 53 deletions

View File

@ -13,7 +13,7 @@ import
stew/[endians2, io2, byteutils, arrayops], stew/[endians2, io2, byteutils, arrayops],
stint, stint,
snappy, snappy,
eth/common/[headers, blocks_rlp, receipts_rlp], eth/common/[headers_rlp, blocks_rlp, receipts_rlp],
beacon_chain/spec/beacon_time, beacon_chain/spec/beacon_time,
ssz_serialization, ssz_serialization,
ncli/e2store, ncli/e2store,
@ -184,7 +184,7 @@ proc fromCompressedRlpBytes(bytes: openArray[byte], T: type): Result[T, string]
try: try:
ok(rlp.decode(decodeFramed(bytes, checkIntegrity = false), T)) ok(rlp.decode(decodeFramed(bytes, checkIntegrity = false), T))
except RlpError as e: except RlpError as e:
err("Invalid Compressed RLP data" & e.msg) err("Invalid compressed RLP data for " & $T & ": " & e.msg)
proc init*(T: type Era1Group, f: IoHandle, startNumber: uint64): Result[T, string] = proc init*(T: type Era1Group, f: IoHandle, startNumber: uint64): Result[T, string] =
discard ?f.appendHeader(E2Version, 0) discard ?f.appendHeader(E2Version, 0)
@ -498,7 +498,7 @@ iterator era1BlockHeaders*(f: Era1File): headers.Header =
for blockNumber in startNumber .. endNumber: for blockNumber in startNumber .. endNumber:
let header = f.getBlockHeader(blockNumber).valueOr: let header = f.getBlockHeader(blockNumber).valueOr:
raiseAssert("Failed to read block header") raiseAssert("Failed to read block header: " & error)
yield header yield header
iterator era1BlockTuples*(f: Era1File): BlockTuple = iterator era1BlockTuples*(f: Era1File): BlockTuple =
@ -508,5 +508,5 @@ iterator era1BlockTuples*(f: Era1File): BlockTuple =
for blockNumber in startNumber .. endNumber: for blockNumber in startNumber .. endNumber:
let blockTuple = f.getBlockTuple(blockNumber).valueOr: let blockTuple = f.getBlockTuple(blockNumber).valueOr:
raiseAssert("Failed to read block header") raiseAssert("Failed to read block tuple: " & error)
yield blockTuple yield blockTuple

View File

@ -12,6 +12,8 @@ import
confutils, confutils,
confutils/std/net, confutils/std/net,
nimcrypto/hash, nimcrypto/hash,
../../network_metadata,
../../eth_data/era1,
../../[conf, logging] ../../[conf, logging]
export net export net
@ -36,6 +38,8 @@ proc defaultPortalBridgeStateDir*(): string =
else: else:
defaultDataDir() / "bridge" / "state" defaultDataDir() / "bridge" / "state"
const defaultEndEra* = uint64(era(network_metadata.mergeBlockNumber - 1))
type type
TrustedDigest* = MDigest[32 * 8] TrustedDigest* = MDigest[32 * 8]
@ -117,6 +121,13 @@ type
name: "backfill" name: "backfill"
.}: bool .}: bool
startEra* {.desc: "The era to start from", defaultValue: 0, name: "start-era".}:
uint64
endEra* {.
desc: "The era to stop at", defaultValue: defaultEndEra, name: "end-era"
.}: uint64
audit* {. audit* {.
desc: desc:
"Run pre-merge backfill in audit mode, which will only gossip content that if failed to fetch from the network", "Run pre-merge backfill in audit mode, which will only gossip content that if failed to fetch from the network",

View File

@ -140,7 +140,7 @@ proc getBlockReceipts(
proc gossipBlockHeader( proc gossipBlockHeader(
client: RpcClient, id: Hash32 | uint64, headerWithProof: BlockHeaderWithProof client: RpcClient, id: Hash32 | uint64, headerWithProof: BlockHeaderWithProof
): Future[Result[void, string]] {.async: (raises: []).} = ): Future[Result[void, string]] {.async: (raises: [CancelledError]).} =
let let
contentKey = blockHeaderContentKey(id) contentKey = blockHeaderContentKey(id)
encodedContentKeyHex = contentKey.encode.asSeq().toHex() encodedContentKeyHex = contentKey.encode.asSeq().toHex()
@ -154,13 +154,13 @@ proc gossipBlockHeader(
return err("JSON-RPC portal_historyGossip failed: " & $e.msg) return err("JSON-RPC portal_historyGossip failed: " & $e.msg)
info "Block header gossiped", peers, contentKey = encodedContentKeyHex info "Block header gossiped", peers, contentKey = encodedContentKeyHex
return ok() ok()
proc gossipBlockBody( proc gossipBlockBody(
client: RpcClient, client: RpcClient,
hash: Hash32, hash: Hash32,
body: PortalBlockBodyLegacy | PortalBlockBodyShanghai, body: PortalBlockBodyLegacy | PortalBlockBodyShanghai,
): Future[Result[void, string]] {.async: (raises: []).} = ): Future[Result[void, string]] {.async: (raises: [CancelledError]).} =
let let
contentKey = blockBodyContentKey(hash) contentKey = blockBodyContentKey(hash)
encodedContentKeyHex = contentKey.encode.asSeq().toHex() encodedContentKeyHex = contentKey.encode.asSeq().toHex()
@ -174,11 +174,11 @@ proc gossipBlockBody(
return err("JSON-RPC portal_historyGossip failed: " & $e.msg) return err("JSON-RPC portal_historyGossip failed: " & $e.msg)
info "Block body gossiped", peers, contentKey = encodedContentKeyHex info "Block body gossiped", peers, contentKey = encodedContentKeyHex
return ok() ok()
proc gossipReceipts( proc gossipReceipts(
client: RpcClient, hash: Hash32, receipts: PortalReceipts client: RpcClient, hash: Hash32, receipts: PortalReceipts
): Future[Result[void, string]] {.async: (raises: []).} = ): Future[Result[void, string]] {.async: (raises: [CancelledError]).} =
let let
contentKey = receiptsContentKey(hash) contentKey = receiptsContentKey(hash)
encodedContentKeyHex = contentKey.encode.asSeq().toHex() encodedContentKeyHex = contentKey.encode.asSeq().toHex()
@ -285,7 +285,7 @@ proc gossipHeadersWithProof(
era1File: string, era1File: string,
epochRecordFile: Opt[string] = Opt.none(string), epochRecordFile: Opt[string] = Opt.none(string),
verifyEra = false, verifyEra = false,
): Future[Result[void, string]] {.async: (raises: []).} = ): Future[Result[void, string]] {.async: (raises: [CancelledError]).} =
let f = ?Era1File.open(era1File) let f = ?Era1File.open(era1File)
if verifyEra: if verifyEra:
@ -296,54 +296,72 @@ proc gossipHeadersWithProof(
# UX hassle it adds to provide the accumulator ssz files. # UX hassle it adds to provide the accumulator ssz files.
let epochRecord = let epochRecord =
if epochRecordFile.isNone: if epochRecordFile.isNone:
info "Building accumulator from era1 file", era1File
?f.buildAccumulator() ?f.buildAccumulator()
else: else:
?readEpochRecordCached(epochRecordFile.get()) ?readEpochRecordCached(epochRecordFile.get())
for (contentKey, contentValue) in f.headersWithProof(epochRecord): info "Gossip headers from era1 file", era1File
let peers =
try:
await portalClient.portal_historyGossip(
contentKey.asSeq.toHex(), contentValue.toHex()
)
except CatchableError as e:
return err("JSON-RPC portal_historyGossip failed: " & $e.msg)
info "Block header gossiped", peers, contentKey
for blockHeader in f.era1BlockHeaders:
doAssert blockHeader.isPreMerge()
let
headerWithProof = buildHeaderWithProof(blockHeader, epochRecord).valueOr:
raiseAssert "Failed to build header with proof: " & $blockHeader.number
blockHash = blockHeader.rlpHash()
# gossip block header by hash
?(await portalClient.gossipBlockHeader(blockHash, headerWithProof))
# gossip block header by number
?(await portalClient.gossipBlockHeader(blockHeader.number, headerWithProof))
info "Succesfully gossiped headers from era1 file", era1File
ok() ok()
proc gossipBlockContent( proc gossipBlockContent(
portalClient: RpcClient, era1File: string, verifyEra = false portalClient: RpcClient, era1File: string, verifyEra = false
): Future[Result[void, string]] {.async: (raises: []).} = ): Future[Result[void, string]] {.async: (raises: [CancelledError]).} =
let f = ?Era1File.open(era1File) let f = ?Era1File.open(era1File)
if verifyEra: if verifyEra:
let _ = ?f.verify() let _ = ?f.verify()
for (contentKey, contentValue) in f.blockContent(): info "Gossip bodies and receipts from era1 file", era1File
let peers =
try:
await portalClient.portal_historyGossip(
contentKey.asSeq.toHex(), contentValue.toHex()
)
except CatchableError as e:
return err("JSON-RPC portal_historyGossip failed: " & $e.msg)
info "Block content gossiped", peers, contentKey
for (header, body, receipts, _) in f.era1BlockTuples:
let blockHash = header.rlpHash()
# gossip block body
?(
await portalClient.gossipBlockBody(
blockHash, PortalBlockBodyLegacy.fromBlockBody(body)
)
)
# gossip receipts
?(
await portalClient.gossipReceipts(
blockHash, PortalReceipts.fromReceipts(receipts)
)
)
info "Succesfully gossiped bodies and receipts from era1 file", era1File
ok() ok()
proc runBackfillLoop( proc runBackfillLoop(
portalClient: RpcClient, web3Client: RpcClient, era1Dir: string portalClient: RpcClient,
web3Client: RpcClient,
era1Dir: string,
startEra: uint64,
endEra: uint64,
) {.async: (raises: [CancelledError]).} = ) {.async: (raises: [CancelledError]).} =
let let accumulator = loadAccumulator()
rng = newRng()
accumulator = loadAccumulator() for era in startEra .. endEra:
while true:
let let
# Grab a random era1 to backfill
era = rng[].rand(int(era(network_metadata.mergeBlockNumber - 1)))
root = accumulator.historicalEpochs[era] root = accumulator.historicalEpochs[era]
eraFile = era1Dir / era1FileName("mainnet", Era1(era), Digest(data: root)) era1File = era1Dir / era1FileName("mainnet", Era1(era), Digest(data: root))
# Note: # Note:
# There are two design options here: # There are two design options here:
@ -360,40 +378,36 @@ proc runBackfillLoop(
# new era1 can be gossiped (might need another custom json-rpc that checks # new era1 can be gossiped (might need another custom json-rpc that checks
# the offer queue) # the offer queue)
when false: when false:
info "Gossip headers from era1 file", eraFile info "Gossip headers from era1 file", era1File
let headerRes = let headerRes =
try: try:
await portalClient.portal_debug_historyGossipHeaders(eraFile) await portalClient.portal_debug_historyGossipHeaders(era1File)
except CatchableError as e: except CatchableError as e:
error "JSON-RPC portal_debug_historyGossipHeaders failed", error = e.msg error "JSON-RPC portal_debug_historyGossipHeaders failed", error = e.msg
false false
if headerRes: if headerRes:
info "Gossip block content from era1 file", eraFile info "Gossip block content from era1 file", era1File
let res = let res =
try: try:
await portalClient.portal_debug_historyGossipBlockContent(eraFile) await portalClient.portal_debug_historyGossipBlockContent(era1File)
except CatchableError as e: except CatchableError as e:
error "JSON-RPC portal_debug_historyGossipBlockContent failed", error "JSON-RPC portal_debug_historyGossipBlockContent failed",
error = e.msg error = e.msg
false false
if res: if res:
error "Failed to gossip block content from era1 file", eraFile error "Failed to gossip block content from era1 file", era1File
else: else:
error "Failed to gossip headers from era1 file", eraFile error "Failed to gossip headers from era1 file", era1File
else: else:
info "Gossip headers from era1 file", eraFile (await portalClient.gossipHeadersWithProof(era1File)).isOkOr:
(await portalClient.gossipHeadersWithProof(eraFile)).isOkOr: error "Failed to gossip headers from era1 file", error, era1File
error "Failed to gossip headers from era1 file", error, eraFile
continue continue
info "Gossip block content from era1 file", eraFile (await portalClient.gossipBlockContent(era1File)).isOkOr:
(await portalClient.gossipBlockContent(eraFile)).isOkOr: error "Failed to gossip block content from era1 file", error, era1File
error "Failed to gossip block content from era1 file", error, eraFile
continue continue
info "Succesfully gossiped era1 file", eraFile
proc runBackfillLoopAuditMode( proc runBackfillLoopAuditMode(
portalClient: RpcClient, web3Client: RpcClient, era1Dir: string portalClient: RpcClient, web3Client: RpcClient, era1Dir: string
) {.async: (raises: [CancelledError]).} = ) {.async: (raises: [CancelledError]).} =
@ -548,7 +562,9 @@ proc runHistory*(config: PortalBridgeConf) =
portalClient, web3Client, config.era1Dir.string portalClient, web3Client, config.era1Dir.string
) )
else: else:
asyncSpawn runBackfillLoop(portalClient, web3Client, config.era1Dir.string) asyncSpawn runBackfillLoop(
portalClient, web3Client, config.era1Dir.string, config.startEra, config.endEra
)
while true: while true:
poll() poll()

2
vendor/nim-eth vendored

@ -1 +1 @@
Subproject commit 88e4be4dc40e044834dca68f8b69d144744bf145 Subproject commit dc092ca39303b030b42aa405e8d5f2f44f21b457