From 9192aa13ed100d70edbc185cd5c701c56ff2a052 Mon Sep 17 00:00:00 2001 From: bhartnett <51288821+bhartnett@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:57:19 +0800 Subject: [PATCH] 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. --- fluffy/rpc/portal_rpc_client.nim | 19 ++++-- .../portal_bridge/portal_bridge_conf.nim | 10 ++- .../portal_bridge/portal_bridge_state.nim | 45 +++++++++----- .../state_bridge/world_state.nim | 62 ++++++------------- 4 files changed, 70 insertions(+), 66 deletions(-) diff --git a/fluffy/rpc/portal_rpc_client.nim b/fluffy/rpc/portal_rpc_client.nim index eac6cc5fc..5ed73569e 100644 --- a/fluffy/rpc/portal_rpc_client.nim +++ b/fluffy/rpc/portal_rpc_client.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - std/strutils, + json_serialization, chronos, stew/byteutils, results, @@ -29,17 +29,26 @@ type InvalidContentValue ContentValidationFailed + ErrorResponse = object + code*: int + message*: string + proc init*(T: type PortalRpcClient, rpcClient: RpcClient): T = T(rpcClient) func toPortalRpcError(e: ref CatchableError): PortalRpcError = - # TODO: Update this to parse the error message json - if e.msg.contains("-39001"): + let error = + try: + Json.decode(e.msg, ErrorResponse) + except SerializationError as e: + raiseAssert(e.msg) + + if error.code == -39001: ContentNotFound - elif e.msg.contains("-32602"): + elif error.code == -32602: InvalidContentKey else: - raiseAssert(e.msg) # Shouldn't happen + raiseAssert(e.msg) proc historyLocalContent( client: PortalRpcClient, contentKey: string diff --git a/fluffy/tools/portal_bridge/portal_bridge_conf.nim b/fluffy/tools/portal_bridge/portal_bridge_conf.nim index f9ea85d63..935ac32ab 100644 --- a/fluffy/tools/portal_bridge/portal_bridge_conf.nim +++ b/fluffy/tools/portal_bridge/portal_bridge_conf.nim @@ -146,11 +146,19 @@ type .}: uint64 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, name: "verify-state-proofs" .}: 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* {. desc: "Enable gossip of the genesis state into the portal network when starting from block 1", diff --git a/fluffy/tools/portal_bridge/portal_bridge_state.nim b/fluffy/tools/portal_bridge/portal_bridge_state.nim index 0787f43fa..8a942df8c 100644 --- a/fluffy/tools/portal_bridge/portal_bridge_state.nim +++ b/fluffy/tools/portal_bridge/portal_bridge_state.nim @@ -136,6 +136,7 @@ proc runBackfillBuildBlockOffersLoop( blockDataQueue: AsyncQueue[BlockData], blockOffersQueue: AsyncQueue[BlockOffersRef], verifyStateProofs: bool, + enableGossip: bool, gossipGenesis: bool, ) {.async: (raises: [CancelledError]).} = info "Starting state backfill build block offers loop" @@ -163,7 +164,7 @@ proc runBackfillBuildBlockOffersLoop( raiseAssert(e.msg) # Should never happen ws.applyGenesisAccounts(genesisAccounts) - if gossipGenesis: + if enableGossip and gossipGenesis: let genesisBlockHash = hash32"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" @@ -213,17 +214,18 @@ proc runBackfillBuildBlockOffersLoop( if verifyStateProofs: worldState.verifyProofs(blockData.parentStateRoot, blockData.stateRoot) - var builder = OffersBuilder.init(worldState, blockData.blockHash) - builder.buildBlockOffers() + if enableGossip: + var builder = OffersBuilder.init(worldState, blockData.blockHash) + builder.buildBlockOffers() - await blockOffersQueue.addLast( - BlockOffersRef( - blockNumber: blockData.blockNumber, - accountTrieOffers: builder.getAccountTrieOffers(), - contractTrieOffers: builder.getContractTrieOffers(), - contractCodeOffers: builder.getContractCodeOffers(), + await blockOffersQueue.addLast( + BlockOffersRef( + blockNumber: blockData.blockNumber, + accountTrieOffers: builder.getAccountTrieOffers(), + contractTrieOffers: builder.getContractTrieOffers(), + contractCodeOffers: builder.getContractCodeOffers(), + ) ) - ) # After commit of the above db transaction which stores the updated account state # then we store the last persisted block number in the database so that we can use it @@ -350,12 +352,20 @@ proc runBackfillMetricsLoop( while true: await sleepAsync(30.seconds) - info "Block data queue metrics: ", - nextBlockNumber = blockDataQueue[0].blockNumber, - blockDataQueueLen = blockDataQueue.len() - info "Block offers queue metrics: ", - nextBlockNumber = blockOffersQueue[0].blockNumber, - blockOffersQueueLen = blockOffersQueue.len() + + if blockDataQueue.len() > 0: + info "Block data queue metrics: ", + nextBlockNumber = blockDataQueue[0].blockNumber, + blockDataQueueLen = blockDataQueue.len() + else: + info "Block data queue metrics: ", blockDataQueueLen = blockDataQueue.len() + + if blockOffersQueue.len() > 0: + info "Block offers queue metrics: ", + nextBlockNumber = blockOffersQueue[0].blockNumber, + blockOffersQueueLen = blockOffersQueue.len() + else: + info "Block offers queue metrics: ", blockOffersQueueLen = blockOffersQueue.len() proc runState*(config: PortalBridgeConf) = let @@ -402,7 +412,8 @@ proc runState*(config: PortalBridgeConf) = db, blockDataQueue, web3Client, config.startBlockNumber ) 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: diff --git a/fluffy/tools/portal_bridge/state_bridge/world_state.nim b/fluffy/tools/portal_bridge/state_bridge/world_state.nim index 38aed481e..49e3bbb6c 100644 --- a/fluffy/tools/portal_bridge/state_bridge/world_state.nim +++ b/fluffy/tools/portal_bridge/state_bridge/world_state.nim @@ -22,35 +22,33 @@ type AccountState* = ref object code: seq[byte] codeUpdated: bool -proc init(T: type AccountState, account = newAccount()): T {.inline.} = +proc init(T: type AccountState, account = newAccount()): T = T(account: account, codeUpdated: false) -proc getBalance*(accState: AccountState): UInt256 {.inline.} = +template getBalance*(accState: AccountState): UInt256 = accState.account.balance -proc getNonce*(accState: AccountState): AccountNonce {.inline.} = +template getNonce*(accState: AccountState): AccountNonce = accState.account.nonce -proc setBalance*(accState: var AccountState, balance: UInt256) {.inline.} = - accState.account.balance = balance +template setBalance*(accState: var AccountState, bal: UInt256) = + accState.account.balance = bal -proc addBalance*(accState: var AccountState, balance: UInt256) {.inline.} = - accState.account.balance += balance +template addBalance*(accState: var AccountState, bal: UInt256) = + accState.account.balance += bal -proc setNonce*(accState: var AccountState, nonce: AccountNonce) {.inline.} = - accState.account.nonce = nonce +template setNonce*(accState: var AccountState, nce: AccountNonce) = + accState.account.nonce = nce -proc setStorage*( - accState: var AccountState, slotKey: UInt256, slotValue: UInt256 -) {.inline.} = +template setStorage*(accState: var AccountState, slotKey: UInt256, slotValue: UInt256) = 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 accState.setStorage(slotKey, 0.u256) -proc setCode*(accState: var AccountState, code: seq[byte]) = - accState.code = code +template setCode*(accState: var AccountState, bytecode: seq[byte]) = + accState.code = bytecode accState.codeUpdated = true # World State definition @@ -65,7 +63,6 @@ type storageTries: TableRef[AddressHash, HexaryTrie] storageDb: TrieDatabaseRef bytecodeDb: TrieDatabaseRef # maps AddressHash -> seq[byte] - preimagesDb: TrieDatabaseRef # maps AddressHash -> EthAddress proc init*( T: type WorldStateRef, db: DatabaseRef, accountsTrieRoot: KeccakHash = emptyRlpHash @@ -80,35 +77,17 @@ proc init*( storageTries: newTable[AddressHash, HexaryTrie](), storageDb: db.getStorageBackend(), bytecodeDb: db.getBytecodeBackend(), - preimagesDb: db.getPreimagesBackend(), ) -proc stateRoot*(state: WorldStateRef): KeccakHash {.inline.} = +template stateRoot*(state: WorldStateRef): KeccakHash = state.accountsTrie.rootHash() -proc toAccountKey*(address: EthAddress): AddressHash {.inline.} = +template toAccountKey*(address: EthAddress): AddressHash = keccakHash(address) -proc toStorageKey*(slotKey: UInt256): SlotKeyHash {.inline.} = +template toStorageKey*(slotKey: UInt256): SlotKeyHash = 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 = try: if state.accountsTrie.contains(accountKey.data): @@ -119,12 +98,11 @@ proc getAccount(state: WorldStateRef, accountKey: AddressHash): AccountState = except RlpError as e: 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)) proc setAccount*(state: WorldStateRef, address: EthAddress, accState: AccountState) = let accountKey = toAccountKey(address) - state.setAccountPreimage(accountKey, address) try: if not state.storageTries.contains(accountKey): @@ -198,12 +176,10 @@ iterator updatedStorageProofs*( except RlpError as e: raiseAssert(e.msg) # should never happen unless the database is corrupted -proc getUpdatedBytecode*( - state: WorldStateRef, accountKey: AddressHash -): seq[byte] {.inline.} = +template getUpdatedBytecode*(state: WorldStateRef, accountKey: AddressHash): seq[byte] = state.db.getBytecodeUpdatedCache().get(accountKey.data) -# Slow: Used for testing only +# Slow: Only used for testing proc verifyProofs*( state: WorldStateRef, preStateRoot: KeccakHash, expectedStateRoot: KeccakHash ) =