Fix TxPool when handling EIP-4844 blob tx (#1831)

* Fix TxPool when handling EIP-4844 blob tx
This commit is contained in:
andri lim 2023-10-20 15:30:05 +07:00 committed by GitHub
parent 14ee5f6820
commit 7169c846a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 74 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
# ------------------------------------------------------------------------------

View File

@ -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
# ------------------------------------------------------------------------------

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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):

View File

@ -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]]

View File

@ -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)