Introduce wrapper type for EIP-4844 transactions

EIP-4844 blob sidecars are a concept that only exists in the mempool.
After inclusion of a transaction into an execution block, only the
versioned hash within the transaction remains. To improve type safety,
replace the `Transaction.networkPayload` member with a wrapper type
`PooledTransaction` that is used in contexts where blob sidecars exist.
This commit is contained in:
Etan Kissling 2024-05-06 22:52:56 +02:00
parent 143f2e99f5
commit aedf76f9c8
No known key found for this signature in database
GPG Key ID: B21DA824C5A3D03D
46 changed files with 403 additions and 251 deletions

2
.gitmodules vendored
View File

@ -22,7 +22,7 @@
path = vendor/nim-eth
url = https://github.com/status-im/nim-eth.git
ignore = dirty
branch = master
branch = feat/eip-6493
[submodule "vendor/nim-http-utils"]
path = vendor/nim-http-utils
url = https://github.com/status-im/nim-http-utils.git

View File

@ -336,7 +336,7 @@ func getTimestamp*(cust: CustomPayloadData, basePayload: ExecutionPayload): uint
# Construct a customized payload by taking an existing payload as base and mixing it CustomPayloadData
# blockHash is calculated automatically.
proc customizePayload*(cust: CustomPayloadData, data: ExecutableData): ExecutableData {.gcsafe.} =
var customHeader = blockHeader(data.basePayload, removeBlobs = false, beaconRoot = data.beaconRoot)
var customHeader = blockHeader(data.basePayload, beaconRoot = data.beaconRoot)
if cust.transactions.isSome:
customHeader.txRoot = calcTxRoot(cust.transactions.get)

View File

@ -29,7 +29,7 @@ type
TestBlobTxPool* = ref object
currentBlobID* : BlobID
currentTxIndex*: int
transactions* : Table[common.Hash256, Transaction]
transactions* : Table[common.Hash256, PooledTransaction]
hashesByIndex* : Table[int, common.Hash256]
const
@ -53,7 +53,7 @@ func getMinExcessBlobGasForBlobGasPrice(data_gas_price: uint64): uint64 =
func getMinExcessBlobsForBlobGasPrice*(data_gas_price: uint64): uint64 =
return getMinExcessBlobGasForBlobGasPrice(data_gas_price) div GAS_PER_BLOB.uint64
proc addBlobTransaction*(pool: TestBlobTxPool, tx: Transaction) =
proc addBlobTransaction*(pool: TestBlobTxPool, tx: PooledTransaction) =
let txHash = rlpHash(tx)
pool.transactions[txHash] = tx
@ -178,19 +178,19 @@ proc getBlobDataInPayload*(pool: TestBlobTxPool, payload: ExecutionPayload): Res
return err("blob data is nil")
let np = blobTx.networkPayload
if blobTx.versionedHashes.len != np.commitments.len or
if blobTx.tx.versionedHashes.len != np.commitments.len or
np.commitments.len != np.blobs.len or
np.blobs.len != np.proofs.len:
return err("invalid blob wrap data")
for i in 0..<blobTx.versionedHashes.len:
for i in 0..<blobTx.tx.versionedHashes.len:
blobData.data.add BlobWrapData(
versionedHash: blobTx.versionedHashes[i],
versionedHash: blobTx.tx.versionedHashes[i],
commitment : np.commitments[i],
blob : np.blobs[i],
proof : np.proofs[i],
)
blobData.txs.add blobTx
blobData.txs.add blobTx.tx
return ok(blobData)

View File

@ -40,7 +40,7 @@ method execute*(step: DevP2PRequestPooledTransactionHash, ctx: CancunTestContext
var
txHashes = newSeq[common.Hash256](step.transactionIndexes.len)
txs = newSeq[Transaction](step.transactionIndexes.len)
txs = newSeq[PooledTransaction](step.transactionIndexes.len)
for i, txIndex in step.transactionIndexes:
if not ctx.txPool.hashesByIndex.hasKey(txIndex):

View File

@ -80,7 +80,7 @@ method execute*(step: SendBlobTransactions, ctx: CancunTestContext): bool =
let blobTx = res.get
if not step.skipVerificationFromNode:
let r = verifyTransactionFromNode(engine.client, blobTx)
let r = verifyTransactionFromNode(engine.client, blobTx.tx)
if r.isErr:
error "verify tx from node", msg=r.error
return false

View File

@ -333,7 +333,7 @@ proc getNextPayload(cl: CLMocker): bool =
cl.latestShouldOverrideBuilder = x.shouldOverrideBuilder
let beaconRoot = ethHash cl.latestPayloadAttributes.parentBeaconblockRoot
let header = blockHeader(cl.latestPayloadBuilt, removeBlobs = true, beaconRoot = beaconRoot)
let header = blockHeader(cl.latestPayloadBuilt, beaconRoot = beaconRoot)
let blockHash = w3Hash header.blockHash
if blockHash != cl.latestPayloadBuilt.blockHash:
error "CLMocker: getNextPayload blockHash mismatch",

View File

@ -192,7 +192,7 @@ method getName(cs: InvalidMissingAncestorReOrgSyncTest): string =
$cs.invalidField, $cs.emptyTransactions, $cs.reOrgFromCanonical, $cs.invalidIndex]
proc executableDataToBlock(ex: ExecutableData): EthBlock =
ethBlock(ex.basePayload, removeBlobs = true, beaconRoot = ex.beaconRoot)
ethBlock(ex.basePayload, beaconRoot = ex.beaconRoot)
method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool =
var sec = env.addEngine(true, cs.reOrgFromCanonical)

View File

