diff --git a/nimbus/beacon/payload_conv.nim b/nimbus/beacon/payload_conv.nim index f5159c449..e1a6c3803 100644 --- a/nimbus/beacon/payload_conv.nim +++ b/nimbus/beacon/payload_conv.nim @@ -31,7 +31,7 @@ func wdRoot(x: Option[seq[WithdrawalV1]]): Option[common.Hash256] func txRoot(list: openArray[Web3Tx]): common.Hash256 {.gcsafe, raises:[RlpError].} = {.nosideEffect.}: - calcTxRoot(ethTxs list) + calcTxRoot(ethTxs(list, removeBlobs = true)) # ------------------------------------------------------------------------------ # Public functions diff --git a/nimbus/beacon/web3_eth_conv.nim b/nimbus/beacon/web3_eth_conv.nim index 04223cd86..8c02bbc30 100644 --- a/nimbus/beacon/web3_eth_conv.nim +++ b/nimbus/beacon/web3_eth_conv.nim @@ -127,11 +127,15 @@ func ethWithdrawals*(x: Option[seq[WithdrawalV1]]): func ethTx*(x: Web3Tx): common.Transaction {.gcsafe, raises:[RlpError].} = result = rlp.decode(distinctBase x, common.Transaction) -func ethTxs*(list: openArray[Web3Tx]): +func ethTxs*(list: openArray[Web3Tx], removeBlobs = false): seq[common.Transaction] {.gcsafe, raises:[RlpError].} = result = newSeqOfCap[common.Transaction](list.len) - for x in list: - result.add ethTx(x) + if removeBlobs: + for x in list: + result.add ethTx(x).removeNetworkPayload + else: + for x in list: + result.add ethTx(x) # ------------------------------------------------------------------------------ # Eth types to Web3 types diff --git a/nimbus/core/eip4844.nim b/nimbus/core/eip4844.nim index 625d19db4..60e583bdd 100644 --- a/nimbus/core/eip4844.nim +++ b/nimbus/core/eip4844.nim @@ -169,7 +169,7 @@ func validateEip4844Header*( proc validateBlobTransactionWrapper*(tx: Transaction): Result[void, string] {.raises: [].} = - if not tx.networkPayload.isNil: + if tx.networkPayload.isNil: return err("tx wrapper is none") # note: assert blobs are not malformatted diff --git a/nimbus/core/tx_pool.nim b/nimbus/core/tx_pool.nim index 4290b95c1..f035f224d 100644 --- a/nimbus/core/tx_pool.nim +++ b/nimbus/core/tx_pool.nim @@ -921,6 +921,22 @@ proc inPoolAndOk*(xp: TxPoolRef; txHash: Hash256): bool = if res.isErr: return false res.get().reject == txInfoOk +proc inPoolAndReason*(xp: TxPoolRef; txHash: Hash256): Result[void, string] = + let res = xp.getItem(txHash) + if res.isErr: + # try to look in rejecteds + let r = xp.txDB.byRejects.eq(txHash) + if r.isErr: + return err("cannot find tx in txpool") + else: + return err(r.get().rejectInfo) + + let item = res.get() + if item.reject == txInfoOk: + return ok() + else: + return err(item.rejectInfo) + # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------ diff --git a/nimbus/core/tx_pool/tx_item.nim b/nimbus/core/tx_pool/tx_item.nim index 657951a94..28a2a89a2 100644 --- a/nimbus/core/tx_pool/tx_item.nim +++ b/nimbus/core/tx_pool/tx_item.nim @@ -159,7 +159,7 @@ proc cost*(tx: Transaction): UInt256 = proc effectiveGasTip*(tx: Transaction; baseFee: GasPrice): GasPriceEx = ## The effective miner gas tip for the globally argument `baseFee`. The ## result (which is a price per gas) might well be negative. - if tx.txType != TxEip1559: + if tx.txType < TxEip1559: (tx.gasPrice - baseFee.int64).GasPriceEx else: # London, EIP1559 @@ -205,6 +205,13 @@ proc tx*(item: TxItemRef): Transaction = ## Getter item.tx +func rejectInfo*(item: TxItemRef): string = + ## Getter + result = $item.reject + if item.info.len > 0: + result.add ": " + result.add item.info + # ------------------------------------------------------------------------------ # Public functions, setters # ------------------------------------------------------------------------------ @@ -217,6 +224,10 @@ proc `reject=`*(item: TxItemRef; val: TxInfo) = ## Setter item.reject = val +proc `info=`*(item: TxItemRef; val: string) = + ## Setter + item.info = val + # ------------------------------------------------------------------------------ # Public functions, pretty printing and debugging # ------------------------------------------------------------------------------ diff --git a/nimbus/core/tx_pool/tx_tasks/tx_add.nim b/nimbus/core/tx_pool/tx_tasks/tx_add.nim index 33c3526f5..9d8297406 100644 --- a/nimbus/core/tx_pool/tx_tasks/tx_add.nim +++ b/nimbus/core/tx_pool/tx_tasks/tx_add.nim @@ -45,7 +45,7 @@ type ## Temporary sorter list SortedSet[AccountNonce,TxItemRef] - AccouuntNonceTab = ##\ + AccountNonceTab = ##\ ## Temporary sorter table Table[EthAddress,NonceList] @@ -56,7 +56,7 @@ logScope: # Private helper # ------------------------------------------------------------------------------ -proc getItemList(tab: var AccouuntNonceTab; key: EthAddress): var NonceList +proc getItemList(tab: var AccountNonceTab; key: EthAddress): var NonceList {.gcsafe,raises: [KeyError].} = if not tab.hasKey(key): tab[key] = NonceList.init @@ -176,18 +176,19 @@ proc addTxs*(xp: TxPoolRef; ## basket, this list keeps the items with the highest nonce (handy for ## chasing nonce gaps after a back-move of the block chain head.) ## - var accTab: AccouuntNonceTab + var accTab: AccountNonceTab for tx in txs.items: var reason: TxInfo - + if tx.txType == TxEip4844: let res = tx.validateBlobTransactionWrapper() if res.isErr: - # move item to waste basket + # move item to waste basket reason = txInfoErrInvalidBlob xp.txDB.reject(tx, reason, txItemPending, res.error) invalidTxMeter(1) + continue # Create tx item wrapper, preferably recovered from waste basket let rcTx = xp.recoverItem(tx, txItemPending, info) diff --git a/nimbus/core/tx_pool/tx_tasks/tx_classify.nim b/nimbus/core/tx_pool/tx_tasks/tx_classify.nim index 958b70ce2..7460af04b 100644 --- a/nimbus/core/tx_pool/tx_tasks/tx_classify.nim +++ b/nimbus/core/tx_pool/tx_tasks/tx_classify.nim @@ -37,7 +37,11 @@ logScope: # ------------------------------------------------------------------------------ proc checkTxBasic(xp: TxPoolRef; item: TxItemRef): bool = - validateTxBasic(item.tx, xp.chain.nextFork).isOk + let res = validateTxBasic(item.tx.removeNetworkPayload, xp.chain.nextFork) + if res.isOk: + return true + item.info = res.error + return false proc checkTxNonce(xp: TxPoolRef; item: TxItemRef): bool {.gcsafe,raises: [CatchableError].} = @@ -224,7 +228,7 @@ proc classifyValidatePacked*(xp: TxPoolRef; tx = item.tx.eip1559TxNormalization(xp.chain.baseFee.GasInt) excessBlobGas = calcExcessBlobGas(vmState.parent) - roDB.validateTransaction(tx, item.sender, gasLimit, baseFee, excessBlobGas, fork).isOk + roDB.validateTransaction(tx.removeNetworkPayload, item.sender, gasLimit, baseFee, excessBlobGas, fork).isOk proc classifyPacked*(xp: TxPoolRef; gasBurned, moreBurned: GasInt): bool = ## Classifier for *packing* (i.e. adding up `gasUsed` values after executing diff --git a/nimbus/graphql/ethapi.nim b/nimbus/graphql/ethapi.nim index 82a4bf640..8f622ced9 100644 --- a/nimbus/graphql/ethapi.nim +++ b/nimbus/graphql/ethapi.nim @@ -1418,10 +1418,11 @@ proc sendRawTransaction(ud: RootRef, params: Args, parent: Node): RespResult {.a ctx.txPool.add(tx) - if ctx.txPool.inPoolAndOk(txHash): + let res = ctx.txPool.inPoolAndReason(txHash) + if res.isOk: return resp(txHash) else: - return err("transaction rejected by txpool") + return err(res.error) except CatchableError as em: return err("failed to process raw transaction: " & em.msg) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index c66392a08..483d715f3 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -274,8 +274,9 @@ proc setupEthRpc*( txHash = rlpHash(signedTx) txPool.add(signedTx) - if not txPool.inPoolAndOk(txHash): - raise newException(ValueError, "transaction rejected by txpool") + let res = txPool.inPoolAndReason(txHash) + if res.isErr: + raise newException(ValueError, res.error) result = txHash.ethHashStr server.rpc("eth_call") do(call: EthCall, quantityTag: string) -> HexDataStr: @@ -337,17 +338,19 @@ proc setupEthRpc*( ## ## data: hash of a transaction. ## Returns requested transaction information. - let txDetails = chainDB.getTransactionKey(data.toHash()) + let txHash = data.toHash() + let res = txPool.getItem(txHash) + if res.isOk: + return some(populateTransactionObject(res.get().tx)) + + let txDetails = chainDB.getTransactionKey(txHash) if txDetails.index < 0: return none(TransactionObject) let header = chainDB.getBlockHeader(txDetails.blockNumber) var tx: Transaction if chainDB.getTransaction(header.txRoot, txDetails.index, tx): - result = some(populateTransactionObject(tx, header, txDetails.index)) - - # TODO: if the requested transaction not in blockchain - # try to look for pending transaction in txpool + result = some(populateTransactionObject(tx, some(header), some(txDetails.index))) server.rpc("eth_getTransactionByBlockHashAndIndex") do(data: EthHashStr, quantity: HexQuantityStr) -> Option[TransactionObject]: ## Returns information about a transaction by block hash and transaction index position. @@ -362,7 +365,7 @@ proc setupEthRpc*( var tx: Transaction if chainDB.getTransaction(header.txRoot, index, tx): - result = some(populateTransactionObject(tx, header, index)) + result = some(populateTransactionObject(tx, some(header), some(index))) server.rpc("eth_getTransactionByBlockNumberAndIndex") do(quantityTag: string, quantity: HexQuantityStr) -> Option[TransactionObject]: ## Returns information about a transaction by block number and transaction index position. @@ -375,7 +378,7 @@ proc setupEthRpc*( var tx: Transaction if chainDB.getTransaction(header.txRoot, index, tx): - result = some(populateTransactionObject(tx, header, index)) + result = some(populateTransactionObject(tx, some(header), some(index))) server.rpc("eth_getTransactionReceipt") do(data: EthHashStr) -> Option[ReceiptObject]: ## Returns the receipt of a transaction by transaction hash. diff --git a/nimbus/rpc/rpc_utils.nim b/nimbus/rpc/rpc_utils.nim index bcf7d5e78..cda4945fe 100644 --- a/nimbus/rpc/rpc_utils.nim +++ b/nimbus/rpc/rpc_utils.nim @@ -164,11 +164,14 @@ proc toAccessTupleList(list: openArray[AccessPair]): seq[AccessTuple] = for x in list: result.add toAccessTuple(x) -proc populateTransactionObject*(tx: Transaction, header: BlockHeader, txIndex: int): TransactionObject +proc populateTransactionObject*(tx: Transaction, + header: Option[BlockHeader] = none(BlockHeader), + txIndex: Option[int] = none(int)): TransactionObject {.gcsafe, raises: [ValidationError].} = result.`type` = encodeQuantity(tx.txType.uint64) - result.blockHash = some(header.hash) - result.blockNumber = some(encodeQuantity(header.blockNumber)) + if header.isSome: + result.blockHash = some(header.get().hash) + result.blockNumber = some(encodeQuantity(header.get().blockNumber)) result.`from` = tx.getSender() result.gas = encodeQuantity(tx.gasLimit.uint64) result.gasPrice = encodeQuantity(tx.gasPrice.uint64) @@ -176,7 +179,8 @@ proc populateTransactionObject*(tx: Transaction, header: BlockHeader, txIndex: i result.input = tx.payload result.nonce = encodeQuantity(tx.nonce.uint64) result.to = some(tx.destination) - result.transactionIndex = some(encodeQuantity(txIndex.uint64)) + if txIndex.isSome: + result.transactionIndex = some(encodeQuantity(txIndex.get().uint64)) result.value = encodeQuantity(tx.value) result.v = encodeQuantity(tx.V.uint) result.r = encodeQuantity(tx.R) @@ -228,7 +232,7 @@ proc populateBlockObject*(header: BlockHeader, chain: CoreDbRef, fullTx: bool, i if fullTx: var i = 0 for tx in chain.getBlockTransactions(header): - result.transactions.add %(populateTransactionObject(tx, header, i)) + result.transactions.add %(populateTransactionObject(tx, some(header), some(i))) inc i else: for x in chain.getBlockTransactionHashes(header): diff --git a/tests/graphql/queries.toml b/tests/graphql/queries.toml index 4299c6278..34bdab24d 100644 --- a/tests/graphql/queries.toml +++ b/tests/graphql/queries.toml @@ -472,7 +472,7 @@ mutation { sendRawTransaction(data: "0xf86080018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba077c7cd36820c71821c1aed59de46e70e701c4a8dd89c9ba508ab722210f60da8a03f29825d40c7c3f7bff3ca69267e0f3fb74b2d18b8c2c4e3c135b5d3b06e288d") } """ - errors = ["[2, 3]: Fatal: Field 'sendRawTransaction' cannot be resolved: \"transaction rejected by txpool\": @[\"sendRawTransaction\"]"] + errors = ["[2, 3]: Fatal: Field 'sendRawTransaction' cannot be resolved: \"Tx rejected by basic validator\": @[\"sendRawTransaction\"]"] result = """null""" [[units]] diff --git a/tests/test_txpool2.nim b/tests/test_txpool2.nim index a2dfedab3..447dbab78 100644 --- a/tests/test_txpool2.nim +++ b/tests/test_txpool2.nim @@ -7,7 +7,6 @@ import ../nimbus/core/clique/[clique_sealer, clique_desc], ../nimbus/[config, transaction, constants], ../nimbus/core/tx_pool, - ../nimbus/core/tx_pool/tx_item, ../nimbus/core/casper, ../nimbus/core/executor, ../nimbus/common/common, @@ -269,11 +268,6 @@ proc runTxPoolPosTest*() = let bal = sdb.getBalance(feeRecipient) check not bal.isZero -proc inPoolAndOk(txPool: TxPoolRef, txHash: Hash256): bool = - let res = txPool.getItem(txHash) - if res.isErr: return false - res.get().reject == txInfoOk - proc runTxPoolBlobhashTest*() = var env = initEnv(Cancun)