Fluffy state bridge updates (#2683)

* Use Json decode when handling portal rpc client error.

* Remove preimages cache.

* Support disabling state gossip in order to just build state.
This commit is contained in:
bhartnett 2024-10-03 10:57:19 +08:00 committed by GitHub
parent a03bb56bec
commit 9192aa13ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 66 deletions

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
std/strutils, json_serialization,
chronos, chronos,
stew/byteutils, stew/byteutils,
results, results,
@ -29,17 +29,26 @@ type
InvalidContentValue InvalidContentValue
ContentValidationFailed ContentValidationFailed
ErrorResponse = object
code*: int
message*: string
proc init*(T: type PortalRpcClient, rpcClient: RpcClient): T = proc init*(T: type PortalRpcClient, rpcClient: RpcClient): T =
T(rpcClient) T(rpcClient)
func toPortalRpcError(e: ref CatchableError): PortalRpcError = func toPortalRpcError(e: ref CatchableError): PortalRpcError =
# TODO: Update this to parse the error message json let error =
if e.msg.contains("-39001"): try:
Json.decode(e.msg, ErrorResponse)
except SerializationError as e:
raiseAssert(e.msg)
if error.code == -39001:
ContentNotFound ContentNotFound
elif e.msg.contains("-32602"): elif error.code == -32602:
InvalidContentKey InvalidContentKey
else: else:
raiseAssert(e.msg) # Shouldn't happen raiseAssert(e.msg)
proc historyLocalContent( proc historyLocalContent(
client: PortalRpcClient, contentKey: string client: PortalRpcClient, contentKey: string

View File

@ -146,11 +146,19 @@ type
.}: uint64 .}: uint64
verifyStateProofs* {. verifyStateProofs* {.
desc: "Verify state proofs before gossiping them into the portal network", desc:
"Verify state proofs before gossiping them into the portal network (Slow: Only used for testing).",
defaultValue: false, defaultValue: false,
name: "verify-state-proofs" name: "verify-state-proofs"
.}: bool .}: bool
enableGossip* {.
desc:
"Enable gossipping the state into the portal network. Disable to only build the state without gossiping it.",
defaultValue: true,
name: "enable-gossip"
.}: bool
gossipGenesis* {. gossipGenesis* {.
desc: desc:
"Enable gossip of the genesis state into the portal network when starting from block 1", "Enable gossip of the genesis state into the portal network when starting from block 1",

View File

@ -136,6 +136,7 @@ proc runBackfillBuildBlockOffersLoop(
blockDataQueue: AsyncQueue[BlockData], blockDataQueue: AsyncQueue[BlockData],
blockOffersQueue: AsyncQueue[BlockOffersRef], blockOffersQueue: AsyncQueue[BlockOffersRef],
verifyStateProofs: bool, verifyStateProofs: bool,
enableGossip: bool,
gossipGenesis: bool, gossipGenesis: bool,
) {.async: (raises: [CancelledError]).} = ) {.async: (raises: [CancelledError]).} =
info "Starting state backfill build block offers loop" info "Starting state backfill build block offers loop"
@ -163,7 +164,7 @@ proc runBackfillBuildBlockOffersLoop(
raiseAssert(e.msg) # Should never happen raiseAssert(e.msg) # Should never happen
ws.applyGenesisAccounts(genesisAccounts) ws.applyGenesisAccounts(genesisAccounts)
if gossipGenesis: if enableGossip and gossipGenesis:
let genesisBlockHash = let genesisBlockHash =
hash32"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" hash32"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
@ -213,6 +214,7 @@ proc runBackfillBuildBlockOffersLoop(
if verifyStateProofs: if verifyStateProofs:
worldState.verifyProofs(blockData.parentStateRoot, blockData.stateRoot) worldState.verifyProofs(blockData.parentStateRoot, blockData.stateRoot)
if enableGossip:
var builder = OffersBuilder.init(worldState, blockData.blockHash) var builder = OffersBuilder.init(worldState, blockData.blockHash)
builder.buildBlockOffers() builder.buildBlockOffers()
@ -350,12 +352,20 @@ proc runBackfillMetricsLoop(
while true: while true:
await sleepAsync(30.seconds) await sleepAsync(30.seconds)
if blockDataQueue.len() > 0:
info "Block data queue metrics: ", info "Block data queue metrics: ",
nextBlockNumber = blockDataQueue[0].blockNumber, nextBlockNumber = blockDataQueue[0].blockNumber,
blockDataQueueLen = blockDataQueue.len() blockDataQueueLen = blockDataQueue.len()
else:
info "Block data queue metrics: ", blockDataQueueLen = blockDataQueue.len()
if blockOffersQueue.len() > 0:
info "Block offers queue metrics: ", info "Block offers queue metrics: ",
nextBlockNumber = blockOffersQueue[0].blockNumber, nextBlockNumber = blockOffersQueue[0].blockNumber,
blockOffersQueueLen = blockOffersQueue.len() blockOffersQueueLen = blockOffersQueue.len()
else:
info "Block offers queue metrics: ", blockOffersQueueLen = blockOffersQueue.len()
proc runState*(config: PortalBridgeConf) = proc runState*(config: PortalBridgeConf) =
let let
@ -402,7 +412,8 @@ proc runState*(config: PortalBridgeConf) =
db, blockDataQueue, web3Client, config.startBlockNumber db, blockDataQueue, web3Client, config.startBlockNumber
) )
asyncSpawn runBackfillBuildBlockOffersLoop( asyncSpawn runBackfillBuildBlockOffersLoop(
db, blockDataQueue, blockOffersQueue, config.verifyStateProofs, config.gossipGenesis db, blockDataQueue, blockOffersQueue, config.verifyStateProofs, config.enableGossip,
config.gossipGenesis,
) )
for workerId in 1 .. config.gossipWorkersCount.int: for workerId in 1 .. config.gossipWorkersCount.int:

View File

@ -22,35 +22,33 @@ type AccountState* = ref object
code: seq[byte] code: seq[byte]
codeUpdated: bool codeUpdated: bool
proc init(T: type AccountState, account = newAccount()): T {.inline.} = proc init(T: type AccountState, account = newAccount()): T =
T(account: account, codeUpdated: false) T(account: account, codeUpdated: false)
proc getBalance*(accState: AccountState): UInt256 {.inline.} = template getBalance*(accState: AccountState): UInt256 =
accState.account.balance accState.account.balance
proc getNonce*(accState: AccountState): AccountNonce {.inline.} = template getNonce*(accState: AccountState): AccountNonce =
accState.account.nonce accState.account.nonce
proc setBalance*(accState: var AccountState, balance: UInt256) {.inline.} = template setBalance*(accState: var AccountState, bal: UInt256) =
accState.account.balance = balance accState.account.balance = bal
proc addBalance*(accState: var AccountState, balance: UInt256) {.inline.} = template addBalance*(accState: var AccountState, bal: UInt256) =
accState.account.balance += balance accState.account.balance += bal
proc setNonce*(accState: var AccountState, nonce: AccountNonce) {.inline.} = template setNonce*(accState: var AccountState, nce: AccountNonce) =
accState.account.nonce = nonce accState.account.nonce = nce
proc setStorage*( template setStorage*(accState: var AccountState, slotKey: UInt256, slotValue: UInt256) =
accState: var AccountState, slotKey: UInt256, slotValue: UInt256
) {.inline.} =
accState.storageUpdates[slotKey] = slotValue accState.storageUpdates[slotKey] = slotValue
proc deleteStorage*(accState: var AccountState, slotKey: UInt256) {.inline.} = template deleteStorage*(accState: var AccountState, slotKey: UInt256) =
# setting to zero has the effect of deleting the slot # setting to zero has the effect of deleting the slot
accState.setStorage(slotKey, 0.u256) accState.setStorage(slotKey, 0.u256)
proc setCode*(accState: var AccountState, code: seq[byte]) = template setCode*(accState: var AccountState, bytecode: seq[byte]) =
accState.code = code accState.code = bytecode
accState.codeUpdated = true accState.codeUpdated = true
# World State definition # World State definition
@ -65,7 +63,6 @@ type
storageTries: TableRef[AddressHash, HexaryTrie] storageTries: TableRef[AddressHash, HexaryTrie]
storageDb: TrieDatabaseRef storageDb: TrieDatabaseRef
bytecodeDb: TrieDatabaseRef # maps AddressHash -> seq[byte] bytecodeDb: TrieDatabaseRef # maps AddressHash -> seq[byte]
preimagesDb: TrieDatabaseRef # maps AddressHash -> EthAddress
proc init*( proc init*(
T: type WorldStateRef, db: DatabaseRef, accountsTrieRoot: KeccakHash = emptyRlpHash T: type WorldStateRef, db: DatabaseRef, accountsTrieRoot: KeccakHash = emptyRlpHash
@ -80,35 +77,17 @@ proc init*(
storageTries: newTable[AddressHash, HexaryTrie](), storageTries: newTable[AddressHash, HexaryTrie](),
storageDb: db.getStorageBackend(), storageDb: db.getStorageBackend(),
bytecodeDb: db.getBytecodeBackend(), bytecodeDb: db.getBytecodeBackend(),
preimagesDb: db.getPreimagesBackend(),
) )
proc stateRoot*(state: WorldStateRef): KeccakHash {.inline.} = template stateRoot*(state: WorldStateRef): KeccakHash =
state.accountsTrie.rootHash() state.accountsTrie.rootHash()
proc toAccountKey*(address: EthAddress): AddressHash {.inline.} = template toAccountKey*(address: EthAddress): AddressHash =
keccakHash(address) keccakHash(address)
proc toStorageKey*(slotKey: UInt256): SlotKeyHash {.inline.} = template toStorageKey*(slotKey: UInt256): SlotKeyHash =
keccakHash(toBytesBE(slotKey)) keccakHash(toBytesBE(slotKey))
proc getAccountPreimage(state: WorldStateRef, accountKey: AddressHash): EthAddress =
doAssert(
state.preimagesDb.contains(rlp.encode(accountKey)),
"No account preimage with address hash: " & $accountKey,
)
let addressBytes = state.preimagesDb.get(rlp.encode(accountKey))
try:
rlp.decode(addressBytes, EthAddress)
except RlpError as e:
raiseAssert(e.msg) # Should never happen
proc setAccountPreimage(
state: WorldStateRef, accountKey: AddressHash, address: EthAddress
) =
state.preimagesDb.put(rlp.encode(accountKey), rlp.encode(address))
proc getAccount(state: WorldStateRef, accountKey: AddressHash): AccountState = proc getAccount(state: WorldStateRef, accountKey: AddressHash): AccountState =
try: try:
if state.accountsTrie.contains(accountKey.data): if state.accountsTrie.contains(accountKey.data):
@ -119,12 +98,11 @@ proc getAccount(state: WorldStateRef, accountKey: AddressHash): AccountState =
except RlpError as e: except RlpError as e:
raiseAssert(e.msg) # should never happen unless the database is corrupted raiseAssert(e.msg) # should never happen unless the database is corrupted
proc getAccount*(state: WorldStateRef, address: EthAddress): AccountState {.inline.} = template getAccount*(state: WorldStateRef, address: EthAddress): AccountState =
state.getAccount(toAccountKey(address)) state.getAccount(toAccountKey(address))
proc setAccount*(state: WorldStateRef, address: EthAddress, accState: AccountState) = proc setAccount*(state: WorldStateRef, address: EthAddress, accState: AccountState) =
let accountKey = toAccountKey(address) let accountKey = toAccountKey(address)
state.setAccountPreimage(accountKey, address)
try: try:
if not state.storageTries.contains(accountKey): if not state.storageTries.contains(accountKey):
@ -198,12 +176,10 @@ iterator updatedStorageProofs*(
except RlpError as e: except RlpError as e:
raiseAssert(e.msg) # should never happen unless the database is corrupted raiseAssert(e.msg) # should never happen unless the database is corrupted
proc getUpdatedBytecode*( template getUpdatedBytecode*(state: WorldStateRef, accountKey: AddressHash): seq[byte] =
state: WorldStateRef, accountKey: AddressHash
): seq[byte] {.inline.} =
state.db.getBytecodeUpdatedCache().get(accountKey.data) state.db.getBytecodeUpdatedCache().get(accountKey.data)
# Slow: Used for testing only # Slow: Only used for testing
proc verifyProofs*( proc verifyProofs*(
state: WorldStateRef, preStateRoot: KeccakHash, expectedStateRoot: KeccakHash state: WorldStateRef, preStateRoot: KeccakHash, expectedStateRoot: KeccakHash
) = ) =