@ -380,7 +380,7 @@ method execute(cs: PayloadBuildAfterInvalidPayloadTest, env: TestEnv): bool =
type
InvalidTxChainIDTest* = ref object of EngineSpec
InvalidTxChainIDShadow = ref object
invalidTx: Transaction
invalidTx: PooledTransaction
method withMainFork(cs: InvalidTxChainIDTest, fork: EngineFork): BaseSpec =
var res = cs.clone()
@ -430,7 +430,9 @@ method execute(cs: InvalidTxChainIDTest, env: TestEnv): bool =
chainID: some((chainId.uint64 + 1'u64).ChainId)
)
shadow.invalidTx = env.customizeTransaction(sender, tx, txCustomizerData)
shadow.invalidTx = tx
shadow.invalidTx.tx = env.customizeTransaction(
sender, shadow.invalidTx.tx, txCustomizerData)
testCond env.sendTx(shadow.invalidTx):
info "Error on sending transaction with incorrect chain ID"

View File

@ -23,7 +23,7 @@ type
startBlockNumber: uint64
blockCount: int
currentTxIndex: int
txs: seq[Transaction]
txs: seq[PooledTransaction]
method withMainFork(cs: PrevRandaoTransactionTest, fork: EngineFork): BaseSpec =
var res = cs.clone()

View File

@ -119,9 +119,9 @@ type
ShadowTx = ref object
payload: ExecutionPayload
nextTx: Transaction
tx: Option[Transaction]
sendTransaction: proc(i: int): Transaction {.gcsafe.}
nextTx: PooledTransaction
tx: Option[PooledTransaction]
sendTransaction: proc(i: int): PooledTransaction {.gcsafe.}
method withMainFork(cs: TransactionReOrgTest, fork: EngineFork): BaseSpec =
var res = cs.clone()
@ -153,7 +153,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
var shadow = ShadowTx()
# Send a transaction on each payload of the canonical chain
shadow.sendTransaction = proc(i: int): Transaction {.gcsafe.} =
shadow.sendTransaction = proc(i: int): PooledTransaction {.gcsafe.} =
let sstoreContractAddr = hexToByteArray[20]("0000000000000000000000000000000000000317")
var data: array[32, byte]
data[^1] = i.byte
@ -332,7 +332,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
txt.expectBlockHash(ethHash env.clMock.latestForkchoice.headBlockHash)
if cs.scenario != TransactionReOrgScenarioReOrgBackIn:
shadow.tx = none(Transaction)
shadow.tx = none(PooledTransaction)
if cs.scenario == TransactionReOrgScenarioReOrgBackIn and i > 0:
# Reasoning: Most of the clients do not re-add blob transactions to the pool

View File

@ -512,7 +512,8 @@ proc namedHeader*(client: RpcClient, name: string): Result[common.BlockHeader, s
return err("failed to get named blockHeader")
return ok(res.toBlockHeader)
proc sendTransaction*(client: RpcClient, tx: common.Transaction): Result[void, string] =
proc sendTransaction*(
client: RpcClient, tx: common.PooledTransaction): Result[void, string] =
wrapTry:
let encodedTx = rlp.encode(tx)
let res = waitFor client.eth_sendRawTransaction(encodedTx)
@ -603,7 +604,10 @@ TraceOpts.useDefaultSerializationIn JrpcConv
createRpcSigsFromNim(RpcClient):
proc debug_traceTransaction(hash: TxHash, opts: TraceOpts): JsonNode
proc debugPrevRandaoTransaction*(client: RpcClient, tx: Transaction, expectedPrevRandao: Hash256): Result[void, string] =
proc debugPrevRandaoTransaction*(
client: RpcClient,
tx: PooledTransaction,
expectedPrevRandao: Hash256): Result[void, string] =
wrapTry:
let hash = w3Hash tx.rlpHash
# we only interested in stack, disable all other elems

View File

@ -116,18 +116,20 @@ func numEngines*(env: TestEnv): int =
func accounts*(env: TestEnv, idx: int): TestAccount =
env.sender.getAccount(idx)
proc makeTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
proc makeTx*(
env: TestEnv, tc: BaseTx, nonce: AccountNonce): PooledTransaction =
env.sender.makeTx(tc, nonce)
proc makeTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): Transaction =
proc makeTx*(
env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): PooledTransaction =
env.sender.makeTx(tc, nonce)
proc makeTxs*(env: TestEnv, tc: BaseTx, num: int): seq[Transaction] =
result = newSeqOfCap[Transaction](num)
proc makeTxs*(env: TestEnv, tc: BaseTx, num: int): seq[PooledTransaction] =
result = newSeqOfCap[PooledTransaction](num)
for _ in 0..<num:
result.add env.sender.makeNextTx(tc)
proc makeNextTx*(env: TestEnv, tc: BaseTx): Transaction =
proc makeNextTx*(env: TestEnv, tc: BaseTx): PooledTransaction =
env.sender.makeNextTx(tc)
proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool =
@ -145,7 +147,8 @@ proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): boo
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
env.sender.sendTx(eng.client, tc, nonce)
proc sendTxs*(env: TestEnv, eng: EngineEnv, txs: openArray[Transaction]): bool =
proc sendTxs*(
env: TestEnv, eng: EngineEnv, txs: openArray[PooledTransaction]): bool =
for tx in txs:
if not sendTx(eng.client, tx):
return false
@ -163,17 +166,29 @@ proc sendTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
let client = env.engine.client
env.sender.sendTx(client, tc, nonce)
proc sendTx*(env: TestEnv, tx: Transaction): bool =
proc sendTx*(env: TestEnv, tx: PooledTransaction): bool =
let client = env.engine.client
sendTx(client, tx)
proc sendTx*(env: TestEnv, sender: TestAccount, eng: EngineEnv, tc: BlobTx): Result[Transaction, void] =
proc sendTx*(
env: TestEnv,
sender: TestAccount,
eng: EngineEnv,
tc: BlobTx): Result[PooledTransaction, void] =
env.sender.sendTx(sender, eng.client, tc)
proc replaceTx*(env: TestEnv, sender: TestAccount, eng: EngineEnv, tc: BlobTx): Result[Transaction, void] =
proc replaceTx*(
env: TestEnv,
sender: TestAccount,
eng: EngineEnv,
tc: BlobTx): Result[PooledTransaction, void] =
env.sender.replaceTx(sender, eng.client, tc)
proc makeTx*(env: TestEnv, tc: BaseTx, sender: TestAccount, nonce: AccountNonce): Transaction =
proc makeTx*(
env: TestEnv,
tc: BaseTx,
sender: TestAccount,
nonce: AccountNonce): PooledTransaction =
env.sender.makeTx(tc, sender, nonce)
proc customizeTransaction*(env: TestEnv,

View File

@ -130,7 +130,7 @@ proc getTxType(tc: BaseTx, nonce: uint64): TxType =
else:
tc.txType.get
proc makeTxOfType(params: MakeTxParams, tc: BaseTx): Transaction =
proc makeTxOfType(params: MakeTxParams, tc: BaseTx): PooledTransaction =
let
gasFeeCap = if tc.gasFee != 0.GasInt: tc.gasFee
else: gasPrice
@ -140,26 +140,30 @@ proc makeTxOfType(params: MakeTxParams, tc: BaseTx): Transaction =
let txType = tc.getTxType(params.nonce)
case txType
of TxLegacy:
Transaction(
txType : TxLegacy,
nonce : params.nonce,
to : tc.recipient,
value : tc.amount,
gasLimit: tc.gasLimit,
gasPrice: gasPrice,
payload : tc.payload
PooledTransaction(
tx: Transaction(
txType : TxLegacy,
nonce : params.nonce,
to : tc.recipient,
value : tc.amount,
gasLimit: tc.gasLimit,
gasPrice: gasPrice,
payload : tc.payload
)
)
of TxEip1559:
Transaction(
txType : TxEIP1559,
nonce : params.nonce,
gasLimit: tc.gasLimit,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : params.chainId
PooledTransaction(
tx: Transaction(
txType : TxEIP1559,
nonce : params.nonce,
gasLimit: tc.gasLimit,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : params.chainId
)
)
of TxEip4844:
doAssert(tc.recipient.isSome, "recipient must be some")
@ -173,19 +177,21 @@ proc makeTxOfType(params: MakeTxParams, tc: BaseTx): Transaction =
var blobData = blobDataGenerator(tc.blobID, blobCount)
#tc.blobID += BlobID(blobCount)
Transaction(
txType : TxEIP4844,
nonce : params.nonce,
chainId : params.chainId,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
gasLimit: tc.gasLimit,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
#AccessList: tc.AccessList,
maxFeePerBlobGas: blobFeeCap,
versionedHashes: system.move(blobData.hashes),
PooledTransaction(
tx: Transaction(
txType : TxEIP4844,
nonce : params.nonce,
chainId : params.chainId,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
gasLimit: tc.gasLimit,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
#AccessList: tc.AccessList,
maxFeePerBlobGas: blobFeeCap,
versionedHashes: system.move(blobData.hashes),
),
networkPayload: NetworkPayload(
blobs: system.move(blobData.blobs),
commitments: system.move(blobData.commitments),
@ -193,15 +199,16 @@ proc makeTxOfType(params: MakeTxParams, tc: BaseTx): Transaction =
)
)
else:
doAssert(false, "unsupported tx type")
Transaction()
raiseAssert "unsupported tx type"
proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
proc makeTx(params: MakeTxParams, tc: BaseTx): PooledTransaction =
# Build the transaction depending on the specified type
let tx = makeTxOfType(params, tc)
signTransaction(tx, params.key, params.chainId, eip155 = true)
PooledTransaction(
tx: signTransaction(tx.tx, params.key, params.chainId, eip155 = true),
networkPayload: tx.networkPayload)
proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): Transaction =
proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): PooledTransaction =
var tx = tc
if tx.payload.len == 0:
# Prepare initcode payload
@ -215,7 +222,8 @@ proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): Transaction =
doAssert(tx.recipient.isNone, "invalid configuration for big contract tx creator")
params.makeTx(tx.BaseTx)
proc makeTx*(sender: TxSender, tc: BaseTx, nonce: AccountNonce): Transaction =
proc makeTx*(
sender: TxSender, tc: BaseTx, nonce: AccountNonce): PooledTransaction =
let acc = sender.getNextAccount()
let params = MakeTxParams(
chainId: sender.chainId,
@ -224,7 +232,10 @@ proc makeTx*(sender: TxSender, tc: BaseTx, nonce: AccountNonce): Transaction =
)
params.makeTx(tc)
proc makeTx*(sender: TxSender, tc: BigInitcodeTx, nonce: AccountNonce): Transaction =
proc makeTx*(
sender: TxSender,
tc: BigInitcodeTx,
nonce: AccountNonce): PooledTransaction =
let acc = sender.getNextAccount()
let params = MakeTxParams(
chainId: sender.chainId,
@ -233,7 +244,7 @@ proc makeTx*(sender: TxSender, tc: BigInitcodeTx, nonce: AccountNonce): Transact
)
params.makeTx(tc)
proc makeNextTx*(sender: TxSender, tc: BaseTx): Transaction =
proc makeNextTx*(sender: TxSender, tc: BaseTx): PooledTransaction =
let
acc = sender.getNextAccount()
nonce = sender.getNextNonce(acc.address)
@ -290,14 +301,14 @@ proc sendTx*(sender: TxSender, client: RpcClient, tc: BigInitcodeTx, nonce: Acco
inc sender.txSent
return true
proc sendTx*(client: RpcClient, tx: Transaction): bool =
proc sendTx*(client: RpcClient, tx: PooledTransaction): bool =
let rr = client.sendTransaction(tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc makeTx*(params: MakeTxParams, tc: BlobTx): Transaction =
proc makeTx*(params: MakeTxParams, tc: BlobTx): PooledTransaction =
# Need tx wrap data that will pass blob verification
let data = blobDataGenerator(tc.blobID, tc.blobCount)
doAssert(tc.recipient.isSome, "nil recipient address")
@ -323,19 +334,23 @@ proc makeTx*(params: MakeTxParams, tc: BlobTx): Transaction =
versionedHashes: data.hashes,
)
var tx = signTransaction(unsignedTx, params.key, params.chainId, eip155 = true)
tx.networkPayload = NetworkPayload(
blobs : data.blobs,
commitments: data.commitments,
proofs : data.proofs,
PooledTransaction(
tx: signTransaction(unsignedTx, params.key, params.chainId, eip155 = true),
networkPayload: NetworkPayload(
blobs : data.blobs,
commitments: data.commitments,
proofs : data.proofs,
),
)
tx
proc getAccount*(sender: TxSender, idx: int): TestAccount =
sender.accounts[idx]
proc sendTx*(sender: TxSender, acc: TestAccount, client: RpcClient, tc: BlobTx): Result[Transaction, void] =
proc sendTx*(
sender: TxSender,
acc: TestAccount,
client: RpcClient,
tc: BlobTx): Result[PooledTransaction, void] =
let
params = MakeTxParams(
chainId: sender.chainId,
@ -352,7 +367,11 @@ proc sendTx*(sender: TxSender, acc: TestAccount, client: RpcClient, tc: BlobTx):
inc sender.txSent
return ok(tx)
proc replaceTx*(sender: TxSender, acc: TestAccount, client: RpcClient, tc: BlobTx): Result[Transaction, void] =
proc replaceTx*(
sender: TxSender,
acc: TestAccount,
client: RpcClient,
tc: BlobTx): Result[PooledTransaction, void] =
let
params = MakeTxParams(
chainId: sender.chainId,
@ -369,7 +388,11 @@ proc replaceTx*(sender: TxSender, acc: TestAccount, client: RpcClient, tc: BlobT
inc sender.txSent
return ok(tx)
proc makeTx*(sender: TxSender, tc: BaseTx, acc: TestAccount, nonce: AccountNonce): Transaction =
proc makeTx*(
sender: TxSender,
tc: BaseTx,
acc: TestAccount,
nonce: AccountNonce): PooledTransaction =
let
params = MakeTxParams(
chainId: sender.chainId,

View File

@ -112,7 +112,7 @@ proc execute*(ws: MaxInitcodeSizeSpec, env: TestEnv): bool =
# Customize the payload to include a tx with an invalid initcode
let customizer = CustomPayloadData(
parentBeaconRoot: ethHash env.clMock.latestPayloadAttributes.parentBeaconBlockRoot,
transactions: some( @[invalidTx] ),
transactions: some( @[invalidTx.tx] ),
)
let customPayload = customizer.customizePayload(env.clMock.latestExecutableData).basePayload

View File

@ -18,7 +18,8 @@ import
export eth_api
proc sendTransaction*(client: RpcClient, tx: Transaction): Future[bool] {.async.} =
proc sendTransaction*(
client: RpcClient, tx: PooledTransaction): Future[bool] {.async.} =
let data = rlp.encode(tx)
let txHash = keccakHash(data)
let hex = await client.eth_sendRawTransaction(data)

View File

@ -90,7 +90,7 @@ proc balanceAndNonceAtTest(t: TestEnv): Future[TestStatus] {.async.} =
let txHash = rlpHash(tx)
echo "BalanceAt: send $1 wei from 0x$2 to 0x$3 in 0x$4" % [
$tx.value, sourceAddr.toHex, targetAddr.toHex, txHash.data.toHex]
$tx.tx.value, sourceAddr.toHex, targetAddr.toHex, txHash.data.toHex]
let ok = await client.sendTransaction(tx)
if not ok:
@ -117,14 +117,16 @@ proc balanceAndNonceAtTest(t: TestEnv): Future[TestStatus] {.async.} =
let balanceTargetAccountAfter = await client.balanceAt(targetAddr)
# expected balance is previous balance - tx amount - tx fee (gasUsed * gasPrice)
let exp = sourceAddressBalanceBefore - amount - (gasUsed * tx.gasPrice).u256
let exp =
sourceAddressBalanceBefore - amount - (gasUsed * tx.tx.gasPrice).u256
if exp != accountBalanceAfter:
echo "Expected sender account to have a balance of $1, got $2" % [$exp, $accountBalanceAfter]
return TestStatus.Failed
if balanceTargetAccountAfter != amount:
echo "Expected new account to have a balance of $1, got $2" % [$tx.value, $balanceTargetAccountAfter]
echo "Expected new account to have a balance of $1, got $2" % [
$tx.tx.value, $balanceTargetAccountAfter]
return TestStatus.Failed
# ensure nonce is incremented by 1

View File

@ -79,7 +79,8 @@ proc sendSome(address: EthAddress, amount: UInt256): seq[byte] =
result.add amount.toBytesBE
doAssert(result.len == 68) # 4 + 32 + 32
proc makeFundingTx*(v: Vault, recipient: EthAddress, amount: UInt256): Transaction =
proc makeFundingTx*(
v: Vault, recipient: EthAddress, amount: UInt256): PooledTransaction =
let
unsignedTx = Transaction(
txType : TxLegacy,
@ -92,7 +93,8 @@ proc makeFundingTx*(v: Vault, recipient: EthAddress, amount: UInt256): Transacti
payload : sendSome(recipient, amount)
)
signTransaction(unsignedTx, v.vaultKey, v.chainId, eip155 = true)
PooledTransaction(
tx: signTransaction(unsignedTx, v.vaultKey, v.chainId, eip155 = true))
proc signTx*(v: Vault,
sender: EthAddress,
@ -100,7 +102,7 @@ proc signTx*(v: Vault,
recipient: EthAddress,
amount: UInt256,
gasLimit, gasPrice: GasInt,
payload: seq[byte] = @[]): Transaction =
payload: seq[byte] = @[]): PooledTransaction =
let
unsignedTx = Transaction(
@ -115,7 +117,8 @@ proc signTx*(v: Vault,
)
let key = v.accounts[sender]
signTransaction(unsignedTx, key, v.chainId, eip155 = true)
PooledTransaction(
tx: signTransaction(unsignedTx, key, v.chainId, eip155 = true))
# createAccount creates a new account that is funded from the vault contract.
# It will panic when the account could not be created and funded.

View File

@ -188,17 +188,17 @@ proc forkchoiceUpdated*(ben: BeaconEngineRef,
let attrs = attrsOpt.get()
validateVersion(attrs, com, apiVersion)
let payload = ben.generatePayload(attrs).valueOr:
let bundle = ben.generatePayload(attrs).valueOr:
error "Failed to create sealing payload", err = error
raise invalidAttr(error)
let id = computePayloadId(blockHash, attrs)
ben.put(id, ben.blockValue, payload)
ben.put(id, ben.blockValue, bundle.executionPayload, bundle.blobsBundle)
info "Created payload for sealing",
id = id.toHex,
hash = payload.blockHash.short,
number = payload.blockNumber
hash = bundle.executionPayload.blockHash.short,
number = bundle.executionPayload.blockNumber
return validFCU(some(id), blockHash)

View File

@ -26,14 +26,18 @@ proc getPayload*(ben: BeaconEngineRef,
var payloadGeneric: ExecutionPayload
var blockValue: UInt256
if not ben.get(id, blockValue, payloadGeneric):
var blobsBundle: Option[BlobsBundleV1]
if not ben.get(id, blockValue, payloadGeneric, blobsBundle):
raise unknownPayload("Unknown payload")
let version = payloadGeneric.version
if version > expectedVersion:
raise unsupportedFork("getPayload" & $expectedVersion &
" expect ExecutionPayload" & $expectedVersion &
" but get ExecutionPayload" & $version)
" expect ExecutionPayload" & $expectedVersion &
" but get ExecutionPayload" & $version)
if blobsBundle.isSome:
raise unsupportedFork("getPayload" & $expectedVersion &
" contains unsupported BlobsBundleV1")
GetPayloadV2Response(
executionPayload: payloadGeneric.V1V2,
@ -46,38 +50,25 @@ proc getPayloadV3*(ben: BeaconEngineRef, id: PayloadID): GetPayloadV3Response =
var payloadGeneric: ExecutionPayload
var blockValue: UInt256
if not ben.get(id, blockValue, payloadGeneric):
var blobsBundle: Option[BlobsBundleV1]
if not ben.get(id, blockValue, payloadGeneric, blobsBundle):
raise unknownPayload("Unknown payload")
let version = payloadGeneric.version
if version != Version.V3:
raise unsupportedFork("getPayloadV3 expect ExecutionPayloadV3 but get ExecutionPayload" & $version)
if blobsBundle.isNone:
raise unsupportedFork("getPayloadV3 is missing BlobsBundleV1")
let payload = payloadGeneric.V3
let com = ben.com
if not com.isCancunOrLater(ethTime payload.timestamp):
raise unsupportedFork("payload timestamp is less than Cancun activation")
var
blobsBundle: BlobsBundleV1
try:
for ttx in payload.transactions:
let tx = rlp.decode(distinctBase(ttx), Transaction)
if tx.networkPayload.isNil.not:
for blob in tx.networkPayload.blobs:
blobsBundle.blobs.add Web3Blob(blob)
for p in tx.networkPayload.proofs:
blobsBundle.proofs.add Web3KZGProof(p)
for k in tx.networkPayload.commitments:
blobsBundle.commitments.add Web3KZGCommitment(k)
except RlpError:
doAssert(false, "found TypedTransaction that RLP failed to decode")
GetPayloadV3Response(
executionPayload: payload,
blockValue: blockValue,
blobsBundle: blobsBundle,
blobsBundle: blobsBundle.get,
shouldOverrideBuilder: false
)
@ -87,37 +78,24 @@ proc getPayloadV4*(ben: BeaconEngineRef, id: PayloadID): GetPayloadV4Response =
var payloadGeneric: ExecutionPayload
var blockValue: UInt256
if not ben.get(id, blockValue, payloadGeneric):
var blobsBundle: Option[BlobsBundleV1]
if not ben.get(id, blockValue, payloadGeneric, blobsBundle):
raise unknownPayload("Unknown payload")
let version = payloadGeneric.version
if version != Version.V4:
raise unsupportedFork("getPayloadV4 expect ExecutionPayloadV4 but get ExecutionPayload" & $version)
if blobsBundle.isNone:
raise unsupportedFork("getPayloadV4 is missing BlobsBundleV1")
let payload = payloadGeneric.V4
let com = ben.com
if not com.isPragueOrLater(ethTime payload.timestamp):
raise unsupportedFork("payload timestamp is less than Prague activation")
var
blobsBundle: BlobsBundleV1
try:
for ttx in payload.transactions:
let tx = rlp.decode(distinctBase(ttx), Transaction)
if tx.networkPayload.isNil.not:
for blob in tx.networkPayload.blobs:
blobsBundle.blobs.add Web3Blob(blob)
for p in tx.networkPayload.proofs:
blobsBundle.proofs.add Web3KZGProof(p)
for k in tx.networkPayload.commitments:
blobsBundle.commitments.add Web3KZGCommitment(k)
except RlpError:
doAssert(false, "found TypedTransaction that RLP failed to decode")
GetPayloadV4Response(
executionPayload: payload,
blockValue: blockValue,
blobsBundle: blobsBundle,
blobsBundle: blobsBundle.get,
shouldOverrideBuilder: false
)

View File

@ -118,20 +118,20 @@ proc newPayload*(ben: BeaconEngineRef,
validatePayload(apiVersion, version, payload)
validateVersion(com, timestamp, version, apiVersion)
var header = blockHeader(payload, removeBlobs = true, beaconRoot = ethHash beaconRoot)
var header = blockHeader(payload, beaconRoot = ethHash beaconRoot)
if apiVersion >= Version.V3:
if versionedHashes.isNone:
raise invalidParams("newPayload" & $apiVersion &
" expect blobVersionedHashes but got none")
if not validateVersionedHashed(payload, versionedHashes.get):
return invalidStatus(header.parentHash, "invalid blob versionedHashes")
let blockHash = ethHash payload.blockHash
header.validateBlockHash(blockHash, version).isOkOr:
return error
# If we already have the block locally, ignore the entire execution and just
# return a fake success.
if db.getBlockHeader(blockHash, header):
@ -195,7 +195,7 @@ proc newPayload*(ben: BeaconEngineRef,
trace "Inserting block without sethead",
hash = blockHash, number = header.blockNumber
let body = blockBody(payload, removeBlobs = true)
let body = blockBody(payload)
let vres = ben.chain.insertBlockWithoutSetHead(header, body)
if vres != ValidationResult.OK:
let blockHash = latestValidHash(db, parent, ttd)

View File

@ -8,6 +8,7 @@
# those terms.
import
std/sequtils,
./web3_eth_conv,
./payload_conv,
web3/execution_types,
@ -80,12 +81,22 @@ proc put*(ben: BeaconEngineRef,
ben.queue.put(hash, header)
proc put*(ben: BeaconEngineRef, id: PayloadID,
blockValue: UInt256, payload: ExecutionPayload) =
ben.queue.put(id, blockValue, payload)
blockValue: UInt256, payload: ExecutionPayload,
blobsBundle: Option[BlobsBundleV1]) =
ben.queue.put(id, blockValue, payload, blobsBundle)
proc put*(ben: BeaconEngineRef, id: PayloadID,
blockValue: UInt256, payload: SomeExecutionPayload) =
ben.queue.put(id, blockValue, payload)
blockValue: UInt256, payload: SomeExecutionPayload,
blobsBundle: Option[BlobsBundleV1]) =
doAssert blobsBundle.isNone == (payload is
ExecutionPayloadV1 | ExecutionPayloadV2)
ben.queue.put(id, blockValue, payload, blobsBundle)
proc put*(ben: BeaconEngineRef, id: PayloadID,
blockValue: UInt256,
payload: ExecutionPayloadV1 | ExecutionPayloadV2) =
ben.queue.put(
id, blockValue, payload, blobsBundle = options.none(BlobsBundleV1))
# ------------------------------------------------------------------------------
# Public functions, getters
@ -115,8 +126,9 @@ proc get*(ben: BeaconEngineRef, hash: common.Hash256,
proc get*(ben: BeaconEngineRef, id: PayloadID,
blockValue: var UInt256,
payload: var ExecutionPayload): bool =
ben.queue.get(id, blockValue, payload)
payload: var ExecutionPayload,
blobsBundle: var Option[BlobsBundleV1]): bool =
ben.queue.get(id, blockValue, payload, blobsBundle)
proc get*(ben: BeaconEngineRef, id: PayloadID,
blockValue: var UInt256,
@ -130,8 +142,9 @@ proc get*(ben: BeaconEngineRef, id: PayloadID,
proc get*(ben: BeaconEngineRef, id: PayloadID,
blockValue: var UInt256,
payload: var ExecutionPayloadV3): bool =
ben.queue.get(id, blockValue, payload)
payload: var ExecutionPayloadV3,
blobsBundle: var BlobsBundleV1): bool =
ben.queue.get(id, blockValue, payload, blobsBundle)
proc get*(ben: BeaconEngineRef, id: PayloadID,
blockValue: var UInt256,
@ -142,9 +155,13 @@ proc get*(ben: BeaconEngineRef, id: PayloadID,
# Public functions
# ------------------------------------------------------------------------------
type ExecutionPayloadAndBlobsBundle* = object
executionPayload*: ExecutionPayload
blobsBundle*: Option[BlobsBundleV1]
proc generatePayload*(ben: BeaconEngineRef,
attrs: PayloadAttributes):
Result[ExecutionPayload, string] =
Result[ExecutionPayloadAndBlobsBundle, string] =
wrapException:
let
xp = ben.txPool
@ -168,12 +185,22 @@ proc generatePayload*(ben: BeaconEngineRef,
if pos.timestamp <= headBlock.timestamp:
return err "timestamp must be strictly later than parent"
# someBaseFee = true: make sure blk.header
# someBaseFee = true: make sure bundle.blk.header
# have the same blockHash with generated payload
let blk = xp.assembleBlock(someBaseFee = true).valueOr:
let bundle = xp.assembleBlock(someBaseFee = true).valueOr:
return err(error)
if blk.header.extraData.len > 32:
if bundle.blk.header.extraData.len > 32:
return err "extraData length should not exceed 32 bytes"
ok(executionPayload(blk))
var blobsBundle: Option[BlobsBundleV1]
if bundle.blobsBundle.isSome:
template blobData: untyped = bundle.blobsBundle.get
blobsBundle = options.some BlobsBundleV1(
commitments: blobData.commitments.mapIt it.Web3KZGCommitment,
proofs: blobData.proofs.mapIt it.Web3KZGProof,
blobs: blobData.blobs.mapIt it.Web3Blob)
ok ExecutionPayloadAndBlobsBundle(
executionPayload: executionPayload(bundle.blk),
blobsBundle: blobsBundle)

View File

@ -28,10 +28,10 @@ func wdRoot(x: Option[seq[WithdrawalV1]]): Option[common.Hash256]
if x.isNone: none(common.Hash256)
else: some(wdRoot x.get)
func txRoot(list: openArray[Web3Tx], removeBlobs: bool): common.Hash256
func txRoot(list: openArray[Web3Tx]): common.Hash256
{.gcsafe, raises:[RlpError].} =
{.noSideEffect.}:
calcTxRoot(ethTxs(list, removeBlobs))
calcTxRoot(ethTxs(list))
# ------------------------------------------------------------------------------
# Public functions
@ -80,15 +80,14 @@ func executionPayloadV1V2*(blk: EthBlock): ExecutionPayloadV1OrV2 =
)
func blockHeader*(p: ExecutionPayload,
removeBlobs: bool,
beaconRoot: Option[common.Hash256]):
common.BlockHeader {.gcsafe, raises:[CatchableError].} =
common.BlockHeader {.gcsafe, raises:[CatchableError].} =
common.BlockHeader(
parentHash : ethHash p.parentHash,
ommersHash : EMPTY_UNCLE_HASH,
coinbase : ethAddr p.feeRecipient,
stateRoot : ethHash p.stateRoot,
txRoot : txRoot(p.transactions, removeBlobs),
txRoot : txRoot p.transactions,
receiptRoot : ethHash p.receiptsRoot,
bloom : ethBloom p.logsBloom,
difficulty : 0.u256,
@ -106,21 +105,20 @@ func blockHeader*(p: ExecutionPayload,
parentBeaconBlockRoot: beaconRoot
)
func blockBody*(p: ExecutionPayload, removeBlobs: bool):
common.BlockBody {.gcsafe, raises:[RlpError].} =
func blockBody*(p: ExecutionPayload):
common.BlockBody {.gcsafe, raises:[RlpError].} =
common.BlockBody(
uncles : @[],
transactions: ethTxs(p.transactions, removeBlobs),
transactions: ethTxs p.transactions,
withdrawals : ethWithdrawals p.withdrawals,
)
func ethBlock*(p: ExecutionPayload,
removeBlobs: bool,
beaconRoot: Option[common.Hash256]):
common.EthBlock {.gcsafe, raises:[CatchableError].} =
common.Ethblock(
header : blockHeader(p, removeBlobs, beaconRoot),
common.EthBlock(
header : blockHeader(p, beaconRoot),
uncles : @[],
txs : ethTxs(p.transactions, removeBlobs),
txs : ethTxs p.transactions,
withdrawals: ethWithdrawals p.withdrawals,
)

View File

@ -35,6 +35,7 @@ type
id: PayloadID
payload: ExecutionPayload
blockValue: UInt256
blobsBundle: Option[BlobsBundleV1]
HeaderItem = object
hash: common.Hash256
@ -71,13 +72,22 @@ proc put*(api: var PayloadQueue,
api.headerQueue.put(HeaderItem(hash: hash, header: header))
proc put*(api: var PayloadQueue, id: PayloadID,
blockValue: UInt256, payload: ExecutionPayload) =
blockValue: UInt256, payload: ExecutionPayload,
blobsBundle: Option[BlobsBundleV1]) =
api.payloadQueue.put(PayloadItem(id: id,
payload: payload, blockValue: blockValue))
payload: payload, blockValue: blockValue, blobsBundle: blobsBundle))
proc put*(api: var PayloadQueue, id: PayloadID,
blockValue: UInt256, payload: SomeExecutionPayload) =
api.put(id, blockValue, payload.executionPayload)
blockValue: UInt256, payload: SomeExecutionPayload,
blobsBundle: Option[BlobsBundleV1]) =
doAssert blobsBundle.isNone == (payload is
ExecutionPayloadV1 | ExecutionPayloadV2)
api.put(id, blockValue, payload.executionPayload, blobsBundle: blobsBundle)
proc put*(api: var PayloadQueue, id: PayloadID,
blockValue: UInt256,
payload: ExecutionPayloadV1 | ExecutionPayloadV2) =
api.put(id, blockValue, payload, blobsBundle = options.none(BlobsBundleV1))
# ------------------------------------------------------------------------------
# Public functions, getters
@ -93,46 +103,66 @@ proc get*(api: PayloadQueue, hash: common.Hash256,
proc get*(api: PayloadQueue, id: PayloadID,
blockValue: var UInt256,
payload: var ExecutionPayload): bool =
payload: var ExecutionPayload,
blobsBundle: var Option[BlobsBundleV1]): bool =
for x in api.payloadQueue:
if x.id == id:
payload = x.payload
blockValue = x.blockValue
blobsBundle = x.blobsBundle
return true
false
proc get*(api: PayloadQueue, id: PayloadID,
blockValue: var UInt256,
payload: var ExecutionPayloadV1): bool =
var p: ExecutionPayload
let found = api.get(id, blockValue, p)
doAssert(p.version == Version.V1)
payload = p.V1
var
p: ExecutionPayload
blobsBundleOpt: Option[BlobsBundleV1]
let found = api.get(id, blockValue, p, blobsBundleOpt)
if found:
doAssert(p.version == Version.V1)
payload = p.V1
doAssert(blobsBundleOpt.isNone)
return found
proc get*(api: PayloadQueue, id: PayloadID,
blockValue: var UInt256,
payload: var ExecutionPayloadV2): bool =
var p: ExecutionPayload
let found = api.get(id, blockValue, p)
doAssert(p.version == Version.V2)
payload = p.V2
var
p: ExecutionPayload
blobsBundleOpt: Option[BlobsBundleV1]
let found = api.get(id, blockValue, p, blobsBundleOpt)
if found:
doAssert(p.version == Version.V2)
payload = p.V2
doAssert(blobsBundleOpt.isNone)
return found
proc get*(api: PayloadQueue, id: PayloadID,
blockValue: var UInt256,
payload: var ExecutionPayloadV3): bool =
var p: ExecutionPayload
let found = api.get(id, blockValue, p)
doAssert(p.version == Version.V3)
payload = p.V3
payload: var ExecutionPayloadV3,
blobsBundle: var BlobsBundleV1): bool =
var
p: ExecutionPayload
blobsBundleOpt: Option[BlobsBundleV1]
let found = api.get(id, blockValue, p, blobsBundleOpt)
if found:
doAssert(p.version == Version.V3)
payload = p.V3
doAssert(blobsBundleOpt.isSome)
blobsBundle = blobsBundleOpt.unsafeGet
return found
proc get*(api: PayloadQueue, id: PayloadID,
blockValue: var UInt256,
payload: var ExecutionPayloadV1OrV2): bool =
var p: ExecutionPayload
let found = api.get(id, blockValue, p)
doAssert(p.version in {Version.V1, Version.V2})
payload = p.V1V2
var
p: ExecutionPayload
blobsBundleOpt: Option[BlobsBundleV1]
let found = api.get(id, blockValue, p, blobsBundleOpt)
if found:
doAssert(p.version in {Version.V1, Version.V2})
payload = p.V1V2
doAssert(blobsBundleOpt.isNone)
return found

View File

@ -151,15 +151,11 @@ 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], removeBlobs = false):
func ethTxs*(list: openArray[Web3Tx]):
seq[common.Transaction] {.gcsafe, raises:[RlpError].} =
result = newSeqOfCap[common.Transaction](list.len)
if removeBlobs:
for x in list:
result.add ethTx(x).removeNetworkPayload
else:
for x in list:
result.add ethTx(x)
for x in list:
result.add ethTx(x)
func storageKeys(list: seq[FixedBytes[32]]): seq[StorageKey] =
for x in list:

View File

@ -167,17 +167,17 @@ func validateEip4844Header*(
return ok()
proc validateBlobTransactionWrapper*(tx: Transaction):
proc validateBlobTransactionWrapper*(tx: PooledTransaction):
Result[void, string] {.raises: [].} =
if tx.networkPayload.isNil:
return err("tx wrapper is none")
# note: assert blobs are not malformatted
let goodFormatted = tx.versionedHashes.len ==
let goodFormatted = tx.tx.versionedHashes.len ==
tx.networkPayload.commitments.len and
tx.versionedHashes.len ==
tx.tx.versionedHashes.len ==
tx.networkPayload.blobs.len and
tx.versionedHashes.len ==
tx.tx.versionedHashes.len ==
tx.networkPayload.proofs.len
if not goodFormatted:
@ -194,12 +194,13 @@ proc validateBlobTransactionWrapper*(tx: Transaction):
return err("Failed to verify network payload of a transaction")
# Now that all commitments have been verified, check that versionedHashes matches the commitments
for i in 0 ..< tx.versionedHashes.len:
for i in 0 ..< tx.tx.versionedHashes.len:
# this additional check also done in tx validation
if tx.versionedHashes[i].data[0] != VERSIONED_HASH_VERSION_KZG:
if tx.tx.versionedHashes[i].data[0] != VERSIONED_HASH_VERSION_KZG:
return err("wrong kzg version in versioned hash at index " & $i)
if tx.versionedHashes[i] != kzgToVersionedHash(tx.networkPayload.commitments[i]):
if tx.tx.versionedHashes[i] !=
kzgToVersionedHash(tx.networkPayload.commitments[i]):
return err("tx versioned hash not match commitments at index " & $i)
ok()

View File

@ -61,8 +61,9 @@ proc validateSealer*(conf: NimbusConf, ctx: EthContext, chain: ChainRef): Result
proc generateBlock(engine: SealingEngineRef,
outBlock: var EthBlock): Result[void, string] =
outBlock = engine.txPool.assembleBlock().valueOr:
let bundle = engine.txPool.assembleBlock().valueOr:
return err(error)
outBlock = bundle.blk
if engine.chain.com.consensus == ConsensusType.POS:
# Stop the block generator if we reach TTD

View File

@ -423,7 +423,7 @@
##
import
std/[sequtils, tables],
std/[options, sequtils, tables],
./tx_pool/[tx_chain, tx_desc, tx_info, tx_item],
./tx_pool/tx_tabs,
./tx_pool/tx_tasks/[
@ -517,7 +517,7 @@ proc new*(T: type TxPoolRef; com: CommonRef; miner: EthAddress): T
# core/tx_pool.go(848): func (pool *TxPool) AddLocals(txs []..
# core/tx_pool.go(864): func (pool *TxPool) AddRemotes(txs []..
proc add*(xp: TxPoolRef; txs: openArray[Transaction]; info = "")
proc add*(xp: TxPoolRef; txs: openArray[PooledTransaction]; info = "")
{.gcsafe,raises: [CatchableError].} =
## Add a list of transactions to be processed and added to the buckets
## database. It is OK pass an empty list in which case some maintenance
@ -533,7 +533,7 @@ proc add*(xp: TxPoolRef; txs: openArray[Transaction]; info = "")
# core/tx_pool.go(854): func (pool *TxPool) AddLocals(txs []..
# core/tx_pool.go(883): func (pool *TxPool) AddRemotes(txs []..
proc add*(xp: TxPoolRef; tx: Transaction; info = "")
proc add*(xp: TxPoolRef; tx: PooledTransaction; info = "")
{.gcsafe,raises: [CatchableError].} =
## Variant of `add()` for a single transaction.
xp.add(@[tx], info)
@ -607,8 +607,14 @@ proc dirtyBuckets*(xp: TxPoolRef): bool =
## flag is also set.
xp.pDirtyBuckets
proc assembleBlock*(xp: TxPoolRef, someBaseFee: bool = false): Result[EthBlock, string]
{.gcsafe,raises: [CatchableError].} =
type EthBlockAndBlobsBundle* = object
blk*: EthBlock
blobsBundle*: Option[BlobsBundle]
proc assembleBlock*(
xp: TxPoolRef,
someBaseFee: bool = false
): Result[EthBlockAndBlobsBundle, string] {.gcsafe,raises: [CatchableError].} =
## Getter, retrieves a packed block ready for mining and signing depending
## on the internally cached block chain head, the txs in the pool and some
## tuning parameters. The following block header fields are left
@ -627,19 +633,40 @@ proc assembleBlock*(xp: TxPoolRef, someBaseFee: bool = false): Result[EthBlock,
var blk = EthBlock(
header: xp.chain.getHeader # uses updated vmState
)
var blobsBundle: BlobsBundle
for (_,nonceList) in xp.txDB.packingOrderAccounts(txItemPacked):
blk.txs.add toSeq(nonceList.incNonce).mapIt(it.tx)
for item in nonceList.incNonce:
let tx = item.pooledTx
blk.txs.add tx.tx
for k in tx.networkPayload.commitments:
blobsBundle.commitments.add k
for p in tx.networkPayload.proofs:
blobsBundle.proofs.add p
for blob in tx.networkPayload.blobs:
blobsBundle.blobs.add blob
let com = xp.chain.com
if com.forkGTE(Shanghai):
blk.withdrawals = some(com.pos.withdrawals)
if not com.forkGTE(Cancun) and blobsBundle.commitments.len > 0:
return err("PooledTransaction contains blobs prior to Cancun")
let blobsBundleOpt =
if com.forkGTE(Cancun):
doAssert blobsBundle.commitments.len == blobsBundle.blobs.len
doAssert blobsBundle.proofs.len == blobsBundle.blobs.len
options.some blobsBundle
else:
options.none BlobsBundle
if someBaseFee:
# make sure baseFee always has something
blk.header.fee = some(blk.header.fee.get(0.u256))
ok(blk)
ok EthBlockAndBlobsBundle(
blk: blk,
blobsBundle: blobsBundleOpt)
proc gasCumulative*(xp: TxPoolRef): GasInt =
## Getter, retrieves the gas that will be burned in the block after
@ -856,7 +883,7 @@ proc accountRanks*(xp: TxPoolRef): TxTabsLocality =
xp.txDB.locality
proc addRemote*(xp: TxPoolRef;
tx: Transaction; force = false): Result[void,TxInfo]
tx: PooledTransaction; force = false): Result[void,TxInfo]
{.gcsafe,raises: [CatchableError].} =
## Adds the argument transaction `tx` to the buckets database.
##
@ -890,7 +917,7 @@ proc addRemote*(xp: TxPoolRef;
ok()
proc addLocal*(xp: TxPoolRef;
tx: Transaction; force = false): Result[void,TxInfo]
tx: PooledTransaction; force = false): Result[void,TxInfo]
{.gcsafe,raises: [CatchableError].} =
## Adds the argument transaction `tx` to the buckets database.
##

View File

@ -42,7 +42,7 @@ type
TxItemRef* = ref object of RootObj ##\
## Data container with transaction and meta data. Entries are *read-only*\
## by default, for some there is a setter available.
tx: Transaction ## Transaction data
tx: PooledTransaction ## Transaction data
itemID: Hash256 ## Transaction hash
timeStamp: Time ## Time when added
sender: EthAddress ## Sender account address
@ -112,10 +112,10 @@ proc init*(item: TxItemRef; status: TxItemStatus; info: string) =
item.timeStamp = utcTime()
item.reject = txInfoOk
proc new*(T: type TxItemRef; tx: Transaction; itemID: Hash256;
proc new*(T: type TxItemRef; tx: PooledTransaction; itemID: Hash256;
status: TxItemStatus; info: string): Result[T,void] {.gcsafe,raises: [].} =
## Create item descriptor.
let rc = tx.ecRecover
let rc = tx.tx.ecRecover
if rc.isErr:
return err()
ok(T(itemID: itemID,
@ -125,7 +125,7 @@ proc new*(T: type TxItemRef; tx: Transaction; itemID: Hash256;
info: info,
status: status))
proc new*(T: type TxItemRef; tx: Transaction;
proc new*(T: type TxItemRef; tx: PooledTransaction;
reject: TxInfo; status: TxItemStatus; info: string): T {.gcsafe,raises: [].} =
## Create incomplete item descriptor, so meta-data can be stored (e.g.
## for holding in the waste basket to be investigated later.)
@ -150,6 +150,10 @@ proc itemID*(tx: Transaction): Hash256 =
## Getter, transaction ID
tx.rlpHash
proc itemID*(tx: PooledTransaction): Hash256 =
## Getter, transaction ID
tx.tx.rlpHash
# core/types/transaction.go(297): func (tx *Transaction) Cost() *big.Int {
proc cost*(tx: Transaction): UInt256 =
## Getter (go/ref compat): gas * gasPrice + value.
@ -210,10 +214,14 @@ proc timeStamp*(item: TxItemRef): Time =
## Getter
item.timeStamp
proc tx*(item: TxItemRef): Transaction =
proc pooledTx*(item: TxItemRef): PooledTransaction =
## Getter
item.tx
proc tx*(item: TxItemRef): Transaction =
## Getter
item.tx.tx
func rejectInfo*(item: TxItemRef): string =
## Getter
result = $item.reject

View File

@ -138,7 +138,7 @@ proc new*(T: type TxTabsRef): T {.gcsafe,raises: [].} =
proc insert*(
xp: TxTabsRef;
tx: var Transaction;
tx: var PooledTransaction;
status = txItemPending;
info = ""): Result[void,TxInfo]
{.gcsafe,raises: [CatchableError].} =
@ -221,7 +221,7 @@ proc dispose*(xp: TxTabsRef; item: TxItemRef; reason: TxInfo): bool
xp.byRejects[item.itemID] = item
return true
proc reject*(xp: TxTabsRef; tx: var Transaction;
proc reject*(xp: TxTabsRef; tx: var PooledTransaction;
reason: TxInfo; status = txItemPending; info = "") =
## Similar to dispose but for a tx without the item wrapper, the function
## imports the tx into the waste basket (e.g. after it could not
@ -239,7 +239,7 @@ proc reject*(xp: TxTabsRef; item: TxItemRef; reason: TxInfo) =
item.reject = reason
xp.byRejects[item.itemID] = item
proc reject*(xp: TxTabsRef; tx: Transaction;
proc reject*(xp: TxTabsRef; tx: PooledTransaction;
reason: TxInfo; status = txItemPending; info = "") =
## Variant of `reject()`
var ty = tx

View File

@ -160,7 +160,7 @@ proc addTx*(xp: TxPoolRef; item: TxItemRef): bool
# core/tx_pool.go(883): func (pool *TxPool) AddRemotes(txs []..
# core/tx_pool.go(889): func (pool *TxPool) addTxs(txs []*types.Transaction, ..
proc addTxs*(xp: TxPoolRef;
txs: openArray[Transaction]; info = ""): TxAddStats
txs: openArray[PooledTransaction]; info = ""): TxAddStats
{.discardable,gcsafe,raises: [CatchableError].} =
## Add a list of transactions. The list is sorted after nonces and txs are
## tested and stored into either of the `pending` or `staged` buckets, or
@ -181,7 +181,7 @@ proc addTxs*(xp: TxPoolRef;
for tx in txs.items:
var reason: TxInfo
if tx.txType == TxEip4844:
if tx.tx.txType == TxEip4844:
let res = tx.validateBlobTransactionWrapper()
if res.isErr:
# move item to waste basket

View File

@ -38,7 +38,7 @@ logScope:
proc checkTxBasic(xp: TxPoolRef; item: TxItemRef): bool =
let res = validateTxBasic(
item.tx.removeNetworkPayload,
item.tx,
xp.chain.nextFork,
# A new transaction of the next fork may be
# coming before the fork activated
@ -234,7 +234,8 @@ proc classifyValidatePacked*(xp: TxPoolRef;
tx = item.tx.eip1559TxNormalization(xp.chain.baseFee.GasInt)
excessBlobGas = calcExcessBlobGas(vmState.parent)
roDB.validateTransaction(tx.removeNetworkPayload, item.sender, gasLimit, baseFee, excessBlobGas, fork).isOk
roDB.validateTransaction(
tx, 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

@ -31,7 +31,7 @@ type
## Diff data, txs changes that apply after changing the head\
## insertion point of the block chain
addTxs*: KeyedQueue[Hash256,Transaction] ##\
addTxs*: KeyedQueue[Hash256, PooledTransaction] ##\
## txs to add; using a queue makes it more intuive to delete
## items while travesing the queue in a loop.
@ -50,7 +50,13 @@ proc insert(xp: TxPoolRef; kq: TxHeadDiffRef; blockHash: Hash256)
{.gcsafe,raises: [CatchableError].} =
let db = xp.chain.com.db
for tx in db.getBlockBody(blockHash).transactions:
kq.addTxs[tx.itemID] = tx
if tx.versionedHashes.len > 0:
# EIP-4844 blobs are not persisted and cannot be re-broadcasted.
# Note that it is also not possible to crete a cache in all cases,
# as we may have never seen the actual blob sidecar while syncing.
# Only the consensus layer persists the blob sidecar.
continue
kq.addTxs[tx.itemID] = PooledTransaction(tx: tx)
proc remove(xp: TxPoolRef; kq: TxHeadDiffRef; blockHash: Hash256)
{.gcsafe,raises: [CatchableError].} =

View File

@ -141,7 +141,7 @@ proc runTxCommit(pst: TxPackerStateRef; item: TxItemRef; gasBurned: GasInt)
pst.blobGasUsed += item.tx.getTotalBlobGas
# Update txRoot
pst.tr.put(rlp.encode(inx), rlp.encode(item.tx.removeNetworkPayload))
pst.tr.put(rlp.encode(inx), rlp.encode(item.tx))
# Add the item to the `packed` bucket. This implicitely increases the
# receipts index `inx` at the next visit of this function.

View File

@ -35,7 +35,7 @@ let
# Public functions
# ------------------------------------------------------------------------------
proc recoverItem*(xp: TxPoolRef; tx: Transaction; status = txItemPending;
proc recoverItem*(xp: TxPoolRef; tx: PooledTransaction; status = txItemPending;
info = ""; acceptExisting = false): Result[TxItemRef,TxInfo] =
## Recover item from waste basket or create new. It is an error if the item
## is in the buckets database, already.

View File

@ -267,9 +267,6 @@ proc validateTxBasic*(
"index=$1, len=$2" % [$i, $acl.storageKeys.len])
if tx.txType >= TxEip4844:
if tx.networkPayload.isNil.not:
return err("invalid tx: network payload should not appear in block validation")
if tx.to.isNone:
return err("invalid tx: destination must be not empty")

View File

@ -558,8 +558,8 @@ proc persistTransactions*(
for idx, tx in transactions:
let
encodedKey = rlp.encode(idx)
encodedTx = rlp.encode(tx.removeNetworkPayload)
txHash = rlpHash(tx) # beware EIP-4844
encodedTx = rlp.encode(tx)
txHash = rlpHash(tx)
blockKey = transactionHashToBlockKey(txHash)
txKey: TransactionKey = (blockNumber, idx)
mpt.merge(encodedKey, encodedTx).isOkOr:

View File

@ -1364,8 +1364,8 @@ proc sendRawTransaction(ud: RootRef, params: Args, parent: Node): RespResult {.a
let ctx = GraphqlContextRef(ud)
try:
let data = hexToSeqByte(params[0].val.stringVal)
let tx = decodeTx(data) # we want to know if it is a valid tx blob
let txHash = rlpHash(tx) # beware EIP-4844
let tx = decodePooledTx(data) # we want to know if it is a valid tx blob
let txHash = rlpHash(tx)
ctx.txPool.add(tx)

View File

@ -10,7 +10,7 @@
{.push raises: [].}
import
std/[times, tables, typetraits],
std/[sequtils, times, tables, typetraits],
json_rpc/rpcserver, stint, stew/byteutils,
json_serialization, web3/conversions, json_serialization/std/options,
eth/common/eth_types_json_serialization,
@ -282,8 +282,27 @@ proc setupEthRpc*(
tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1)
eip155 = com.isEIP155(com.syncCurrent)
signedTx = signTransaction(tx, acc.privateKey, com.chainId, eip155)
networkPayload =
if signedTx.txType == TxEip4844:
if data.blobs.isNone or data.commitments.isNone or data.proofs.isNone:
raise newException(ValueError, "EIP-4844 transaction needs blobs")
if data.blobs.get.len != signedTx.versionedHashes.len:
raise newException(ValueError, "Incorrect number of blobs")
if data.commitments.get.len != signedTx.versionedHashes.len:
raise newException(ValueError, "Incorrect number of commitments")
if data.proofs.get.len != signedTx.versionedHashes.len:
raise newException(ValueError, "Incorrect number of proofs")
NetworkPayload(
blobs: data.blobs.get.mapIt it.NetworkBlob,
commitments: data.commitments.get.mapIt eth_types.KzgCommitment(it),
proofs: data.proofs.get.mapIt eth_types.KzgProof(it))
else:
if data.blobs.isSome or data.commitments.isSome or data.proofs.isSome:
raise newException(ValueError, "Blobs require EIP-4844 transaction")
nil
pooledTx = PooledTransaction(tx: signedTx, networkPayload: networkPayload)
txPool.add(signedTx)
txPool.add(pooledTx)
result = rlpHash(signedTx).w3Hash
server.rpc("eth_sendRawTransaction") do(txBytes: seq[byte]) -> Web3Hash:
@ -293,10 +312,10 @@ proc setupEthRpc*(
## Returns the transaction hash, or the zero hash if the transaction is not yet available.
## Note: Use eth_getTransactionReceipt to get the contract address, after the transaction was mined, when you created a contract.
let
signedTx = decodeTx(txBytes)
txHash = rlpHash(signedTx)
pooledTx = decodePooledTx(txBytes)
txHash = rlpHash(pooledTx)
txPool.add(signedTx)
txPool.add(pooledTx)
let res = txPool.inPoolAndReason(txHash)
if res.isErr:
raise newException(ValueError, res.error)

View File

@ -442,14 +442,14 @@ method getReceipts*(ctx: EthWireRef,
method getPooledTxs*(ctx: EthWireRef,
hashes: openArray[Hash256]):
Result[seq[Transaction], string]
Result[seq[PooledTransaction], string]
{.gcsafe.} =
let txPool = ctx.txPool
var list: seq[Transaction]
var list: seq[PooledTransaction]
for txHash in hashes:
let res = txPool.getItem(txHash)
if res.isOk:
list.add res.value.tx
list.add res.value.pooledTx
else:
trace "handlers.getPooledTxs: tx not found", txHash
ok(list)
@ -522,7 +522,11 @@ method handleAnnouncedTxs*(ctx: EthWireRef,
txHashes.add rlpHash(tx)
ctx.addToKnownByPeer(txHashes, peer)
ctx.txPool.add(txs)
for tx in txs:
if tx.versionedHashes.len > 0:
# EIP-4844 blobs are not persisted and cannot be broadcasted
continue
ctx.txPool.add PooledTransaction(tx: tx)
var newTxHashes = newSeqOfCap[Hash256](txHashes.len)
var validTxs = newSeqOfCap[Transaction](txHashes.len)

View File

@ -65,7 +65,7 @@ method getReceipts*(ctx: EthWireBase,
method getPooledTxs*(ctx: EthWireBase,
hashes: openArray[Hash256]):
Result[seq[Transaction], string]
Result[seq[PooledTransaction], string]
{.base, gcsafe.} =
notImplemented("getPooledTxs")

View File

@ -266,7 +266,8 @@ p2pProtocol eth66(version = ethVersion,
await response.send(txs.get)
# User message 0x0a: PooledTransactions.
proc pooledTransactions(peer: Peer, transactions: openArray[Transaction])
proc pooledTransactions(
peer: Peer, transactions: openArray[PooledTransaction])
nextId 0x0d

View File

@ -267,7 +267,8 @@ p2pProtocol eth67(version = ethVersion,
await response.send(txs.get)
# User message 0x0a: PooledTransactions.
proc pooledTransactions(peer: Peer, transactions: openArray[Transaction])
proc pooledTransactions(
peer: Peer, transactions: openArray[PooledTransaction])
# User message 0x0d: GetNodeData -- removed, was so 66ish
# User message 0x0e: NodeData -- removed, was so 66ish

View File

@ -270,7 +270,8 @@ p2pProtocol eth68(version = ethVersion,
await response.send(txs.get)
# User message 0x0a: PooledTransactions.
proc pooledTransactions(peer: Peer, transactions: openArray[Transaction])
proc pooledTransactions(
peer: Peer, transactions: openArray[PooledTransaction])
# User message 0x0d: GetNodeData -- removed, was so 66ish
# User message 0x0e: NodeData -- removed, was so 66ish

View File

@ -233,3 +233,9 @@ proc decodeTx*(bytes: openArray[byte]): Transaction =
result = rlp.read(Transaction)
if rlp.hasData:
raise newException(RlpError, "rlp: input contains more than one value")
proc decodePooledTx*(bytes: openArray[byte]): PooledTransaction =
var rlp = rlpFromBytes(bytes)
result = rlp.read(PooledTransaction)
if rlp.hasData:
raise newException(RlpError, "rlp: input contains more than one value")

2
vendor/nim-eth vendored

@ -1 +1 @@
Subproject commit d8209f623f837d14c43a9e3fd464b0e199c5d180
Subproject commit a662fc1aa70c48c8ab9c18c6a25b1d8578925c3e

2
vendor/nim-web3 vendored

@ -1 +1 @@
Subproject commit 9620fee53f630efcc4b6a2c0cd9f22bfcb376928
Subproject commit de87f860874be944cdc3dfd08765c687fff736c4