Fix TxPool when handling EIP-4844 blob tx (#1831)
* Fix TxPool when handling EIP-4844 blob tx
This commit is contained in:
parent
14ee5f6820
commit
7169c846a3
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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]]
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue