2024-03-22 18:01:22 +00:00
|
|
|
# Fluffy
|
|
|
|
# Copyright (c) 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: [].}
|
|
|
|
|
2024-07-18 09:01:40 +00:00
|
|
|
import
|
|
|
|
std/sequtils,
|
|
|
|
chronicles,
|
|
|
|
chronos,
|
|
|
|
stint,
|
|
|
|
web3/[eth_api, eth_api_types],
|
|
|
|
results,
|
|
|
|
eth/common/[eth_types, eth_types_rlp],
|
|
|
|
../../../nimbus/common/chain_config,
|
|
|
|
../../rpc/rpc_calls/rpc_trace_calls,
|
|
|
|
./state_bridge/[database, state_diff, world_state_helper],
|
|
|
|
./[portal_bridge_conf, portal_bridge_common]
|
|
|
|
|
|
|
|
type BlockData = object
|
|
|
|
blockNumber: uint64
|
|
|
|
blockObject: BlockObject
|
|
|
|
stateDiffs: seq[StateDiffRef]
|
|
|
|
uncleBlocks: seq[BlockObject]
|
|
|
|
|
|
|
|
proc runBackfillCollectBlockDataLoop(
|
|
|
|
blockDataQueue: AsyncQueue[BlockData],
|
|
|
|
web3Client: RpcClient,
|
|
|
|
startBlockNumber: uint64,
|
|
|
|
) {.async: (raises: [CancelledError]).} =
|
|
|
|
debug "Starting state backfill collect block data loop"
|
|
|
|
|
|
|
|
var currentBlockNumber = startBlockNumber
|
|
|
|
|
|
|
|
while true:
|
|
|
|
if currentBlockNumber mod 10000 == 0:
|
|
|
|
info "Collecting block data for block number: ", blockNumber = currentBlockNumber
|
|
|
|
|
|
|
|
let
|
|
|
|
blockId = blockId(currentBlockNumber)
|
|
|
|
blockRequest = web3Client.getBlockByNumber(blockId, false)
|
|
|
|
stateDiffsRequest = web3Client.getStateDiffsByBlockNumber(blockId)
|
|
|
|
|
|
|
|
blockObject = (await blockRequest).valueOr:
|
|
|
|
error "Failed to get block", error
|
|
|
|
await sleepAsync(1.seconds)
|
|
|
|
continue
|
|
|
|
|
|
|
|
var uncleBlockRequests: seq[Future[Result[BlockObject, string]]]
|
|
|
|
for i in 0 .. blockObject.uncles.high:
|
|
|
|
uncleBlockRequests.add(
|
|
|
|
web3Client.getUncleByBlockNumberAndIndex(blockId, i.Quantity)
|
|
|
|
)
|
|
|
|
|
|
|
|
let stateDiffs = (await stateDiffsRequest).valueOr:
|
|
|
|
error "Failed to get state diffs", error
|
|
|
|
await sleepAsync(1.seconds)
|
|
|
|
continue
|
|
|
|
|
|
|
|
var uncleBlocks: seq[BlockObject]
|
|
|
|
for uncleBlockRequest in uncleBlockRequests:
|
|
|
|
try:
|
|
|
|
let uncleBlock = (await uncleBlockRequest).valueOr:
|
|
|
|
error "Failed to get uncle blocks", error
|
|
|
|
await sleepAsync(1.seconds)
|
|
|
|
break
|
|
|
|
uncleBlocks.add(uncleBlock)
|
|
|
|
except CatchableError as e:
|
|
|
|
error "Failed to get uncleBlockRequest", error = e.msg
|
|
|
|
break
|
|
|
|
|
|
|
|
if uncleBlocks.len() < uncleBlockRequests.len():
|
|
|
|
continue
|
|
|
|
|
|
|
|
let blockData = BlockData(
|
|
|
|
blockNumber: currentBlockNumber,
|
|
|
|
blockObject: blockObject,
|
|
|
|
stateDiffs: stateDiffs,
|
|
|
|
uncleBlocks: uncleBlocks,
|
|
|
|
)
|
|
|
|
await blockDataQueue.addLast(blockData)
|
|
|
|
|
|
|
|
inc currentBlockNumber
|
|
|
|
|
|
|
|
proc runBackfillBuildStateLoop(
|
|
|
|
blockDataQueue: AsyncQueue[BlockData], stateDir: string
|
|
|
|
) {.async: (raises: [CancelledError]).} =
|
|
|
|
debug "Starting state backfill build state loop"
|
|
|
|
|
|
|
|
let db = DatabaseRef.init(stateDir).get()
|
|
|
|
defer:
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
let worldState = db.withTransaction:
|
|
|
|
let
|
|
|
|
# Requires an active transaction because it writes an emptyRlp node
|
|
|
|
# to the accounts HexaryTrie on initialization
|
|
|
|
ws = WorldStateRef.init(db)
|
|
|
|
genesisAccounts =
|
|
|
|
try:
|
|
|
|
genesisBlockForNetwork(MainNet).alloc
|
|
|
|
except CatchableError as e:
|
|
|
|
raiseAssert(e.msg) # Should never happen
|
|
|
|
ws.applyGenesisAccounts(genesisAccounts)
|
|
|
|
ws
|
|
|
|
|
|
|
|
while true:
|
|
|
|
let blockData = await blockDataQueue.popFirst()
|
|
|
|
|
|
|
|
if blockData.blockNumber mod 10000 == 0:
|
|
|
|
info "Building state for block number: ", blockNumber = blockData.blockNumber
|
|
|
|
|
|
|
|
db.withTransaction:
|
|
|
|
for stateDiff in blockData.stateDiffs:
|
|
|
|
worldState.applyStateDiff(stateDiff)
|
|
|
|
let
|
|
|
|
minerData =
|
|
|
|
(EthAddress(blockData.blockObject.miner), blockData.blockObject.number.uint64)
|
|
|
|
uncleMinersData =
|
|
|
|
blockData.uncleBlocks.mapIt((EthAddress(it.miner), it.number.uint64))
|
|
|
|
worldState.applyBlockRewards(minerData, uncleMinersData)
|
|
|
|
|
|
|
|
doAssert(blockData.blockObject.stateRoot.bytes() == worldState.stateRoot.data)
|
|
|
|
trace "State diffs successfully applied to block number:",
|
|
|
|
blockNumber = blockData.blockNumber
|
|
|
|
|
|
|
|
proc runBackfillMetricsLoop(
|
|
|
|
blockDataQueue: AsyncQueue[BlockData]
|
|
|
|
) {.async: (raises: [CancelledError]).} =
|
|
|
|
debug "Starting state backfill metrics loop"
|
|
|
|
|
|
|
|
while true:
|
|
|
|
await sleepAsync(10.seconds)
|
|
|
|
info "Block data queue length: ", queueLen = blockDataQueue.len()
|
2024-03-22 18:01:22 +00:00
|
|
|
|
|
|
|
proc runState*(config: PortalBridgeConf) =
|
|
|
|
let
|
2024-07-18 09:01:40 +00:00
|
|
|
#portalClient = newRpcClientConnect(config.portalRpcUrl)
|
|
|
|
web3Client = newRpcClientConnect(config.web3UrlState)
|
2024-03-22 18:01:22 +00:00
|
|
|
|
|
|
|
# TODO:
|
|
|
|
# Here we'd want to implement initially a loop that backfills the state
|
|
|
|
# content. Secondly, a loop that follows the head and injects the latest
|
|
|
|
# state changes too.
|
|
|
|
#
|
|
|
|
# The first step would probably be the easier one to start with, as one
|
|
|
|
# can start from genesis state.
|
|
|
|
# It could be implemented by using the `exp_getProofsByBlockNumber` JSON-RPC
|
|
|
|
# method from nimbus-eth1.
|
|
|
|
# It could also be implemented by having the whole state execution happening
|
|
|
|
# inside the bridge, and getting the blocks from era1 files.
|
2024-07-18 09:01:40 +00:00
|
|
|
|
|
|
|
if config.backfillState:
|
|
|
|
const startBlockNumber = 1
|
|
|
|
# This will become a parameter in the config once we can support it
|
|
|
|
info "Starting state backfill from block number: ", startBlockNumber
|
|
|
|
|
|
|
|
const bufferSize = 1000 # Should we make this configurable?
|
|
|
|
let blockDataQueue = newAsyncQueue[BlockData](bufferSize)
|
|
|
|
|
|
|
|
asyncSpawn runBackfillCollectBlockDataLoop(
|
|
|
|
blockDataQueue, web3Client, startBlockNumber
|
|
|
|
)
|
|
|
|
|
|
|
|
asyncSpawn runBackfillBuildStateLoop(blockDataQueue, config.stateDir.string)
|
|
|
|
|
|
|
|
asyncSpawn runBackfillMetricsLoop(blockDataQueue)
|
|
|
|
|
|
|
|
while true:
|
|
|
|
poll()
|