diff --git a/hive_integration/nodocker/consensus/consensus_sim.nim b/hive_integration/nodocker/consensus/consensus_sim.nim index 2d0b1e25e2..aa63cc1651 100644 --- a/hive_integration/nodocker/consensus/consensus_sim.nim +++ b/hive_integration/nodocker/consensus/consensus_sim.nim @@ -35,7 +35,7 @@ proc processChainData(cd: ChainData): TestStatus = # bad blocks discard importRlpBlocks(bytes, c, finalize = true) - let head = com.db.getCanonicalHead() + let head = com.db.getCanonicalHead().expect("canonical head exists") let blockHash = "0x" & head.blockHash.data.toHex if blockHash == cd.lastBlockHash: TestStatus.OK diff --git a/hive_integration/nodocker/engine/engine_env.nim b/hive_integration/nodocker/engine/engine_env.nim index 2621b41a15..eaef107a4f 100644 --- a/hive_integration/nodocker/engine/engine_env.nim +++ b/hive_integration/nodocker/engine/engine_env.nim @@ -82,7 +82,7 @@ proc newEngineEnv*(conf: var NimbusConf, chainFile: string, enableAuth: bool): E let node = setupEthNode(conf, ctx) com = makeCom(conf) - head = com.db.getCanonicalHead() + head = com.db.getCanonicalHead().expect("canonical head exists") chain = newForkedChain(com, head) let txPool = TxPoolRef.new(com) diff --git a/hive_integration/nodocker/engine/node.nim b/hive_integration/nodocker/engine/node.nim index 390f9fafa8..cb7f806e31 100644 --- a/hive_integration/nodocker/engine/node.nim +++ b/hive_integration/nodocker/engine/node.nim @@ -102,18 +102,14 @@ proc setBlock*(c: ChainRef; blk: Block): Result[void, string] = _ = vmState.parent.stateRoot # Check point ? vmState.processBlock(blk) - if not c.db.persistHeader( - header, c.com.proofOfStake(header), c.com.startOfHistory): - return err("Could not persist header") - - try: - c.db.persistTransactions(header.number, header.txRoot, blk.transactions) - c.db.persistReceipts(header.receiptsRoot, vmState.receipts) - - if blk.withdrawals.isSome: - c.db.persistWithdrawals(header.withdrawalsRoot.get, blk.withdrawals.get) - except CatchableError as exc: - return err(exc.msg) + ? c.db.persistHeader( + header, c.com.proofOfStake(header), c.com.startOfHistory) + + c.db.persistTransactions(header.number, header.txRoot, blk.transactions) + c.db.persistReceipts(header.receiptsRoot, vmState.receipts) + + if blk.withdrawals.isSome: + c.db.persistWithdrawals(header.withdrawalsRoot.get, blk.withdrawals.get) # update currentBlock *after* we persist it # so the rpc return consistent result diff --git a/hive_integration/nodocker/pyspec/test_env.nim b/hive_integration/nodocker/pyspec/test_env.nim index bced73a2da..c6f308d08a 100644 --- a/hive_integration/nodocker/pyspec/test_env.nim +++ b/hive_integration/nodocker/pyspec/test_env.nim @@ -46,10 +46,10 @@ proc setupELClient*(conf: ChainConfig, node: JsonNode): TestEnv = stateDB.persist() doAssert stateDB.getStateRoot == genesisHeader.stateRoot - doAssert com.db.persistHeader(genesisHeader, - com.proofOfStake(genesisHeader)) - doAssert(com.db.getCanonicalHead().blockHash == - genesisHeader.blockHash) + com.db.persistHeader(genesisHeader, + com.proofOfStake(genesisHeader)).expect("persistHeader no error") + let head = com.db.getCanonicalHead().expect("canonical head exists") + doAssert(head.blockHash == genesisHeader.blockHash) let txPool = TxPoolRef.new(com) diff --git a/hive_integration/nodocker/rpc/test_env.nim b/hive_integration/nodocker/rpc/test_env.nim index dc22fb78aa..4019b16202 100644 --- a/hive_integration/nodocker/rpc/test_env.nim +++ b/hive_integration/nodocker/rpc/test_env.nim @@ -86,7 +86,7 @@ proc setupEnv*(): TestEnv = manageAccounts(ethCtx, conf) - let head = com.db.getCanonicalHead() + let head = com.db.getCanonicalHead().expect("canonical head exists") let chainRef = newForkedChain(com, head) let txPool = TxPoolRef.new(com) diff --git a/nimbus/beacon/api_handler/api_exchangeconf.nim b/nimbus/beacon/api_handler/api_exchangeconf.nim index 2a34a741a7..0b54bc50ab 100644 --- a/nimbus/beacon/api_handler/api_exchangeconf.nim +++ b/nimbus/beacon/api_handler/api_exchangeconf.nim @@ -43,20 +43,17 @@ proc exchangeConf*(ben: BeaconEngineRef, terminalBlockHash = conf.terminalBlockHash if terminalBlockHash != default(Hash32): - var headerHash: Hash32 - - if not db.getBlockHash(terminalBlockNumber, headerHash): - raise newException(ValueError, "cannot get terminal block hash, number $1" % - [$terminalBlockNumber]) + let headerHash = db.getBlockHash(terminalBlockNumber).valueOr: + raise newException(ValueError, "cannot get terminal block hash, number $1, msg: $2" % + [$terminalBlockNumber, error]) if terminalBlockHash != headerHash: raise newException(ValueError, "invalid terminal block hash, got $1 want $2" % [$terminalBlockHash, $headerHash]) - var header: Header - if not db.getBlockHeader(headerHash, header): - raise newException(ValueError, "cannot get terminal block header, hash $1" % - [$terminalBlockHash]) + let header = db.getBlockHeader(headerHash).valueOr: + raise newException(ValueError, "cannot get terminal block header, hash $1, msg: $2" % + [$terminalBlockHash, error]) return TransitionConfigurationV1( terminalTotalDifficulty: ttd.get, diff --git a/nimbus/beacon/api_handler/api_forkchoice.nim b/nimbus/beacon/api_handler/api_forkchoice.nim index 5e98b13a7c..b48085c172 100644 --- a/nimbus/beacon/api_handler/api_forkchoice.nim +++ b/nimbus/beacon/api_handler/api_forkchoice.nim @@ -124,11 +124,12 @@ proc forkchoiceUpdated*(ben: BeaconEngineRef, if apiVersion == Version.V1: let blockNumber = header.number if header.difficulty > 0.u256 or blockNumber == 0'u64: - var - td, ptd: DifficultyInt + let + td = db.getScore(blockHash) + ptd = db.getScore(header.parentHash) ttd = com.ttd.get(high(UInt256)) - if not db.getTd(blockHash, td) or (blockNumber > 0'u64 and not db.getTd(header.parentHash, ptd)): + if td.isNone or (blockNumber > 0'u64 and ptd.isNone): error "TDs unavailable for TTD check", number = blockNumber, hash = blockHash.short, @@ -137,12 +138,12 @@ proc forkchoiceUpdated*(ben: BeaconEngineRef, ptd = ptd return simpleFCU(PayloadExecutionStatus.invalid, "TDs unavailable for TTD check") - if td < ttd or (blockNumber > 0'u64 and ptd > ttd): + if td.get < ttd or (blockNumber > 0'u64 and ptd.get > ttd): notice "Refusing beacon update to pre-merge", number = blockNumber, hash = blockHash.short, diff = header.difficulty, - ptd = ptd, + ptd = ptd.get, ttd = ttd return invalidFCU("Refusing beacon update to pre-merge") diff --git a/nimbus/beacon/api_handler/api_getbodies.nim b/nimbus/beacon/api_handler/api_getbodies.nim index 457290e7b6..d43985fbf9 100644 --- a/nimbus/beacon/api_handler/api_getbodies.nim +++ b/nimbus/beacon/api_handler/api_getbodies.nim @@ -25,8 +25,7 @@ proc getPayloadBodyByHeader(db: CoreDbRef, header: Header, output: var seq[Opt[ExecutionPayloadBodyV1]]) {.raises:[].} = - var body: BlockBody - if not db.getBlockBody(header, body): + let body = db.getBlockBody(header).valueOr: output.add Opt.none(ExecutionPayloadBodyV1) return @@ -93,7 +92,6 @@ proc getPayloadBodiesByRange*(ben: BeaconEngineRef, var last = start+count-1 - header: Header if start > ben.chain.latestNumber: # requested range beyond the latest known block @@ -104,7 +102,7 @@ proc getPayloadBodiesByRange*(ben: BeaconEngineRef, # get bodies from database for bn in start..ben.chain.baseNumber: - if not db.getBlockHeader(bn, header): + let header = db.getBlockHeader(bn).valueOr: result.add Opt.none(ExecutionPayloadBodyV1) continue db.getPayloadBodyByHeader(header, result) diff --git a/nimbus/beacon/api_handler/api_utils.nim b/nimbus/beacon/api_handler/api_utils.nim index 08084da350..ba69235de0 100644 --- a/nimbus/beacon/api_handler/api_utils.nim +++ b/nimbus/beacon/api_handler/api_utils.nim @@ -9,7 +9,6 @@ import std/[typetraits, strutils], - eth/rlp, json_rpc/errors, nimcrypto/sha2, stew/endians2, @@ -173,28 +172,25 @@ proc tooLargeRequest*(msg: string): ref InvalidRequest = ) proc latestValidHash*(db: CoreDbRef, - parent: common.Header, - ttd: DifficultyInt): common.Hash32 = + parent: Header, + ttd: DifficultyInt): Hash32 = if parent.isGenesis: - return default(common.Hash32) + return default(Hash32) let ptd = db.getScore(parent.parentHash).valueOr(0.u256) if ptd >= ttd: parent.blockHash else: # If the most recent valid ancestor is a PoW block, # latestValidHash MUST be set to ZERO - default(common.Hash32) + default(Hash32) proc invalidFCU*(validationError: string, com: CommonRef, header: common.Header): ForkchoiceUpdatedResponse = - var parent: common.Header - if not com.db.getBlockHeader(header.parentHash, parent): + let parent = com.db.getBlockHeader(header.parentHash).valueOr: return invalidFCU(validationError) - let blockHash = try: + let blockHash = latestValidHash(com.db, parent, com.ttd.get(high(UInt256))) - except RlpError: - default(common.Hash32) invalidFCU(validationError, blockHash) diff --git a/nimbus/beacon/beacon_engine.nim b/nimbus/beacon/beacon_engine.nim index a5f4a05aba..c8168041bd 100644 --- a/nimbus/beacon/beacon_engine.nim +++ b/nimbus/beacon/beacon_engine.nim @@ -196,6 +196,13 @@ func setInvalidAncestor*(ben: BeaconEngineRef, header: Header, blockHash: Hash32 # bad ancestor. If yes, it constructs the payload failure response to return. proc checkInvalidAncestor*(ben: BeaconEngineRef, check, head: Hash32): Opt[PayloadStatusV1] = + proc latestValidHash(db: CoreDbRef, invalid: auto): Hash32 = + let parent = db.getBlockHeader(invalid.parentHash).valueOr: + return invalid.parentHash + if parent.difficulty != 0.u256: + return default(Hash32) + invalid.parentHash + # If the hash to check is unknown, return valid ben.invalidTipsets.withValue(check, invalid) do: # If the bad hash was hit too many times, evict it and try to reprocess in @@ -236,16 +243,9 @@ proc checkInvalidAncestor*(ben: BeaconEngineRef, ben.invalidTipsets[head] = invalid[] - var lastValid = invalid.parentHash - # If the last valid hash is the terminal pow block, return 0x0 for latest valid hash - var header: Header - if ben.com.db.getBlockHeader(invalid.parentHash, header): - if header.difficulty != 0.u256: - lastValid = default(Hash32) - + let lastValid = latestValidHash(ben.com.db, invalid) return Opt.some invalidStatus(lastValid, "links to previously rejected block") - do: return Opt.none(PayloadStatusV1) diff --git a/nimbus/common/common.nim b/nimbus/common/common.nim index e6722f997a..7570261a8c 100644 --- a/nimbus/common/common.nim +++ b/nimbus/common/common.nim @@ -137,36 +137,27 @@ proc initializeDb(com: CommonRef) = nonce = com.genesisHeader.nonce doAssert(com.genesisHeader.number == 0.BlockNumber, "can't commit genesis block with number > 0") - doAssert(com.db.persistHeader(com.genesisHeader, + com.db.persistHeader(com.genesisHeader, com.proofOfStake(com.genesisHeader), - startOfHistory=com.genesisHeader.parentHash), - "can persist genesis header") + startOfHistory=com.genesisHeader.parentHash). + expect("can persist genesis header") doAssert(canonicalHeadHashKey().toOpenArray in kvt) # The database must at least contain the base and head pointers - the base # is implicitly considered finalized let baseNum = com.db.getSavedStateBlockNumber() - base = - try: - com.db.getBlockHeader(baseNum) - except BlockNotFound as exc: - fatal "Cannot load base block header", - baseNum, err = exc.msg - quit 1 - finalized = - try: - com.db.finalizedHeader() - except BlockNotFound: - debug "No finalized block stored in database, reverting to base" - base - head = - try: - com.db.getCanonicalHead() - except EVMError as exc: - fatal "Cannot load canonical block header", - err = exc.msg - quit 1 + base = com.db.getBlockHeader(baseNum).valueOr: + fatal "Cannot load base block header", + baseNum, err = error + quit 1 + finalized = com.db.finalizedHeader().valueOr: + debug "No finalized block stored in database, reverting to base" + base + head = com.db.getCanonicalHead().valueOr: + fatal "Cannot load canonical block header", + err = error + quit 1 info "Database initialized", base = (base.blockHash, base.number), @@ -204,8 +195,8 @@ proc init(com : CommonRef, fork = toHardFork(com.forkTransitionTable, forkDeterminer) # Must not overwrite the global state on the single state DB - if not db.getBlockHeader(0.BlockNumber, com.genesisHeader): - com.genesisHeader = toGenesisHeader(genesis, fork, com.db) + com.genesisHeader = db.getBlockHeader(0.BlockNumber).valueOr: + toGenesisHeader(genesis, fork, com.db) com.setForkId(com.genesisHeader) com.pos.timestamp = genesis.timestamp diff --git a/nimbus/core/block_import.nim b/nimbus/core/block_import.nim index 4f80307feb..54e98e3f82 100644 --- a/nimbus/core/block_import.nim +++ b/nimbus/core/block_import.nim @@ -49,9 +49,8 @@ proc importRlpBlocks*(importFile: string, importRlpBlocks(bytes, chain, finalize) proc importRlpBlocks*(conf: NimbusConf, com: CommonRef) = - var head: Header - if not com.db.getCanonicalHead(head): - error "cannot get canonical head from db" + let head = com.db.getCanonicalHead().valueOr: + error "cannot get canonical head from db", msg=error quit(QuitFailure) let chain = newForkedChain(com, head, baseDistance = 0) diff --git a/nimbus/core/chain/chain_desc.nim b/nimbus/core/chain/chain_desc.nim index 3df56ee904..1eb87a2a03 100644 --- a/nimbus/core/chain/chain_desc.nim +++ b/nimbus/core/chain/chain_desc.nim @@ -55,16 +55,13 @@ func newChain*(com: CommonRef, proc newChain*(com: CommonRef): ChainRef = ## Constructor for the `Chain` descriptor object. All sub-object descriptors ## are initialised with defaults. So is extra block chain validation - try: - let header = com.db.getCanonicalHead() - let extraValidation = com.proofOfStake(header) - return ChainRef( - com: com, - extraValidation: extraValidation, - ) - except CatchableError: - doAssert(false, "no canonical head") - + let header = com.db.getCanonicalHead().expect("canonical head exists") + let extraValidation = com.proofOfStake(header) + return ChainRef( + com: com, + extraValidation: extraValidation, + ) + # ------------------------------------------------------------------------------ # Public `Chain` getters # ------------------------------------------------------------------------------ @@ -88,8 +85,7 @@ func verifyFrom*(c: ChainRef): BlockNumber = ## Getter c.verifyFrom -proc currentBlock*(c: ChainRef): Header - {.gcsafe, raises: [CatchableError].} = +proc currentBlock*(c: ChainRef): Result[Header, string] = ## currentBlock retrieves the current head block of the canonical chain. ## Ideally the block should be retrieved from the blockchain's internal cache. ## but now it's enough to retrieve it from database diff --git a/nimbus/core/chain/forked_chain.nim b/nimbus/core/chain/forked_chain.nim index 64b528c40e..997d3fbb7d 100644 --- a/nimbus/core/chain/forked_chain.nim +++ b/nimbus/core/chain/forked_chain.nim @@ -89,11 +89,10 @@ proc processBlock(c: ForkedChainRef, # We still need to write header to database # because validateUncles still need it let blockHash = header.blockHash() - if not c.db.persistHeader( - blockHash, - header, - c.com.startOfHistory): - return err("Could not persist header") + ?c.db.persistHeader( + blockHash, + header, + c.com.startOfHistory) # update currentBlock *after* we persist it # so the rpc return consistent result @@ -423,14 +422,8 @@ proc init*( ## let base = com.db.getSavedStateBlockNumber - var - baseHash: Hash32 - baseHeader: Header - try: - baseHash = com.db.getBlockHash(base) - baseHeader = com.db.getBlockHeader(baseHash) - except BlockNotFound: - raiseAssert "Base header missing for #" & $base + baseHash = com.db.getBlockHash(base).expect("baseHash exists") + baseHeader = com.db.getBlockHeader(baseHash).expect("base header exists") # update global syncStart com.syncStart = baseHeader.number @@ -453,8 +446,7 @@ proc newForkedChain*(com: CommonRef, ## for some particular test or other applications. Otherwise consider ## `init()`. let baseHash = baseHeader.blockHash - - var chain = ForkedChainRef( + let chain = ForkedChainRef( com: com, db : com.db, baseHeader : baseHeader, @@ -652,16 +644,12 @@ proc latestBlock*(c: ForkedChainRef): Block = return val.blk do: # This can happen if block pointed by cursorHash is not loaded yet - try: - result = c.db.getEthBlock(c.cursorHash) - c.blocks[c.cursorHash] = BlockDesc( - blk: result, - receipts: c.db.getReceipts(result.header.receiptsRoot), - ) - except BlockNotFound: - doAssert(false, "Block should exists in database") - except RlpError: - doAssert(false, "Receipts should exists in database") + result = c.db.getEthBlock(c.cursorHash).expect("cursorBlock exists") + c.blocks[c.cursorHash] = BlockDesc( + blk: result, + receipts: c.db.getReceipts(result.header.receiptsRoot). + expect("receipts exists"), + ) proc headerByNumber*(c: ForkedChainRef, number: BlockNumber): Result[Header, string] = if number > c.cursorHeader.number: @@ -674,11 +662,7 @@ proc headerByNumber*(c: ForkedChainRef, number: BlockNumber): Result[Header, str return ok(c.baseHeader) if number < c.baseHeader.number: - var header: Header - if c.db.getBlockHeader(number, header): - return ok(header) - else: - return err("Failed to get header with number: " & $number) + return c.db.getBlockHeader(number) shouldNotKeyError: var prevHash = c.cursorHeader.parentHash @@ -696,38 +680,23 @@ proc headerByHash*(c: ForkedChainRef, blockHash: Hash32): Result[Header, string] do: if c.baseHash == blockHash: return ok(c.baseHeader) - var header: Header - if c.db.getBlockHeader(blockHash, header): - return ok(header) - return err("Failed to get header with hash: " & $blockHash) + return c.db.getBlockHeader(blockHash) -proc blockByHash*(c: ForkedChainRef, blockHash: Hash32): Opt[Block] = +proc blockByHash*(c: ForkedChainRef, blockHash: Hash32): Result[Block, string] = # used by getPayloadBodiesByHash # https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.4/src/engine/shanghai.md#specification-3 # 4. Client software MAY NOT respond to requests for finalized blocks by hash. c.blocks.withValue(blockHash, val) do: - return Opt.some(val.blk) + return ok(val.blk) do: - var - header: Header - body: BlockBody - if c.db.getBlockHeader(blockHash, header) and c.db.getBlockBody(blockHash, body): - return ok(Block.init(move(header), move(body))) - else: - return Opt.none(Block) + return c.db.getEthBlock(blockHash) proc blockByNumber*(c: ForkedChainRef, number: BlockNumber): Result[Block, string] = if number > c.cursorHeader.number: return err("Requested block number not exists: " & $number) if number < c.baseHeader.number: - var - header: Header - body: BlockBody - if c.db.getBlockHeader(number, header) and c.db.getBlockBody(header, body): - return ok(Block.init(move(header), move(body))) - else: - return err("Failed to get block with number: " & $number) + return c.db.getEthBlock(number) shouldNotKeyError: var prevHash = c.cursorHash @@ -782,5 +751,6 @@ proc isCanonicalAncestor*(c: ForkedChainRef, # canonical chain in database should have a marker # and the marker is block number - var canonHash: common.Hash32 - c.db.getBlockHash(blockNumber, canonHash) and canonHash == blockHash + let canonHash = c.db.getBlockHash(blockNumber).valueOr: + return false + canonHash == blockHash diff --git a/nimbus/core/chain/persist_blocks.nim b/nimbus/core/chain/persist_blocks.nim index 3f09eca95e..5a58c60d3c 100644 --- a/nimbus/core/chain/persist_blocks.nim +++ b/nimbus/core/chain/persist_blocks.nim @@ -156,10 +156,9 @@ proc persistBlocksImpl( let blockHash = header.blockHash() if NoPersistHeader notin flags: - if not c.db.persistHeader( - blockHash, header, c.com.proofOfStake(header), c.com.startOfHistory - ): - return err("Could not persist header") + ?c.db.persistHeader( + blockHash, header, + c.com.proofOfStake(header), c.com.startOfHistory) if NoPersistTransactions notin flags: c.db.persistTransactions(header.number, header.txRoot, blk.transactions) @@ -205,38 +204,22 @@ proc persistBlocksImpl( proc insertBlockWithoutSetHead*(c: ChainRef, blk: Block): Result[void, string] = discard ?c.persistBlocksImpl([blk], {NoPersistHeader, NoPersistReceipts}) - - if not c.db.persistHeader(blk.header.blockHash, blk.header, c.com.startOfHistory): - return err("Could not persist header") - - ok() - + c.db.persistHeader(blk.header.blockHash, blk.header, c.com.startOfHistory) + proc setCanonical*(c: ChainRef, header: Header): Result[void, string] = if header.parentHash == default(Hash32): - if not c.db.setHead(header): - return err("setHead failed") - return ok() - - var body: BlockBody - if not c.db.getBlockBody(header, body): - debug "Failed to get BlockBody", hash = header.blockHash - return err("Could not get block body") - + return c.db.setHead(header) + + var body = ?c.db.getBlockBody(header) discard ?c.persistBlocksImpl( [Block.init(header, move(body))], {NoPersistHeader, NoPersistTransactions} ) - if not c.db.setHead(header): - return err("setHead failed") - ok() - + c.db.setHead(header) + proc setCanonical*(c: ChainRef, blockHash: Hash32): Result[void, string] = - var header: Header - if not c.db.getBlockHeader(blockHash, header): - debug "Failed to get Header", hash = blockHash - return err("Could not get block header") - + let header = ?c.db.getBlockHeader(blockHash) setCanonical(c, header) proc persistBlocks*( diff --git a/nimbus/core/executor/process_block.nim b/nimbus/core/executor/process_block.nim index d500f5fbdd..dc2a03bad6 100644 --- a/nimbus/core/executor/process_block.nim +++ b/nimbus/core/executor/process_block.nim @@ -170,8 +170,8 @@ proc procBlkEpilogue( blockNumber = header.number, expected = header.stateRoot, actual = stateRoot, - arrivedFrom = vmState.com.db.getCanonicalHead().stateRoot - return err("stateRoot mismatch, expect: " & + arrivedFrom = vmState.parent.stateRoot + return err("stateRoot mismatch, expect: " & $header.stateRoot & ", got: " & $stateRoot) if not skipReceipts: diff --git a/nimbus/core/tx_pool.nim b/nimbus/core/tx_pool.nim index 2f058b742a..ad87958bba 100644 --- a/nimbus/core/tx_pool.nim +++ b/nimbus/core/tx_pool.nim @@ -390,7 +390,7 @@ proc setHead(xp: TxPoolRef; val: Header) # ------------------------------------------------------------------------------ proc new*(T: type TxPoolRef; com: CommonRef): T - {.gcsafe,raises: [CatchableError].} = + {.gcsafe,raises: [].} = ## Constructor, returns a new tx-pool descriptor. new result result.init(com) diff --git a/nimbus/core/tx_pool/tx_desc.nim b/nimbus/core/tx_pool/tx_desc.nim index 9ec323e167..5e981b5174 100644 --- a/nimbus/core/tx_pool/tx_desc.nim +++ b/nimbus/core/tx_pool/tx_desc.nim @@ -137,12 +137,12 @@ proc update(xp: TxPoolRef; parent: Header) = # Public functions, constructor # ------------------------------------------------------------------------------ -proc init*(xp: TxPoolRef; com: CommonRef) - {.gcsafe,raises: [CatchableError].} = +proc init*(xp: TxPoolRef; com: CommonRef) = ## Constructor, returns new tx-pool descriptor. xp.startDate = getTime().utc.toTime - xp.vmState = setupVMState(com, com.db.getCanonicalHead) + let head = com.db.getCanonicalHead.expect("Canonicalhead exists") + xp.vmState = setupVMState(com, head) xp.txDB = TxTabsRef.new xp.lifeTime = txItemLifeTime diff --git a/nimbus/core/validate.nim b/nimbus/core/validate.nim index e7933e5e92..9a60ad0fab 100644 --- a/nimbus/core/validate.nim +++ b/nimbus/core/validate.nim @@ -15,7 +15,6 @@ import ../db/ledger, ../common/common, ../transaction/call_types, - ../errors, ../transaction, ../utils/utils, "."/[dao, eip4844, gaslimit, withdrawals], @@ -127,18 +126,11 @@ proc validateUncles(com: CommonRef; header: Header; else: uncleSet.incl uncleHash - let chainDB = com.db - let recentAncestorHashes = try: - chainDB.getAncestorsHashes(MAX_UNCLE_DEPTH + 1, header) - except CatchableError as err: - return err("Block not present in database") - - let recentUncleHashes = try: - chainDB.getUncleHashes(recentAncestorHashes) - except CatchableError as err: - return err("Ancenstors not present in database") - - let blockHash = header.blockHash + let + chainDB = com.db + recentAncestorHashes = ?chainDB.getAncestorsHashes(MAX_UNCLE_DEPTH + 1, header) + recentUncleHashes = ?chainDB.getUncleHashes(recentAncestorHashes) + blockHash = header.blockHash for uncle in uncles: let uncleHash = uncle.blockHash @@ -164,17 +156,11 @@ proc validateUncles(com: CommonRef; header: Header; return err("uncle block number larger than current block number") # check uncle against own parent - var parent: Header - if not chainDB.getBlockHeader(uncle.parentHash,parent): - return err("Uncle's parent has gone missing") + let parent = ?chainDB.getBlockHeader(uncle.parentHash) if uncle.timestamp <= parent.timestamp: return err("Uncle's parent must me older") - let uncleParent = try: - chainDB.getBlockHeader(uncle.parentHash) - except BlockNotFound: - return err("Uncle parent not found") - + let uncleParent = ?chainDB.getBlockHeader(uncle.parentHash) ? com.validateHeader( Block.init(uncle, BlockBody()), uncleParent, checkSealOK) diff --git a/nimbus/db/core_db/core_apps.nim b/nimbus/db/core_db/core_apps.nim index e71a833c12..f63d662362 100644 --- a/nimbus/db/core_db/core_apps.nim +++ b/nimbus/db/core_db/core_apps.nim @@ -11,14 +11,15 @@ ## Rewrite of `core_apps.nim` using the new `CoreDb` API. The original ## `core_apps.nim` was renamed `core_apps_legacy.nim`. -{.push raises: [].} +{.push gcsafe, raises: [].} import std/[algorithm, sequtils], chronicles, eth/[common, rlp], stew/byteutils, - "../.."/[errors, constants], + results, + "../.."/[constants], ".."/[aristo, storage_types], "."/base @@ -26,9 +27,9 @@ logScope: topics = "core_db" type - TransactionKey = tuple - blockNumber: BlockNumber - index: uint + TransactionKey* = object + blockNumber*: BlockNumber + index*: uint # ------------------------------------------------------------------------------ # Forward declarations @@ -37,73 +38,62 @@ type proc getBlockHeader*( db: CoreDbRef; n: BlockNumber; - output: var Header; - ): bool - {.gcsafe.} + ): Result[Header, string] proc getBlockHeader*( db: CoreDbRef, blockHash: Hash32; - ): Header - {.gcsafe, raises: [BlockNotFound].} + ): Result[Header, string] proc getBlockHash*( db: CoreDbRef; n: BlockNumber; - output: var Hash32; - ): bool - {.gcsafe.} + ): Result[Hash32, string] proc addBlockNumberToHashLookup*( db: CoreDbRef; blockNumber: BlockNumber; blockHash: Hash32; - ) {.gcsafe.} + ) -proc getBlockHeader*( - db: CoreDbRef; - blockHash: Hash32; - output: var Header; - ): bool - {.gcsafe.} - -proc getCanonicalHeaderHash*(db: CoreDbRef): Opt[Hash32] {.gcsafe.} +proc getCanonicalHeaderHash*(db: CoreDbRef): Result[Hash32, string] # ------------------------------------------------------------------------------ # Private helpers # ------------------------------------------------------------------------------ -template discardRlpException(info: static[string]; code: untyped) = +template wrapRlpException(info: static[string]; code: untyped) = try: code except RlpError as e: - warn info, error=($e.name), err=e.msg, errName=e.name + return err(info & ": " & e.msg) # ------------------------------------------------------------------------------ # Private iterators # ------------------------------------------------------------------------------ -iterator findNewAncestors( +proc findNewAncestors( db: CoreDbRef; header: Header; - ): Header = + ): Result[seq[Header], string] = ## Returns the chain leading up from the given header until the first ## ancestor it has in common with our canonical chain. - var h = header - var orig: Header + var + h = header + res = newSeq[Header]() while true: - if db.getBlockHeader(h.number, orig) and orig.rlpHash == h.rlpHash: + let orig = ?db.getBlockHeader(h.number) + if orig.rlpHash == h.rlpHash: break - yield h + res.add(h) if h.parentHash == GENESIS_PARENT_HASH: break else: - if not db.getBlockHeader(h.parentHash, h): - warn "findNewAncestors(): Could not find parent while iterating", - hash = h.parentHash - break + h = ?db.getBlockHeader(h.parentHash) + + ok(res) # ------------------------------------------------------------------------------ # Public iterators @@ -202,28 +192,21 @@ proc setAsCanonicalChainHead( db: CoreDbRef; headerHash: Hash32; header: Header; - ) = + ): Result[void, string] = ## Sets the header as the canonical chain HEAD. - const info = "setAsCanonicalChainHead()" - # TODO This code handles reorgs - this should be moved elsewhere because we'll # be handling reorgs mainly in-memory if header.number == 0 or db.getCanonicalHeaderHash().valueOr(default(Hash32)) != header.parentHash: - var newCanonicalHeaders = sequtils.toSeq(db.findNewAncestors(header)) + var newCanonicalHeaders = ?db.findNewAncestors(header) reverse(newCanonicalHeaders) for h in newCanonicalHeaders: - var oldHash: Hash32 - if not db.getBlockHash(h.number, oldHash): - break - - try: - let oldHeader = db.getBlockHeader(oldHash) - for txHash in db.getBlockTransactionHashes(oldHeader): - db.removeTransactionFromCanonicalChain(txHash) - # TODO re-add txn to internal pending pool (only if local sender) - except BlockNotFound: - warn info & ": Could not load old header", oldHash + let + oldHash = ?db.getBlockHash(h.number) + oldHeader = ?db.getBlockHeader(oldHash) + for txHash in db.getBlockTransactionHashes(oldHeader): + db.removeTransactionFromCanonicalChain(txHash) + # TODO re-add txn to internal pending pool (only if local sender) for h in newCanonicalHeaders: # TODO don't recompute block hash @@ -231,13 +214,14 @@ proc setAsCanonicalChainHead( let canonicalHeadHash = canonicalHeadHashKey() db.ctx.getKvt.put(canonicalHeadHash.toOpenArray, rlp.encode(headerHash)).isOkOr: - warn info, canonicalHeadHash, error=($$error) + return err($$error) + ok() proc markCanonicalChain( db: CoreDbRef; header: Header; headerHash: Hash32; - ): bool = + ): Result[void, string] = ## mark this chain as canonical by adding block number to hash lookup ## down to forking point const @@ -251,17 +235,15 @@ proc markCanonicalChain( kvt = db.ctx.getKvt() key = blockNumberToHashKey(currHeader.number) kvt.put(key.toOpenArray, rlp.encode(currHash)).isOkOr: - warn info, key, error=($$error) - return false + return err($$error) # it is a genesis block, done if currHeader.parentHash == default(Hash32): - return true + return ok() # mark ancestor blocks as canonical too currHash = currHeader.parentHash - if not db.getBlockHeader(currHeader.parentHash, currHeader): - return false + currHeader = ?db.getBlockHeader(currHeader.parentHash) template rlpDecodeOrZero(data: openArray[byte]): Hash32 = try: @@ -273,16 +255,15 @@ proc markCanonicalChain( while currHash != default(Hash32): let key = blockNumberToHashKey(currHeader.number) let data = kvt.getOrEmpty(key.toOpenArray).valueOr: - warn info, key, error=($$error) - return false + return err($$error) if data.len == 0: # not marked, mark it kvt.put(key.toOpenArray, rlp.encode(currHash)).isOkOr: - warn info, key, error=($$error) + return err($$error) elif rlpDecodeOrZero(data) != currHash: # replace prev chain kvt.put(key.toOpenArray, rlp.encode(currHash)).isOkOr: - warn info, key, error=($$error) + return err($$error) else: # forking point, done break @@ -291,10 +272,9 @@ proc markCanonicalChain( break currHash = currHeader.parentHash - if not db.getBlockHeader(currHeader.parentHash, currHeader): - return false + currHeader = ?db.getBlockHeader(currHeader.parentHash) - return true + ok() # ------------------------------------------------------------------------------ # Public functions @@ -304,132 +284,59 @@ proc getSavedStateBlockNumber*( db: CoreDbRef; ): BlockNumber = ## Returns the block number registered when the database was last time - ## updated, or `BlockNumber(0)` if there was no updata found. + ## updated, or `BlockNumber(0)` if there was no update found. ## db.stateBlockNumber() proc getBlockHeader*( db: CoreDbRef; blockHash: Hash32; - output: var Header; - ): bool = + ): Result[Header, string] = const info = "getBlockHeader()" let data = db.ctx.getKvt().get(genericHashKey(blockHash).toOpenArray).valueOr: if error.error != KvtNotFound: warn info, blockHash, error=($$error) - return false + return err("No block with hash " & $blockHash) - discardRlpException info: - output = rlp.decode(data, Header) - return true + wrapRlpException info: + return ok(rlp.decode(data, Header)) proc getBlockHeader*( - db: CoreDbRef, - blockHash: Hash32; - ): Header = - ## Returns the requested block header as specified by block hash. - ## - ## Raises BlockNotFound if it is not present in the db. - if not db.getBlockHeader(blockHash, result): - raise newException( - BlockNotFound, "No block with hash " & blockHash.data.toHex) + db: CoreDbRef; + n: BlockNumber; + ): Result[Header, string] = + ## Returns the block header with the given number in the canonical chain. + let blockHash = ?db.getBlockHash(n) + db.getBlockHeader(blockHash) proc getHash( db: CoreDbRef; key: DbKey; - ): Opt[Hash32] = + ): Result[Hash32, string] = const info = "getHash()" let data = db.ctx.getKvt().get(key.toOpenArray).valueOr: if error.error != KvtNotFound: warn info, key, error=($$error) - return Opt.none(Hash32) + return err($$error) - try: - Opt.some(rlp.decode(data, Hash32)) - except RlpError as exc: - warn info, key, error=exc.msg - Opt.none(Hash32) + wrapRlpException info: + return ok(rlp.decode(data, Hash32)) -proc getCanonicalHeaderHash*(db: CoreDbRef): Opt[Hash32] = +proc getCanonicalHeaderHash*(db: CoreDbRef): Result[Hash32, string] = db.getHash(canonicalHeadHashKey()) proc getCanonicalHead*( db: CoreDbRef; - output: var Header; - ): bool = - let headHash = db.getCanonicalHeaderHash().valueOr: - return false - discardRlpException "getCanonicalHead()": - if db.getBlockHeader(headHash, output): - return true - -proc getCanonicalHead*( - db: CoreDbRef; - ): Header - {.gcsafe, raises: [EVMError].} = - if not db.getCanonicalHead result: - raise newException( - CanonicalHeadNotFound, "No canonical head set for this chain") - -proc getBlockHash*( - db: CoreDbRef; - n: BlockNumber; - output: var Hash32; - ): bool = - ## Return the block hash for the given block number. - output = db.getHash(blockNumberToHashKey(n)).valueOr: - return false - true + ): Result[Header, string] = + let headHash = ?db.getCanonicalHeaderHash() + db.getBlockHeader(headHash) proc getBlockHash*( db: CoreDbRef; n: BlockNumber; - ): Hash32 - {.gcsafe, raises: [BlockNotFound].} = + ): Result[Hash32, string] = ## Return the block hash for the given block number. - if not db.getBlockHash(n, result): - raise newException(BlockNotFound, "No block hash for number " & $n) - -proc getHeadBlockHash*(db: CoreDbRef): Hash32 = - db.getHash(canonicalHeadHashKey()).valueOr(default(Hash32)) - -proc getBlockHeader*( - db: CoreDbRef; - n: BlockNumber; - output: var Header; - ): bool = - ## Returns the block header with the given number in the canonical chain. - var blockHash: Hash32 - if db.getBlockHash(n, blockHash): - result = db.getBlockHeader(blockHash, output) - -proc getBlockHeaderWithHash*( - db: CoreDbRef; - n: BlockNumber; - ): Opt[(Header, Hash32)] = - ## Returns the block header and its hash, with the given number in the - ## canonical chain. Hash is returned to avoid recomputing it - var hash: Hash32 - if db.getBlockHash(n, hash): - # Note: this will throw if header is not present. - var header: Header - if db.getBlockHeader(hash, header): - return Opt.some((header, hash)) - else: - # this should not happen, but if it happen lets fail laudly as this means - # something is super wrong - raiseAssert("Corrupted database. Mapping number->hash present, without header in database") - else: - return Opt.none((Header, Hash32)) - -proc getBlockHeader*( - db: CoreDbRef; - n: BlockNumber; - ): Header - {.raises: [BlockNotFound].} = - ## Returns the block header with the given number in the canonical chain. - ## Raises BlockNotFound error if the block is not in the DB. - db.getBlockHeader(db.getBlockHash(n)) + db.getHash(blockNumberToHashKey(n)) proc getScore*( db: CoreDbRef; @@ -454,11 +361,6 @@ proc setScore*(db: CoreDbRef; blockHash: Hash32, score: UInt256) = warn "setScore()", scoreKey, error=($$error) return -proc getTd*(db: CoreDbRef; blockHash: Hash32, td: var UInt256): bool = - td = db.getScore(blockHash).valueOr: - return false - true - proc headTotalDifficulty*( db: CoreDbRef; ): UInt256 = @@ -471,16 +373,16 @@ proc getAncestorsHashes*( db: CoreDbRef; limit: BlockNumber; header: Header; - ): seq[Hash32] - {.gcsafe, raises: [BlockNotFound].} = - var ancestorCount = min(header.number, limit) - var h = header - - result = newSeq[Hash32](ancestorCount) + ): Result[seq[Hash32], string] = + var + ancestorCount = min(header.number, limit) + h = header + res = newSeq[Hash32](ancestorCount) while ancestorCount > 0: - h = db.getBlockHeader(h.parentHash) - result[ancestorCount - 1] = h.rlpHash + h = ?db.getBlockHeader(h.parentHash) + res[ancestorCount - 1] = h.rlpHash dec ancestorCount + ok(res) proc addBlockNumberToHashLookup*( db: CoreDbRef; blockNumber: BlockNumber, blockHash: Hash32) = @@ -506,7 +408,7 @@ proc persistTransactions*( encodedTx = rlp.encode(tx) txHash = keccak256(encodedTx) blockKey = transactionHashToBlockKey(txHash) - txKey: TransactionKey = (blockNumber, idx.uint) + txKey = TransactionKey(blockNumber: blockNumber, index: idx.uint) key = hashIndexKey(txRoot, idx.uint16) kvt.put(key, encodedTx).isOkOr: warn info, idx, error=($$error) @@ -521,41 +423,33 @@ proc forgetHistory*( ): bool = ## Remove all data related to the block number argument `num`. This function ## returns `true`, if some history was available and deleted. - var blockHash: Hash32 - if db.getBlockHash(blockNum, blockHash): - let kvt = db.ctx.getKvt() - # delete blockNum->blockHash - discard kvt.del(blockNumberToHashKey(blockNum).toOpenArray) - result = true + let blockHash = db.getBlockHash(blockNum).valueOr: + return false - var header: Header - if db.getBlockHeader(blockHash, header): - # delete blockHash->header, stateRoot->blockNum - discard kvt.del(genericHashKey(blockHash).toOpenArray) + let kvt = db.ctx.getKvt() + # delete blockNum->blockHash + discard kvt.del(blockNumberToHashKey(blockNum).toOpenArray) + # delete blockHash->header, stateRoot->blockNum + discard kvt.del(genericHashKey(blockHash).toOpenArray) + true proc getTransactionByIndex*( db: CoreDbRef; txRoot: Hash32; txIndex: uint16; - res: var Transaction; - ): bool = + ): Result[Transaction, string] = const info = "getTransaction()" let kvt = db.ctx.getKvt() let key = hashIndexKey(txRoot, txIndex) let txData = kvt.getOrEmpty(key).valueOr: - warn info, txRoot, key, error=($$error) - return false + return err($$error) if txData.len == 0: - return false + return err("tx data is empty for root=" & $txRoot & " and index=" & $txIndex) - try: - res = rlp.decode(txData, Transaction) - except RlpError as e: - warn info, txRoot, err=e.msg, errName=e.name - return false - true + wrapRlpException info: + return ok(rlp.decode(txData, Transaction)) proc getTransactionCount*( db: CoreDbRef; @@ -581,32 +475,36 @@ proc getTransactionCount*( proc getUnclesCount*( db: CoreDbRef; ommersHash: Hash32; - ): int - {.gcsafe, raises: [RlpError].} = + ): Result[int, string] = const info = "getUnclesCount()" - if ommersHash != EMPTY_UNCLE_HASH: + if ommersHash == EMPTY_UNCLE_HASH: + return ok(0) + + wrapRlpException info: let encodedUncles = block: let key = genericHashKey(ommersHash) db.ctx.getKvt().get(key.toOpenArray).valueOr: if error.error == KvtNotFound: warn info, ommersHash, error=($$error) - return 0 - return rlpFromBytes(encodedUncles).listLen + return ok(0) + return ok(rlpFromBytes(encodedUncles).listLen) proc getUncles*( db: CoreDbRef; ommersHash: Hash32; - ): seq[Header] - {.gcsafe, raises: [RlpError].} = + ): Result[seq[Header], string] = const info = "getUncles()" - if ommersHash != EMPTY_UNCLE_HASH: + if ommersHash == EMPTY_UNCLE_HASH: + return ok(default(seq[Header])) + + wrapRlpException info: let encodedUncles = block: let key = genericHashKey(ommersHash) db.ctx.getKvt().get(key.toOpenArray).valueOr: if error.error == KvtNotFound: warn info, ommersHash, error=($$error) - return @[] - return rlp.decode(encodedUncles, seq[Header]) + return ok(default(seq[Header])) + return ok(rlp.decode(encodedUncles, seq[Header])) proc persistWithdrawals*( db: CoreDbRef; @@ -625,115 +523,103 @@ proc persistWithdrawals*( proc getWithdrawals*( db: CoreDbRef; - withdrawalsRoot: Hash32; - ): seq[Withdrawal] - {.gcsafe, raises: [RlpError].} = - for wd in db.getWithdrawals(withdrawalsRoot): - result.add(wd) + withdrawalsRoot: Hash32 + ): Result[seq[Withdrawal], string] = + wrapRlpException "getWithdrawals": + var res: seq[Withdrawal] + for wd in db.getWithdrawals(withdrawalsRoot): + res.add(wd) + return ok(res) proc getTransactions*( db: CoreDbRef; - txRoot: Hash32; - output: var seq[Transaction]) - {.gcsafe, raises: [RlpError].} = - for encodedTx in db.getBlockTransactionData(txRoot): - output.add(rlp.decode(encodedTx, Transaction)) - -proc getTransactions*( - db: CoreDbRef; - txRoot: Hash32; - ): seq[Transaction] - {.gcsafe, raises: [RlpError].} = - db.getTransactions(txRoot, result) + txRoot: Hash32 + ): Result[seq[Transaction], string] = + wrapRlpException "getTransactions": + var res: seq[Transaction] + for encodedTx in db.getBlockTransactionData(txRoot): + res.add(rlp.decode(encodedTx, Transaction)) + return ok(res) proc getBlockBody*( db: CoreDbRef; header: Header; - output: var BlockBody; - ): bool = - try: - output.transactions = db.getTransactions(header.txRoot) - output.uncles = db.getUncles(header.ommersHash) + ): Result[BlockBody, string] = + wrapRlpException "getBlockBody": + var body: BlockBody + body.transactions = ?db.getTransactions(header.txRoot) + body.uncles = ?db.getUncles(header.ommersHash) if header.withdrawalsRoot.isSome: - output.withdrawals = Opt.some(db.getWithdrawals(header.withdrawalsRoot.get)) - true - except RlpError: - false + let wds = ?db.getWithdrawals(header.withdrawalsRoot.get) + body.withdrawals = Opt.some(wds) + return ok(body) proc getBlockBody*( db: CoreDbRef; blockHash: Hash32; - output: var BlockBody; - ): bool = - var header: Header - if db.getBlockHeader(blockHash, header): - return db.getBlockBody(header, output) - -proc getBlockBody*( - db: CoreDbRef; - hash: Hash32; - ): BlockBody - {.gcsafe, raises: [BlockNotFound].} = - if not db.getBlockBody(hash, result): - raise newException(BlockNotFound, "Error when retrieving block body") + ): Result[BlockBody, string] = + let header = ?db.getBlockHeader(blockHash) + db.getBlockBody(header) proc getEthBlock*( db: CoreDbRef; hash: Hash32; - ): EthBlock - {.gcsafe, raises: [BlockNotFound].} = + ): Result[EthBlock, string] = var - header = db.getBlockHeader(hash) - blockBody = db.getBlockBody(hash) - EthBlock.init(move(header), move(blockBody)) + header = ?db.getBlockHeader(hash) + blockBody = ?db.getBlockBody(hash) + ok(EthBlock.init(move(header), move(blockBody))) proc getEthBlock*( db: CoreDbRef; blockNumber: BlockNumber; - ): EthBlock - {.gcsafe, raises: [BlockNotFound].} = + ): Result[EthBlock, string] = var - header = db.getBlockHeader(blockNumber) + header = ?db.getBlockHeader(blockNumber) headerHash = header.blockHash - blockBody = db.getBlockBody(headerHash) - EthBlock.init(move(header), move(blockBody)) + blockBody = ?db.getBlockBody(headerHash) + ok(EthBlock.init(move(header), move(blockBody))) + proc getUncleHashes*( db: CoreDbRef; blockHashes: openArray[Hash32]; - ): seq[Hash32] - {.gcsafe, raises: [BlockNotFound].} = + ): Result[seq[Hash32], string] = + var res: seq[Hash32] for blockHash in blockHashes: - result &= db.getBlockBody(blockHash).uncles.mapIt(it.rlpHash) + let body = ?db.getBlockBody(blockHash) + res &= body.uncles.mapIt(it.rlpHash) + ok(res) proc getUncleHashes*( db: CoreDbRef; header: Header; - ): seq[Hash32] - {.gcsafe, raises: [RlpError].} = + ): Result[seq[Hash32], string] = if header.ommersHash != EMPTY_UNCLE_HASH: + return ok(default(seq[Hash32])) + + wrapRlpException "getUncleHashes": let key = genericHashKey(header.ommersHash) encodedUncles = db.ctx.getKvt().get(key.toOpenArray).valueOr: if error.error == KvtNotFound: warn "getUncleHashes()", ommersHash=header.ommersHash, error=($$error) - return @[] - return rlp.decode(encodedUncles, seq[Header]).mapIt(it.rlpHash) + return ok(default(seq[Hash32])) + return ok(rlp.decode(encodedUncles, seq[Header]).mapIt(it.rlpHash)) proc getTransactionKey*( db: CoreDbRef; transactionHash: Hash32; - ): tuple[blockNumber: BlockNumber, index: uint64] - {.gcsafe, raises: [RlpError].} = - let - txKey = transactionHashToBlockKey(transactionHash) - tx = db.ctx.getKvt().get(txKey.toOpenArray).valueOr: - if error.error == KvtNotFound: - warn "getTransactionKey()", transactionHash, error=($$error) - return (0.BlockNumber, 0) - let key = rlp.decode(tx, TransactionKey) - (key.blockNumber, key.index.uint64) + ): Result[TransactionKey, string] = + wrapRlpException "getTransactionKey": + let + txKey = transactionHashToBlockKey(transactionHash) + tx = db.ctx.getKvt().get(txKey.toOpenArray).valueOr: + if error.error == KvtNotFound: + warn "getTransactionKey()", transactionHash, error=($$error) + return ok(default(TransactionKey)) + return ok(rlp.decode(tx, TransactionKey)) proc headerExists*(db: CoreDbRef; blockHash: Hash32): bool = ## Returns True if the header with the given block hash is in our DB. @@ -745,38 +631,30 @@ proc headerExists*(db: CoreDbRef; blockHash: Hash32): bool = proc setHead*( db: CoreDbRef; blockHash: Hash32; - ): bool = - var header: Header - if not db.getBlockHeader(blockHash, header): - return false - - if not db.markCanonicalChain(header, blockHash): - return false + ): Result[void, string] = + let header = ?db.getBlockHeader(blockHash) + ?db.markCanonicalChain(header, blockHash) let canonicalHeadHash = canonicalHeadHashKey() db.ctx.getKvt.put(canonicalHeadHash.toOpenArray, rlp.encode(blockHash)).isOkOr: - warn "setHead()", canonicalHeadHash, error=($$error) - return true + return err($$error) + ok() proc setHead*( db: CoreDbRef; header: Header; writeHeader = false; - ): bool = - const info = "setHead()" + ): Result[void, string] = var headerHash = rlpHash(header) let kvt = db.ctx.getKvt() if writeHeader: kvt.put(genericHashKey(headerHash).toOpenArray, rlp.encode(header)).isOkOr: - warn info, headerHash, error=($$error) - return false - if not db.markCanonicalChain(header, headerHash): - return false + return err($$error) + ?db.markCanonicalChain(header, headerHash) let canonicalHeadHash = canonicalHeadHashKey() kvt.put(canonicalHeadHash.toOpenArray, rlp.encode(headerHash)).isOkOr: - warn info, canonicalHeadHash, error=($$error) - return false - true + return err($$error) + ok() proc persistReceipts*( db: CoreDbRef; @@ -796,34 +674,33 @@ proc persistReceipts*( proc getReceipts*( db: CoreDbRef; receiptsRoot: Hash32; - ): seq[Receipt] - {.gcsafe, raises: [RlpError].} = - var receipts = newSeq[Receipt]() - for r in db.getReceipts(receiptsRoot): - receipts.add(r) - return receipts + ): Result[seq[Receipt], string] = + wrapRlpException "getReceipts": + var receipts = newSeq[Receipt]() + for r in db.getReceipts(receiptsRoot): + receipts.add(r) + return ok(receipts) proc persistScore*( db: CoreDbRef; blockHash: Hash32; score: UInt256 - ): bool = + ): Result[void, string] = const info = "persistScore" let kvt = db.ctx.getKvt() scoreKey = blockHashToScoreKey(blockHash) kvt.put(scoreKey.toOpenArray, rlp.encode(score)).isOkOr: - warn info, scoreKey, error=($$error) - return - true + return err(info & ": " & $$error) + ok() proc persistHeader*( db: CoreDbRef; blockHash: Hash32; header: Header; startOfHistory = GENESIS_PARENT_HASH; - ): bool = + ): Result[void, string] = const info = "persistHeader" let @@ -831,12 +708,10 @@ proc persistHeader*( isStartOfHistory = header.parentHash == startOfHistory if not isStartOfHistory and not db.headerExists(header.parentHash): - warn info & ": parent header missing", blockNumber=header.number - return false + return err(info & ": parent header missing number " & $header.number) kvt.put(genericHashKey(blockHash).toOpenArray, rlp.encode(header)).isOkOr: - warn info, blockHash, blockNumber=header.number, error=($$error) - return false + return err(info & ": " & $$error) let parentScore = if isStartOfHistory: @@ -845,17 +720,15 @@ proc persistHeader*( db.getScore(header.parentHash).valueOr: # TODO it's slightly wrong to fail here and leave the block in the db, # but this code is going away soon enough - return false + return err(info & ": cannot get score") score = parentScore + header.difficulty # After EIP-3675, difficulty is set to 0 but we still save the score for # each block to simplify totalDifficulty reporting # TODO get rid of this and store a single value - if not db.persistScore(blockHash, score): - return false - + ?db.persistScore(blockHash, score) db.addBlockNumberToHashLookup(header.number, blockHash) - true + ok() proc persistHeader*( db: CoreDbRef; @@ -863,32 +736,29 @@ proc persistHeader*( header: Header; forceCanonical: bool; startOfHistory = GENESIS_PARENT_HASH; - ): bool = - if not db.persistHeader(blockHash, header, startOfHistory): - return false + ): Result[void, string] = + ?db.persistHeader(blockHash, header, startOfHistory) if not forceCanonical and header.parentHash != startOfHistory: let - canonicalHash = db.getCanonicalHeaderHash().valueOr: - return false + canonicalHash = ?db.getCanonicalHeaderHash() canonScore = db.getScore(canonicalHash).valueOr: - return false + return err("cannot load canon score") # TODO no need to load score from database _really_, but this code is # hopefully going away soon score = db.getScore(blockHash).valueOr: - return false + return err("cannot load score") if score <= canonScore: - return true + return ok() db.setAsCanonicalChainHead(blockHash, header) - true proc persistHeader*( db: CoreDbRef; header: Header; forceCanonical: bool; startOfHistory = GENESIS_PARENT_HASH; - ): bool = + ): Result[void, string] = let blockHash = header.blockHash db.persistHeader(blockHash, header, forceCanonical, startOfHistory) @@ -925,14 +795,12 @@ proc finalizedHeaderHash*(db: CoreDbRef, headerHash: Hash32) = proc safeHeader*( db: CoreDbRef; - ): Header - {.gcsafe, raises: [BlockNotFound].} = + ): Result[Header, string] = db.getBlockHeader(db.safeHeaderHash) proc finalizedHeader*( db: CoreDbRef; - ): Header - {.gcsafe, raises: [BlockNotFound].} = + ): Result[Header, string] = db.getBlockHeader(db.finalizedHeaderHash) # ------------------------------------------------------------------------------ diff --git a/nimbus/evm/state.nim b/nimbus/evm/state.nim index 1d4d235228..4617bea963 100644 --- a/nimbus/evm/state.nim +++ b/nimbus/evm/state.nim @@ -146,12 +146,12 @@ proc reinit*(self: BaseVMState; ## Object descriptor ## This is a variant of the `reinit()` function above where the field ## `header.parentHash`, is used to fetch the `parent` Header to be ## used in the `update()` variant, above. - var parent: Header - self.com.db.getBlockHeader(header.parentHash, parent) and - self.reinit( - parent = parent, - header = header, - linear = false) + let parent = self.com.db.getBlockHeader(header.parentHash).valueOr: + return false + self.reinit( + parent = parent, + header = header, + linear = false) proc init*( self: BaseVMState; ## Object descriptor @@ -204,16 +204,15 @@ proc new*( ## This is a variant of the `new()` constructor above where the field ## `header.parentHash`, is used to fetch the `parent` Header to be ## used in the `new()` variant, above. - var parent: Header - if com.db.getBlockHeader(header.parentHash, parent): - ok(BaseVMState.new( + let parent = com.db.getBlockHeader(header.parentHash).valueOr: + return err(evmErr(EvmHeaderNotFound)) + + ok(BaseVMState.new( parent = parent, header = header, com = com, tracer = tracer, storeSlotHash = storeSlotHash)) - else: - err(evmErr(EvmHeaderNotFound)) proc init*( vmState: BaseVMState; @@ -223,15 +222,15 @@ proc init*( storeSlotHash = false): bool = ## Variant of `new()` which does not throw an exception on a dangling ## `Header` parent hash reference. - var parent: Header - if com.db.getBlockHeader(header.parentHash, parent): - vmState.init( - parent = parent, - header = header, - com = com, - tracer = tracer, - storeSlotHash = storeSlotHash) - return true + let parent = com.db.getBlockHeader(header.parentHash).valueOr: + return false + vmState.init( + parent = parent, + header = header, + com = com, + tracer = tracer, + storeSlotHash = storeSlotHash) + return true func coinbase*(vmState: BaseVMState): Address = vmState.blockCtx.coinbase @@ -261,14 +260,9 @@ func baseFeePerGas*(vmState: BaseVMState): UInt256 = method getAncestorHash*( vmState: BaseVMState, blockNumber: BlockNumber): Hash32 {.gcsafe, base.} = let db = vmState.com.db - try: - var blockHash: Hash32 - if db.getBlockHash(blockNumber, blockHash): - blockHash - else: - default(Hash32) - except RlpError: - default(Hash32) + let blockHash = db.getBlockHash(blockNumber).valueOr: + return default(Hash32) + blockHash proc readOnlyStateDB*(vmState: BaseVMState): ReadOnlyStateDB {.inline.} = ReadOnlyStateDB(vmState.stateDB) diff --git a/nimbus/graphql/ethapi.nim b/nimbus/graphql/ethapi.nim index 362b8b73fc..ef9053f6aa 100644 --- a/nimbus/graphql/ethapi.nim +++ b/nimbus/graphql/ethapi.nim @@ -154,39 +154,33 @@ proc getStateDB(com: CommonRef, header: Header): LedgerRef {.deprecated: "Ledge proc getBlockByNumber(ctx: GraphqlContextRef, number: Node): RespResult = try: - ok(headerNode(ctx, getBlockHeader(ctx.chainDB, toBlockNumber(number)))) - except CatchableError as e: - err(e.msg) + let header = ?ctx.chainDB.getBlockHeader(toBlockNumber(number)) + ok(headerNode(ctx, header)) + except ValueError as exc: + err(exc.msg) proc getBlockByNumber(ctx: GraphqlContextRef, number: base.BlockNumber): RespResult = - try: - ok(headerNode(ctx, getBlockHeader(ctx.chainDB, number))) - except CatchableError as e: - err(e.msg) + let header = ?ctx.chainDB.getBlockHeader(number) + ok(headerNode(ctx, header)) proc getBlockByHash(ctx: GraphqlContextRef, hash: Node): RespResult = try: - ok(headerNode(ctx, getBlockHeader(ctx.chainDB, toHash(hash)))) - except CatchableError as e: - err(e.msg) + let header = ?ctx.chainDB.getBlockHeader(toHash(hash)) + ok(headerNode(ctx, header)) + except ValueError as exc: + err(exc.msg) proc getBlockByHash(ctx: GraphqlContextRef, hash: Hash32): RespResult = - try: - ok(headerNode(ctx, getBlockHeader(ctx.chainDB, hash))) - except CatchableError as e: - err(e.msg) + let header = ?ctx.chainDB.getBlockHeader(hash) + ok(headerNode(ctx, header)) proc getLatestBlock(ctx: GraphqlContextRef): RespResult = - try: - ok(headerNode(ctx, getCanonicalHead(ctx.chainDB))) - except CatchableError as e: - err("can't get latest block: " & e.msg) + let header = ?ctx.chainDB.getCanonicalHead() + ok(headerNode(ctx, header)) proc getTxCount(ctx: GraphqlContextRef, txRoot: Hash32): RespResult = - try: - ok(resp(getTransactionCount(ctx.chainDB, txRoot))) - except CatchableError as e: - err("can't get txcount: " & e.msg) + let txCount = ctx.chainDB.getTransactionCount(txRoot) + ok(resp(txCount)) proc longNode(val: uint64 | int64): RespResult = ok(Node(kind: nkInt, intVal: $val, pos: Pos())) @@ -244,106 +238,85 @@ proc getTotalDifficulty(ctx: GraphqlContextRef, blockHash: Hash32): RespResult = bigIntNode(score) proc getOmmerCount(ctx: GraphqlContextRef, ommersHash: Hash32): RespResult = - try: - ok(resp(getUnclesCount(ctx.chainDB, ommersHash))) - except CatchableError as e: - err("can't get ommers count: " & e.msg) + let ommers = ?ctx.chainDB.getUnclesCount(ommersHash) + ok(resp(ommers)) proc getOmmers(ctx: GraphqlContextRef, ommersHash: Hash32): RespResult = - try: - let uncles = getUncles(ctx.chainDB, ommersHash) - when false: - # EIP 1767 says no ommers == null - # but hive test case want empty array [] - if uncles.len == 0: - return ok(respNull()) - var list = respList() - for n in uncles: - list.add headerNode(ctx, n) - ok(list) - except CatchableError as e: - err("can't get ommers: " & e.msg) - -proc getOmmerAt(ctx: GraphqlContextRef, ommersHash: Hash32, index: int): RespResult = - try: - let uncles = getUncles(ctx.chainDB, ommersHash) + let uncles = ?ctx.chainDB.getUncles(ommersHash) + when false: + # EIP 1767 says no ommers == null + # but hive test case want empty array [] if uncles.len == 0: return ok(respNull()) - if index < 0 or index >= uncles.len: - return ok(respNull()) - ok(headerNode(ctx, uncles[index])) - except CatchableError as e: - err("can't get ommer: " & e.msg) + var list = respList() + for n in uncles: + list.add headerNode(ctx, n) + ok(list) + +proc getOmmerAt(ctx: GraphqlContextRef, ommersHash: Hash32, index: int): RespResult = + let uncles = ?ctx.chainDB.getUncles(ommersHash) + if uncles.len == 0: + return ok(respNull()) + if index < 0 or index >= uncles.len: + return ok(respNull()) + ok(headerNode(ctx, uncles[index])) proc getTxs(ctx: GraphqlContextRef, header: Header): RespResult = - try: - let txCount = getTransactionCount(ctx.chainDB, header.txRoot) - if txCount == 0: - return ok(respNull()) - var list = respList() - var index = 0'u64 - for n in getBlockTransactionData(ctx.chainDB, header.txRoot): - let tx = decodeTx(n) - list.add txNode(ctx, tx, index, header.number, header.baseFeePerGas) - inc index - - index = 0'u64 - var prevUsed = 0.GasInt - for r in getReceipts(ctx.chainDB, header.receiptsRoot): - let tx = TxNode(list.sons[index]) - tx.receipt = r - tx.gasUsed = r.cumulativeGasUsed - prevUsed - prevUsed = r.cumulativeGasUsed - inc index + let txCount = getTransactionCount(ctx.chainDB, header.txRoot) + if txCount == 0: + return ok(respNull()) + var list = respList() + var index = 0'u64 + + let txList = ?ctx.chainDB.getTransactions(header.txRoot) + for tx in txList: + list.add txNode(ctx, tx, index, header.number, header.baseFeePerGas) + inc index + + index = 0'u64 + var prevUsed = 0.GasInt + let receiptList = ?ctx.chainDB.getReceipts(header.receiptsRoot) + for r in receiptList: + let tx = TxNode(list.sons[index]) + tx.receipt = r + tx.gasUsed = r.cumulativeGasUsed - prevUsed + prevUsed = r.cumulativeGasUsed + inc index - ok(list) - except CatchableError as e: - err("can't get transactions: " & e.msg) + ok(list) proc getWithdrawals(ctx: GraphqlContextRef, header: Header): RespResult = - try: - if header.withdrawalsRoot.isSome: - let wds = getWithdrawals(ctx.chainDB, header.withdrawalsRoot.get) - var list = respList() - for wd in wds: - list.add wdNode(ctx, wd) - ok(list) - else: - ok(respNull()) - except CatchableError as e: - err("can't get transactions: " & e.msg) + if header.withdrawalsRoot.isNone: + return ok(respNull()) + + let wds = ?ctx.chainDB.getWithdrawals(header.withdrawalsRoot.get) + var list = respList() + for wd in wds: + list.add wdNode(ctx, wd) + ok(list) proc getTxAt(ctx: GraphqlContextRef, header: Header, index: uint64): RespResult = - try: - var tx: Transaction - if getTransactionByIndex(ctx.chainDB, header.txRoot, index.uint16, tx): - let txn = txNode(ctx, tx, index, header.number, header.baseFeePerGas) - - var i = 0'u64 - var prevUsed = 0.GasInt - for r in getReceipts(ctx.chainDB, header.receiptsRoot): - if i == index: - let tx = TxNode(txn) - tx.receipt = r - tx.gasUsed = r.cumulativeGasUsed - prevUsed - prevUsed = r.cumulativeGasUsed - inc i - - ok(txn) - else: - ok(respNull()) - except CatchableError as exc: - err("can't get transaction by index '" & $index & "': " & exc.msg) - except RlpError as exc: - err("can't get transaction by index '" & $index & "': " & exc.msg) + let tx = ctx.chainDB.getTransactionByIndex(header.txRoot, index.uint16).valueOr: + return ok(respNull()) + + let txn = txNode(ctx, tx, index, header.number, header.baseFeePerGas) + var i = 0'u64 + var prevUsed = 0.GasInt + let receiptList = ?ctx.chainDB.getReceipts(header.receiptsRoot) + for r in receiptList: + if i == index: + let tx = TxNode(txn) + tx.receipt = r + tx.gasUsed = r.cumulativeGasUsed - prevUsed + prevUsed = r.cumulativeGasUsed + inc i + ok(txn) proc getTxByHash(ctx: GraphqlContextRef, hash: Hash32): RespResult = - try: - let (blockNumber, index) = getTransactionKey(ctx.chainDB, hash) - let header = getBlockHeader(ctx.chainDB, blockNumber) - getTxAt(ctx, header, index) - except CatchableError as e: - err("can't get transaction by hash '" & hash.data.toHex & "': $2" & e.msg) + let + txKey = ?ctx.chainDB.getTransactionKey(hash) + header = ?ctx.chainDB.getBlockHeader(txKey.blockNumber) + getTxAt(ctx, header, txKey.index) proc accountNode(ctx: GraphqlContextRef, header: Header, address: Address): RespResult = try: diff --git a/nimbus/rpc/rpc_utils.nim b/nimbus/rpc/rpc_utils.nim index 2e49a6b143..ff8958b3a0 100644 --- a/nimbus/rpc/rpc_utils.nim +++ b/nimbus/rpc/rpc_utils.nim @@ -29,10 +29,11 @@ import ../common/common, web3/eth_api_types -proc calculateMedianGasPrice*(chain: CoreDbRef): GasInt - {.gcsafe, raises: [CatchableError].} = +proc calculateMedianGasPrice*(chain: CoreDbRef): GasInt {.raises: [RlpError].} = + const minGasPrice = 30_000_000_000.GasInt var prices = newSeqOfCap[GasInt](64) - let header = chain.getCanonicalHead() + let header = chain.getCanonicalHead().valueOr: + return minGasPrice for encodedTx in chain.getBlockTransactionData(header.txRoot): let tx = decodeTx(encodedTx) prices.add(tx.gasPrice) @@ -53,8 +54,7 @@ proc calculateMedianGasPrice*(chain: CoreDbRef): GasInt # For compatibility with `ethpandaops/ethereum-package`, set this to a # sane minimum for compatibility to unblock testing. # Note: When this is fixed, update `tests/graphql/queries.toml` and - # re-enable the "query.gasPrice" test case (remove `skip = true`). - const minGasPrice = 30_000_000_000.GasInt + # re-enable the "query.gasPrice" test case (remove `skip = true`). result = max(result, minGasPrice) proc unsignedTx*(tx: TransactionArgs, chain: CoreDbRef, defaultNonce: AccountNonce, chainId: ChainId): Transaction diff --git a/nimbus/rpc/server_api.nim b/nimbus/rpc/server_api.nim index d05fbb15bc..35608963b3 100644 --- a/nimbus/rpc/server_api.nim +++ b/nimbus/rpc/server_api.nim @@ -41,11 +41,9 @@ func newServerAPI*(c: ForkedChainRef, t: TxPoolRef): ServerAPIRef = ServerAPIRef(com: c.com, chain: c, txPool: t) proc getTotalDifficulty*(api: ServerAPIRef, blockHash: Hash32): UInt256 = - var totalDifficulty: UInt256 - if api.com.db.getTd(blockHash, totalDifficulty): - return totalDifficulty - else: + let totalDifficulty = api.com.db.getScore(blockHash).valueOr: return api.com.db.headTotalDifficulty() + return totalDifficulty proc getProof*( accDB: LedgerRef, address: eth_types.Address, slots: seq[UInt256] @@ -216,17 +214,18 @@ proc setupServerAPI*(api: ServerAPIRef, server: RpcServer, ctx: EthContext) = proc getLogsForBlock( chain: ForkedChainRef, header: Header, opts: FilterOptions - ): seq[FilterLog] {.gcsafe, raises: [RlpError].} = + ): seq[FilterLog] {.gcsafe, raises: [].} = if headerBloomFilter(header, opts.address, opts.topics): let (receipts, txs) = if api.chain.isInMemory(header.blockHash): let blk = api.chain.memoryBlock(header.blockHash) (blk.receipts, blk.blk.transactions) else: - ( - chain.db.getReceipts(header.receiptsRoot), - chain.db.getTransactions(header.txRoot), - ) + let rcs = chain.db.getReceipts(header.receiptsRoot).valueOr: + return @[] + let txs = chain.db.getTransactions(header.txRoot).valueOr: + return @[] + (rcs, txs) # Note: this will hit assertion error if number of block transactions # do not match block receipts. # Although this is fine as number of receipts should always match number @@ -242,7 +241,7 @@ proc setupServerAPI*(api: ServerAPIRef, server: RpcServer, ctx: EthContext) = start: base.BlockNumber, finish: base.BlockNumber, opts: FilterOptions, - ): seq[FilterLog] {.gcsafe, raises: [RlpError].} = + ): seq[FilterLog] {.gcsafe, raises: [].} = var logs = newSeq[FilterLog]() blockNum = start @@ -331,19 +330,19 @@ proc setupServerAPI*(api: ServerAPIRef, server: RpcServer, ctx: EthContext) = if blockhash == zeroHash32: # Receipt in database - let txDetails = api.chain.db.getTransactionKey(data) + let txDetails = api.chain.db.getTransactionKey(data).valueOr: + raise newException(ValueError, "TransactionKey not found") if txDetails.index < 0: return nil let header = api.chain.headerByNumber(txDetails.blockNumber).valueOr: raise newException(ValueError, "Block not found") - var tx: Transaction - if not api.chain.db.getTransactionByIndex( - header.txRoot, uint16(txDetails.index), tx - ): + let tx = api.chain.db.getTransactionByIndex( + header.txRoot, uint16(txDetails.index)).valueOr: return nil - - for receipt in api.chain.db.getReceipts(header.receiptsRoot): + let receipts = api.chain.db.getReceipts(header.receiptsRoot).valueOr: + return nil + for receipt in receipts: let gasUsed = receipt.cumulativeGasUsed - prevGasUsed prevGasUsed = receipt.cumulativeGasUsed if idx == txDetails.index: @@ -526,7 +525,8 @@ proc setupServerAPI*(api: ServerAPIRef, server: RpcServer, ctx: EthContext) = if res.isOk: return populateTransactionObject(res.get().tx, Opt.none(Hash32), Opt.none(uint64)) - let txDetails = api.chain.db.getTransactionKey(txHash) + let txDetails = api.chain.db.getTransactionKey(txHash).valueOr: + return nil if txDetails.index < 0: let (blockHash, txid) = api.chain.txRecords(txHash) @@ -535,15 +535,16 @@ proc setupServerAPI*(api: ServerAPIRef, server: RpcServer, ctx: EthContext) = return populateTransactionObject(tx, Opt.some(blockHash), Opt.some(txid)) # TODO: include block number - let header = api.chain.db.getBlockHeader(txDetails.blockNumber) - var tx: Transaction - if api.chain.db.getTransactionByIndex(header.txRoot, uint16(txDetails.index), tx): - return populateTransactionObject( - tx, - Opt.some(header.blockHash), - Opt.some(header.number), - Opt.some(txDetails.index), - ) + let header = api.chain.db.getBlockHeader(txDetails.blockNumber).valueOr: + return nil + let tx = api.chain.db.getTransactionByIndex(header.txRoot, uint16(txDetails.index)).valueOr: + return nil + return populateTransactionObject( + tx, + Opt.some(header.blockHash), + Opt.some(header.number), + Opt.some(txDetails.index.uint64), + ) server.rpc("eth_getTransactionByBlockHashAndIndex") do( data: Hash32, quantity: Web3Quantity @@ -621,9 +622,12 @@ proc setupServerAPI*(api: ServerAPIRef, server: RpcServer, ctx: EthContext) = receipts = blkdesc.receipts txs = blkdesc.blk.transactions else: - for receipt in api.chain.db.getReceipts(header.receiptsRoot): + let receiptList = api.chain.db.getReceipts(header.receiptsRoot).valueOr: + return Opt.none(seq[ReceiptObject]) + for receipt in receiptList: receipts.add receipt - txs = api.chain.db.getTransactions(header.txRoot) + txs = api.chain.db.getTransactions(header.txRoot).valueOr: + return Opt.none(seq[ReceiptObject]) try: for receipt in receipts: diff --git a/nimbus/sync/handlers/eth.nim b/nimbus/sync/handlers/eth.nim index d863cfd80d..c3e8a22351 100644 --- a/nimbus/sync/handlers/eth.nim +++ b/nimbus/sync/handlers/eth.nim @@ -58,27 +58,33 @@ proc notImplemented(name: string) {.used.} = proc successorHeader(db: CoreDbRef, h: Header, - output: var Header, - skip = 0'u): bool = + skip = 0'u): Opt[Header] = let offset = 1 + skip.BlockNumber if h.number <= (not 0.BlockNumber) - offset: - result = db.getBlockHeader(h.number + offset, output) + let header = db.getBlockHeader(h.number + offset).valueOr: + return Opt.none(Header) + return Opt.some(header) + Opt.none(Header) proc ancestorHeader(db: CoreDbRef, h: Header, - output: var Header, - skip = 0'u): bool = + skip = 0'u): Opt[Header] = let offset = 1 + skip.BlockNumber if h.number >= offset: - result = db.getBlockHeader(h.number - offset, output) + let header = db.getBlockHeader(h.number - offset).valueOr: + return Opt.none(Header) + return Opt.some(header) + Opt.none(Header) proc blockHeader(db: CoreDbRef, - b: BlockHashOrNumber, - output: var Header): bool = - if b.isHash: - db.getBlockHeader(b.hash, output) - else: - db.getBlockHeader(b.number, output) + b: BlockHashOrNumber): Opt[Header] = + let header = if b.isHash: + db.getBlockHeader(b.hash).valueOr: + return Opt.none(Header) + else: + db.getBlockHeader(b.number).valueOr: + return Opt.none(Header) + Opt.some(header) # ------------------------------------------------------------------------------ # Private functions: peers related functions @@ -292,45 +298,37 @@ proc new*(_: type EthWireRef, method getStatus*(ctx: EthWireRef): Result[EthState, string] {.gcsafe.} = - try: - let - db = ctx.db - com = ctx.chain.com - bestBlock = db.getCanonicalHead() - forkId = com.forkId(bestBlock.number, bestBlock.timestamp) - - return ok(EthState( - totalDifficulty: db.headTotalDifficulty, - genesisHash: com.genesisHash, - bestBlockHash: bestBlock.blockHash, - forkId: ChainForkId( - forkHash: forkId.crc.toBytesBE, - forkNext: forkId.nextFork - ) - )) - except EVMError as exc: - # TODO: Why an EVM Error in database? - return err(exc.msg) - except RlpError as exc: - return err(exc.msg) + let + db = ctx.db + com = ctx.chain.com + bestBlock = ?db.getCanonicalHead() + forkId = com.forkId(bestBlock.number, bestBlock.timestamp) + + return ok(EthState( + totalDifficulty: db.headTotalDifficulty, + genesisHash: com.genesisHash, + bestBlockHash: bestBlock.blockHash, + forkId: ChainForkId( + forkHash: forkId.crc.toBytesBE, + forkNext: forkId.nextFork + ) + )) method getReceipts*(ctx: EthWireRef, hashes: openArray[Hash32]): Result[seq[seq[Receipt]], string] {.gcsafe.} = - try: - let db = ctx.db - var header: Header - var list: seq[seq[Receipt]] - for blockHash in hashes: - if db.getBlockHeader(blockHash, header): - list.add db.getReceipts(header.receiptsRoot) - else: - list.add @[] - trace "handlers.getReceipts: blockHeader not found", blockHash - return ok(list) - except RlpError as exc: - return err(exc.msg) + let db = ctx.db + var list: seq[seq[Receipt]] + for blockHash in hashes: + let header = db.getBlockHeader(blockHash).valueOr: + list.add @[] + trace "handlers.getReceipts: blockHeader not found", blockHash + continue + let receiptList = ?db.getReceipts(header.receiptsRoot) + list.add receiptList + + return ok(list) method getPooledTxs*(ctx: EthWireRef, hashes: openArray[Hash32]): @@ -356,39 +354,35 @@ method getBlockBodies*(ctx: EthWireRef, Result[seq[BlockBody], string] {.gcsafe.} = let db = ctx.db - var body: BlockBody var list: seq[BlockBody] for blockHash in hashes: - if db.getBlockBody(blockHash, body): - list.add body - else: + let body = db.getBlockBody(blockHash).valueOr: list.add BlockBody() trace "handlers.getBlockBodies: blockBody not found", blockHash + continue + list.add body + return ok(list) method getBlockHeaders*(ctx: EthWireRef, req: EthBlocksRequest): Result[seq[Header], string] {.gcsafe.} = - try: - let db = ctx.db - var foundBlock: Header - var list = newSeqOfCap[Header](req.maxResults) - - if db.blockHeader(req.startBlock, foundBlock): - list.add foundBlock - - while uint64(list.len) < req.maxResults: - if not req.reverse: - if not db.successorHeader(foundBlock, foundBlock, req.skip): - break - else: - if not db.ancestorHeader(foundBlock, foundBlock, req.skip): - break - list.add foundBlock + let db = ctx.db + var list = newSeqOfCap[Header](req.maxResults) + var foundBlock = db.blockHeader(req.startBlock).valueOr: return ok(list) - except RlpError as exc: - return err(exc.msg) + list.add foundBlock + + while uint64(list.len) < req.maxResults: + if not req.reverse: + foundBlock = db.successorHeader(foundBlock, req.skip).valueOr: + break + else: + foundBlock = db.ancestorHeader(foundBlock, req.skip).valueOr: + break + list.add foundBlock + return ok(list) method handleAnnouncedTxs*(ctx: EthWireRef, peer: Peer, diff --git a/nimbus/tracer.nim b/nimbus/tracer.nim index 0704a48694..af4841dc54 100644 --- a/nimbus/tracer.nim +++ b/nimbus/tracer.nim @@ -51,14 +51,6 @@ proc toJson*(receipts: seq[Receipt]): JsonNode {.gcsafe.} # Private helpers # ------------------------------------------------------------------------------ -template safeTracer(info: string; code: untyped) = - try: - code - except CatchableError as e: - raiseAssert info & " name=" & $e.name & " msg=" & e.msg - -# ------------------- - proc init( T: type CaptCtxRef; com: CommonRef; @@ -75,9 +67,9 @@ proc init( T: type CaptCtxRef; com: CommonRef; topHeader: Header; - ): T - {.raises: [CatchableError].} = - T.init(com, com.db.getBlockHeader(topHeader.parentHash).stateRoot) + ): T = + let header = com.db.getBlockHeader(topHeader.parentHash).expect("top header parent exists") + T.init(com, header.stateRoot) proc activate(cc: CaptCtxRef): CaptCtxRef {.discardable.} = ## Install/activate new context `cc.ctx`, old one in `cc.restore` @@ -113,10 +105,11 @@ proc toJson(receipt: Receipt): JsonNode = proc dumpReceiptsImpl( chainDB: CoreDbRef; header: Header; - ): JsonNode - {.raises: [CatchableError].} = + ): JsonNode = result = newJArray() - for receipt in chainDB.getReceipts(header.receiptsRoot): + let receiptList = chainDB.getReceipts(header.receiptsRoot). + expect("receipts exists") + for receipt in receiptList: result.add receipt.toJson # ------------------------------------------------------------------------------ @@ -160,8 +153,7 @@ proc traceTransactionImpl( transactions: openArray[Transaction]; txIndex: uint64; tracerFlags: set[TracerFlags] = {}; - ): JsonNode - {.raises: [CatchableError].}= + ): JsonNode = if header.txRoot == EMPTY_ROOT_HASH: return newJNull() @@ -240,8 +232,7 @@ proc dumpBlockStateImpl( com: CommonRef; blk: EthBlock; dumpState = false; - ): JsonNode - {.raises: [CatchableError].} = + ): JsonNode = template header: Header = blk.header let @@ -307,8 +298,7 @@ proc traceBlockImpl( com: CommonRef; blk: EthBlock; tracerFlags: set[TracerFlags] = {}; - ): JsonNode - {.raises: [CatchableError].} = + ): JsonNode = template header: Header = blk.header let @@ -343,8 +333,7 @@ proc traceTransactionsImpl( com: CommonRef; header: Header; transactions: openArray[Transaction]; - ): JsonNode - {.raises: [CatchableError].} = + ): JsonNode = result = newJArray() for i in 0 ..< transactions.len: result.add traceTransactionImpl( @@ -359,8 +348,7 @@ proc traceBlock*( blk: EthBlock; tracerFlags: set[TracerFlags] = {}; ): JsonNode = - "traceBlock".safeTracer: - result = com.traceBlockImpl(blk, tracerFlags) + com.traceBlockImpl(blk, tracerFlags) proc toJson*(receipts: seq[Receipt]): JsonNode = result = newJArray() @@ -374,8 +362,7 @@ proc dumpMemoryDB*(node: JsonNode, cpt: CoreDbCaptRef) = node["state"] = n proc dumpReceipts*(chainDB: CoreDbRef, header: Header): JsonNode = - "dumpReceipts".safeTracer: - result = chainDB.dumpReceiptsImpl header + chainDB.dumpReceiptsImpl header proc traceTransaction*( com: CommonRef; @@ -384,24 +371,21 @@ proc traceTransaction*( txIndex: uint64; tracerFlags: set[TracerFlags] = {}; ): JsonNode = - "traceTransaction".safeTracer: - result = com.traceTransactionImpl(header, txs, txIndex,tracerFlags) + com.traceTransactionImpl(header, txs, txIndex,tracerFlags) proc dumpBlockState*( com: CommonRef; blk: EthBlock; dumpState = false; ): JsonNode = - "dumpBlockState".safeTracer: - result = com.dumpBlockStateImpl(blk, dumpState) + com.dumpBlockStateImpl(blk, dumpState) proc traceTransactions*( com: CommonRef; header: Header; transactions: openArray[Transaction]; ): JsonNode = - "traceTransactions".safeTracer: - result = com.traceTransactionsImpl(header, transactions) + com.traceTransactionsImpl(header, transactions) # ------------------------------------------------------------------------------ # End diff --git a/tests/replay/undump_blocks_gz.nim b/tests/replay/undump_blocks_gz.nim index 2ed17e2b09..d300e51f77 100644 --- a/tests/replay/undump_blocks_gz.nim +++ b/tests/replay/undump_blocks_gz.nim @@ -47,8 +47,8 @@ proc dumpBlocksBeginNl*(db: CoreDbRef; headers: openArray[Header]): string = if headers[0].number == 1'u64: let - h0 = db.getBlockHeader(0'u64) - b0 = db.getBlockBody(h0.blockHash) + h0 = db.getBlockHeader(0'u64).expect("header exists") + b0 = db.getBlockBody(h0.blockHash).expect("block body exists") result = "" & dumpBlocksBegin(@[h0]) & "\n" & dumpBlocksListNl(h0,b0) & diff --git a/tests/test_blockchain_json.nim b/tests/test_blockchain_json.nim index c61ed8b9e9..ae93351023 100644 --- a/tests/test_blockchain_json.nim +++ b/tests/test_blockchain_json.nim @@ -74,12 +74,13 @@ proc executeCase(node: JsonNode): bool = setupStateDB(env.pre, stateDB) stateDB.persist() - if not com.db.persistHeader(env.genesisHeader, - com.proofOfStake(env.genesisHeader)): - debugEcho "Failed to put genesis header into database" + com.db.persistHeader(env.genesisHeader, + com.proofOfStake(env.genesisHeader)).isOkOr: + debugEcho "Failed to put genesis header into database: ", error return false - if com.db.getCanonicalHead().blockHash != env.genesisHeader.blockHash: + let chead = com.db.getCanonicalHead().expect("canonicalHead exists") + if chead.blockHash != env.genesisHeader.blockHash: debugEcho "Genesis block hash in database is different with expected genesis block hash" return false @@ -102,7 +103,7 @@ proc executeCase(node: JsonNode): bool = debugEcho error return false - let head = com.db.getCanonicalHead() + let head = com.db.getCanonicalHead().expect("canonicalHead exists") let headHash = head.blockHash if headHash != env.lastBlockHash: debugEcho "lastestBlockHash mismatch, get: ", headHash, diff --git a/tests/test_coredb/test_chainsync.nim b/tests/test_coredb/test_chainsync.nim index 9225c58824..48ee9e21e3 100644 --- a/tests/test_coredb/test_chainsync.nim +++ b/tests/test_coredb/test_chainsync.nim @@ -203,7 +203,7 @@ proc test_chainSync*( for w in files.undumpBlocks(least = start): let (fromBlock, toBlock) = (w[0].header.number, w[^1].header.number) if fromBlock == 0'u64: - xCheck w[0].header == com.db.getBlockHeader(0'u64) + xCheck w[0].header == com.db.getBlockHeader(0'u64).expect("block header exists") continue # Process groups of blocks ... diff --git a/tests/test_engine_api.nim b/tests/test_engine_api.nim index c403c19cb3..b3c3c60108 100644 --- a/tests/test_engine_api.nim +++ b/tests/test_engine_api.nim @@ -88,7 +88,7 @@ proc setupEnv(envFork: HardFork = MergeFork, let com = setupCom(conf) - head = com.db.getCanonicalHead() + head = com.db.getCanonicalHead().expect("canonical head exists") chain = ForkedChainRef.init(com) txPool = TxPoolRef.new(com) diff --git a/tests/test_forked_chain.nim b/tests/test_forked_chain.nim index e2c333a695..67f321a105 100644 --- a/tests/test_forked_chain.nim +++ b/tests/test_forked_chain.nim @@ -88,14 +88,15 @@ proc makeBlk(com: CommonRef, number: BlockNumber, parentBlk: Block, extraData: b blk proc headHash(c: CommonRef): Hash32 = - c.db.getCanonicalHead().blockHash + c.db.getCanonicalHead().expect("canonical head exists").blockHash func blockHash(x: Block): Hash32 = x.header.blockHash proc wdWritten(com: CommonRef, blk: Block): int = if blk.header.withdrawalsRoot.isSome: - com.db.getWithdrawals(blk.header.withdrawalsRoot.get).len + com.db.getWithdrawals(blk.header.withdrawalsRoot.get). + expect("withdrawals exists").len else: 0 diff --git a/tests/test_ledger.nim b/tests/test_ledger.nim index 8a0b7d60e4..40cc9e51aa 100644 --- a/tests/test_ledger.nim +++ b/tests/test_ledger.nim @@ -338,8 +338,9 @@ proc runLedgerTransactionTests(noisy = true) = for tx in body.transactions: env.txs.add tx + let head = env.xdb.getCanonicalHead().expect("canonicalHead exists") test &"Collect unique recipient addresses from {env.txs.len} txs," & - &" head=#{env.xdb.getCanonicalHead.number}": + &" head=#{head.number}": # since we generate our own transactions instead of replaying # from testnet blocks, the recipients already unique. for n,tx in env.txs: diff --git a/tests/test_rpc.nim b/tests/test_rpc.nim index d3f9b81e1d..9313d4bc30 100644 --- a/tests/test_rpc.nim +++ b/tests/test_rpc.nim @@ -98,7 +98,7 @@ proc setupEnv(signer, ks2: Address, ctx: EthContext, com: CommonRef): TestEnv = var acc = ctx.am.getAccount(signer).tryGet() blockNumber = 1'u64 - parent = com.db.getCanonicalHead() + parent = com.db.getCanonicalHead().expect("canonicalHead exists") parentHash = parent.blockHash let code = evmByteCode: @@ -193,14 +193,14 @@ proc setupEnv(signer, ks2: Address, ctx: EthContext, com: CommonRef): TestEnv = timestamp : timeStamp ) - doAssert com.db.persistHeader(header, - com.pos.isNil, com.startOfHistory) + com.db.persistHeader(header, + com.pos.isNil, com.startOfHistory).expect("persistHeader not error") let uncles = [header] header.ommersHash = com.db.persistUncles(uncles) - doAssert com.db.persistHeader(header, - com.pos.isNil, com.startOfHistory) + com.db.persistHeader(header, + com.pos.isNil, com.startOfHistory).expect("persistHeader not error") com.db.persistFixtureBlock() @@ -335,7 +335,7 @@ proc rpcMain*() = check res == w3Qty(0'u64) test "eth_getBlockTransactionCountByHash": - let hash = com.db.getBlockHash(0'u64) + let hash = com.db.getBlockHash(0'u64).expect("block hash exists") let res = await client.eth_getBlockTransactionCountByHash(hash) check res == w3Qty(0'u64) @@ -344,7 +344,7 @@ proc rpcMain*() = check res == w3Qty(0'u64) test "eth_getUncleCountByBlockHash": - let hash = com.db.getBlockHash(0'u64) + let hash = com.db.getBlockHash(0'u64).expect("block hash exists") let res = await client.eth_getUncleCountByBlockHash(hash) check res == w3Qty(0'u64) diff --git a/tests/test_tracer_json.nim b/tests/test_tracer_json.nim index af28a6c4f7..d200b1feea 100644 --- a/tests/test_tracer_json.nim +++ b/tests/test_tracer_json.nim @@ -16,7 +16,7 @@ import results, ./test_helpers, ../nimbus/db/aristo, - ../nimbus/db/aristo/[aristo_desc, aristo_layers, aristo_nearby, aristo_part], + ../nimbus/db/aristo/[aristo_desc, aristo_layers, aristo_part], ../nimbus/db/aristo/aristo_part/part_debug, ../nimbus/db/kvt/kvt_utils, ../nimbus/[tracer, evm/types], @@ -141,7 +141,7 @@ proc testFixtureImpl(node: JsonNode, testStatusIMPL: var TestStatus, memoryDB: C # Some hack for `Aristo` using the `snap` protocol proof-loader memoryDB.preLoadAristoDb(state, blockNumber) - var blk = com.db.getEthBlock(blockNumber) + var blk = com.db.getEthBlock(blockNumber).expect("eth block exists") let txTraces = traceTransactions(com, blk.header, blk.transactions) let stateDump = dumpBlockState(com, blk) diff --git a/tests/test_txpool2.nim b/tests/test_txpool2.nim index 3abd5fa2af..b652731cfa 100644 --- a/tests/test_txpool2.nim +++ b/tests/test_txpool2.nim @@ -277,7 +277,7 @@ proc runTxHeadDelta(noisy = true) = xp = env.xp com = env.com chain = env.chain - head = com.db.getCanonicalHead() + head = com.db.getCanonicalHead().expect("canonical head exists") timestamp = head.timestamp const @@ -327,7 +327,7 @@ proc runTxHeadDelta(noisy = true) = setErrorLevel() # in case we set trace level check com.syncCurrent == 10.BlockNumber - head = com.db.getBlockHeader(com.syncCurrent) + head = com.db.getBlockHeader(com.syncCurrent).expect("block header exists") let sdb = LedgerRef.init(com.db) expected = u256(txPerblock * numBlocks) * amount