This commit is contained in:
parent
b54ec688dd
commit
b0158c0619
|
@ -102,7 +102,7 @@
|
|||
path = vendor/nim-web3
|
||||
url = https://github.com/status-im/nim-web3.git
|
||||
ignore = dirty
|
||||
branch = master
|
||||
branch = feat/eip-6493
|
||||
[submodule "vendor/nim-snappy"]
|
||||
path = vendor/nim-snappy
|
||||
url = https://github.com/status-im/nim-snappy.git
|
||||
|
@ -172,7 +172,7 @@
|
|||
path = vendor/nim-ssz-serialization
|
||||
url = https://github.com/status-im/nim-ssz-serialization.git
|
||||
ignore = untracked
|
||||
branch = master
|
||||
branch = feat/eip-6493
|
||||
[submodule "vendor/nimbus-eth2"]
|
||||
path = vendor/nimbus-eth2
|
||||
url = https://github.com/status-im/nimbus-eth2.git
|
||||
|
|
|
@ -21,7 +21,8 @@ import
|
|||
proc processChainData(cd: ChainData): TestStatus =
|
||||
let
|
||||
networkId = NetworkId(cd.params.config.chainId)
|
||||
com = CommonRef.new(newCoreDbRef DefaultDbMemory,
|
||||
com = CommonRef.new(
|
||||
newCoreDbRef(DefaultDbMemory, cd.params.config.chainId),
|
||||
pruneTrie = false,
|
||||
networkId,
|
||||
cd.params
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import
|
||||
std/[options, strutils, typetraits],
|
||||
stew/byteutils,
|
||||
eth/common/transaction,
|
||||
./blobs,
|
||||
../types,
|
||||
../tx_sender,
|
||||
|
@ -656,10 +657,11 @@ proc generateInvalidPayload*(sender: TxSender, data: ExecutableData, payloadFiel
|
|||
|
||||
case payloadField
|
||||
of InvalidTransactionSignature:
|
||||
var sig = CustSig(R: baseTx.R - 1.u256)
|
||||
var sig = CustSig(
|
||||
R: ecdsa_unpack_signature(baseTx.signature.ecdsa_signature).r - 1.u256)
|
||||
custTx.signature = some(sig)
|
||||
of InvalidTransactionNonce:
|
||||
custTx.nonce = some(baseTx.nonce - 1)
|
||||
custTx.nonce = some(baseTx.payload.nonce - 1)
|
||||
of InvalidTransactionGas:
|
||||
custTx.gas = some(0.GasInt)
|
||||
of InvalidTransactionGasPrice:
|
||||
|
@ -670,11 +672,12 @@ proc generateInvalidPayload*(sender: TxSender, data: ExecutableData, payloadFiel
|
|||
# Vault account initially has 0x123450000000000000000, so this value should overflow
|
||||
custTx.value = some(UInt256.fromHex("0x123450000000000000001"))
|
||||
of InvalidTransactionChainID:
|
||||
custTx.chainId = some(ChainId(baseTx.chainId.uint64 + 1))
|
||||
custTx.chainId = some(ChainId(sender.chainId.uint64 + 1))
|
||||
else: discard
|
||||
|
||||
let acc = sender.getNextAccount()
|
||||
let modifiedTx = sender.customizeTransaction(acc, baseTx, custTx)
|
||||
let modifiedTx = sender.customizeTransaction(
|
||||
acc, baseTx, custTx, sender.chainId)
|
||||
customPayloadMod = CustomPayloadData(
|
||||
transactions: some(@[modifiedTx]),
|
||||
)
|
||||
|
|
|
@ -12,7 +12,7 @@ import
|
|||
std/[tables, strutils, typetraits],
|
||||
stint,
|
||||
eth/[common, rlp],
|
||||
eth/common/eth_types_rlp,
|
||||
eth/common/[eth_types_rlp, transaction],
|
||||
chronicles,
|
||||
stew/[results, byteutils],
|
||||
kzg4844/kzg_ex as kzg,
|
||||
|
@ -53,8 +53,9 @@ 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: PooledTransaction) =
|
||||
let txHash = rlpHash(tx)
|
||||
proc addBlobTransaction*(
|
||||
pool: TestBlobTxPool, tx: PooledTransaction, chainId: ChainId) =
|
||||
let txHash = tx.tx.compute_tx_hash(chainId)
|
||||
pool.transactions[txHash] = tx
|
||||
|
||||
proc `==`(a: openArray[AccessTuple], b: openArray[AccessPair]): bool =
|
||||
|
@ -73,7 +74,10 @@ proc `==`(a: openArray[AccessTuple], b: openArray[AccessPair]): bool =
|
|||
return true
|
||||
|
||||
# Test two different transactions with the same blob, and check the blob bundle.
|
||||
proc verifyTransactionFromNode*(client: RpcClient, tx: Transaction): Result[void, string] =
|
||||
proc verifyTransactionFromNode*(
|
||||
client: RpcClient,
|
||||
tx: Transaction,
|
||||
chainId: ChainId): Result[void, string] =
|
||||
let txHash = tx.rlpHash
|
||||
let res = client.txByHash(txHash)
|
||||
if res.isErr:
|
||||
|
@ -81,29 +85,34 @@ proc verifyTransactionFromNode*(client: RpcClient, tx: Transaction): Result[void
|
|||
let returnedTx = res.get()
|
||||
|
||||
# Verify that the tx fields are all the same
|
||||
if returnedTx.nonce != tx.nonce:
|
||||
return err("nonce mismatch: $1 != $2" % [$returnedTx.nonce, $tx.nonce])
|
||||
if returnedTx.nonce != tx.payload.nonce:
|
||||
return err("nonce mismatch: $1 != $2" %
|
||||
[$returnedTx.nonce, $tx.payload.nonce])
|
||||
|
||||
if returnedTx.gasLimit != tx.gasLimit:
|
||||
return err("gas mismatch: $1 != $2" % [$returnedTx.gasLimit, $tx.gasLimit])
|
||||
if returnedTx.gasLimit != tx.payload.gas:
|
||||
return err("gas mismatch: $1 != $2" %
|
||||
[$returnedTx.gasLimit, $tx.payload.gas])
|
||||
|
||||
if returnedTx.gasPrice != tx.gasPrice:
|
||||
return err("gas price mismatch: $1 != $2" % [$returnedTx.gasPrice, $tx.gasPrice])
|
||||
if returnedTx.gasPrice != tx.payload.max_fee_per_gas:
|
||||
return err("gas price mismatch: $1 != $2" %
|
||||
[$returnedTx.gasPrice, $tx.payload.max_fee_per_gas])
|
||||
|
||||
if returnedTx.value != tx.value:
|
||||
return err("value mismatch: $1 != $2" % [$returnedTx.value, $tx.value])
|
||||
if returnedTx.value != tx.payload.value:
|
||||
return err("value mismatch: $1 != $2" %
|
||||
[$returnedTx.value, $tx.payload.value])
|
||||
|
||||
if returnedTx.to != tx.to:
|
||||
return err("to mismatch: $1 != $2" % [$returnedTx.to, $tx.to])
|
||||
if returnedTx.to != tx.payload.to:
|
||||
return err("to mismatch: $1 != $2" % [$returnedTx.to, $tx.payload.to])
|
||||
|
||||
if returnedTx.payload != tx.payload:
|
||||
return err("data mismatch: $1 != $2" % [returnedTx.payload.toHex, tx.payload.toHex])
|
||||
if returnedTx.payload != tx.payload.input:
|
||||
return err("data mismatch: $1 != $2" %
|
||||
[returnedTx.payload.input.toHex, tx.payload.input.toHex])
|
||||
|
||||
if returnedTx.accessList.isNone:
|
||||
if returnedTx.payload.access_list.isNone:
|
||||
return err("expect accessList is some")
|
||||
|
||||
let ac = returnedTx.accessList.get
|
||||
if ac != tx.accessList:
|
||||
let ac = returnedTx.payload.access_list.get
|
||||
if ac != tx.payload.access_list:
|
||||
return err("access list mismatch")
|
||||
|
||||
if returnedTx.chainId.isNone:
|
||||
|
|
|
@ -12,7 +12,7 @@ import
|
|||
std/[tables],
|
||||
chronicles,
|
||||
stew/[byteutils],
|
||||
eth/common, chronos,
|
||||
eth/[common, common/transaction], chronos,
|
||||
json_rpc/rpcclient,
|
||||
web3/execution_types,
|
||||
../../../nimbus/beacon/web3_eth_conv,
|
||||
|
@ -94,8 +94,9 @@ type
|
|||
proc collectBlobHashes(list: openArray[Web3Tx]): seq[common.Hash256] =
|
||||
for w3tx in list:
|
||||
let tx = ethTx(w3tx)
|
||||
for h in tx.versionedHashes:
|
||||
result.add h
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
for h in tx.payload.blob_versioned_hashes.unsafeGet:
|
||||
result.add h
|
||||
|
||||
func latestExecutableData*(cl: CLMocker): ExecutableData =
|
||||
ExecutableData(
|
||||
|
@ -373,12 +374,14 @@ proc getNextPayload(cl: CLMocker): bool =
|
|||
|
||||
return true
|
||||
|
||||
func versionedHashes(payload: ExecutionPayload): seq[Web3Hash] =
|
||||
func versionedHashes(payload: ExecutionPayload, chainId: ChainId): seq[Web3Hash] =
|
||||
result = newSeqOfCap[BlockHash](payload.transactions.len)
|
||||
for x in payload.transactions:
|
||||
let tx = rlp.decode(distinctBase(x), Transaction)
|
||||
for vs in tx.versionedHashes:
|
||||
result.add w3Hash vs
|
||||
let tx = Transaction.fromBytes(distinctBase(x), chainId).valueOr:
|
||||
raise (ref MalformedRlpError)(msg: "Invalid transaction in payload")
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
for vs in tx.payload.blob_versioned_hashes.get:
|
||||
result.add w3Hash vs
|
||||
|
||||
proc broadcastNewPayload(cl: CLMocker,
|
||||
eng: EngineEnv,
|
||||
|
@ -387,10 +390,10 @@ proc broadcastNewPayload(cl: CLMocker,
|
|||
of Version.V1: return eng.client.newPayloadV1(payload.V1)
|
||||
of Version.V2: return eng.client.newPayloadV2(payload.V2)
|
||||
of Version.V3: return eng.client.newPayloadV3(payload.V3,
|
||||
versionedHashes(payload),
|
||||
versionedHashes(payload, cl.com.chainId),
|
||||
cl.latestPayloadAttributes.parentBeaconBlockRoot.get)
|
||||
of Version.V4: return eng.client.newPayloadV4(payload.V4,
|
||||
versionedHashes(payload),
|
||||
versionedHashes(payload, cl.com.chainId),
|
||||
cl.latestPayloadAttributes.parentBeaconBlockRoot.get)
|
||||
|
||||
proc broadcastNextNewPayload(cl: CLMocker): bool =
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
import
|
||||
chronicles,
|
||||
eth/common/transaction,
|
||||
./engine_spec,
|
||||
../helper,
|
||||
../cancun/customizer,
|
||||
|
@ -304,8 +305,9 @@ method getName(cs: PayloadBuildAfterInvalidPayloadTest): string =
|
|||
proc collectBlobHashes(list: openArray[Web3Tx]): seq[common.Hash256] =
|
||||
for w3tx in list:
|
||||
let tx = ethTx(w3tx)
|
||||
for h in tx.versionedHashes:
|
||||
result.add h
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
for h in tx.payload.blob_versioned_hashes.unsafeGet:
|
||||
result.add h
|
||||
|
||||
method execute(cs: PayloadBuildAfterInvalidPayloadTest, env: TestEnv): bool =
|
||||
# Add a second client to build the invalid payload
|
||||
|
@ -442,7 +444,8 @@ method execute(cs: InvalidTxChainIDTest, env: TestEnv): bool =
|
|||
testCond pbRes
|
||||
|
||||
# Verify that the latest payload built does NOT contain the invalid chain Tx
|
||||
let txHash = shadow.invalidTx.rlpHash
|
||||
let txHash = shadow.invalidTx.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId)
|
||||
if txInPayload(env.clMock.latestPayloadBuilt, txHash):
|
||||
fatal "Invalid chain ID tx was included in payload"
|
||||
return false
|
||||
|
|
|
@ -70,7 +70,9 @@ method execute(cs: PrevRandaoTransactionTest, env: TestEnv): bool =
|
|||
onForkchoiceBroadcast: proc(): bool =
|
||||
# Check the transaction tracing, which is client specific
|
||||
let expectedPrevRandao = env.clMock.prevRandaoHistory[env.clMock.latestHeader.blockNumber.truncate(uint64)+1]
|
||||
let res = debugPrevRandaoTransaction(env.engine.client, shadow.txs[shadow.currentTxIndex-1], expectedPrevRandao)
|
||||
let res = debugPrevRandaoTransaction(
|
||||
env.engine.client, shadow.txs[shadow.currentTxIndex-1],
|
||||
expectedPrevRandao, env.conf.networkParams.config.chainId)
|
||||
testCond res.isOk:
|
||||
fatal "Error during transaction tracing", msg=res.error
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# according to those terms.
|
||||
|
||||
import
|
||||
eth/common,
|
||||
eth/[common, common/transaction],
|
||||
chronicles,
|
||||
stew/byteutils,
|
||||
./engine_spec,
|
||||
|
@ -134,11 +134,11 @@ method getName(cs: TransactionReOrgTest): string =
|
|||
name.add ", " & $cs.scenario
|
||||
return name
|
||||
|
||||
proc txHash(shadow: ShadowTx): common.Hash256 =
|
||||
proc txHash(shadow: ShadowTx, chainId: ChainId): common.Hash256 =
|
||||
if shadow.tx.isNone:
|
||||
error "SHADOW TX IS NONE"
|
||||
return
|
||||
shadow.tx.get.rlpHash
|
||||
shadow.tx.get.tx.compute_tx_hash(chainId)
|
||||
|
||||
# Test transaction status after a forkchoiceUpdated re-orgs to an alternative hash where a transaction is not present
|
||||
method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
||||
|
@ -204,7 +204,8 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
shadow.tx = some(shadow.sendTransaction(i))
|
||||
|
||||
# Get the receipt
|
||||
let receipt = env.engine.client.txReceipt(shadow.txHash)
|
||||
let receipt = env.engine.client.txReceipt(
|
||||
shadow.txHash(env.conf.networkParams.config.chainId))
|
||||
testCond receipt.isErr:
|
||||
fatal "Receipt obtained before tx included in block"
|
||||
|
||||
|
@ -213,7 +214,9 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
onGetpayload: proc(): bool =
|
||||
# Check that indeed the payload contains the transaction
|
||||
if shadow.tx.isSome:
|
||||
testCond txInPayload(env.clMock.latestPayloadBuilt, shadow.txHash):
|
||||
testCond txInPayload(
|
||||
env.clMock.latestPayloadBuilt,
|
||||
shadow.txHash(env.conf.networkParams.config.chainId)):
|
||||
fatal "Payload built does not contain the transaction"
|
||||
|
||||
if cs.scenario in [TransactionReOrgScenarioReOrgDifferentBlock, TransactionReOrgScenarioNewPayloadOnRevert]:
|
||||
|
@ -256,7 +259,8 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
g.expectNoError()
|
||||
|
||||
let payload = g.get.executionPayload
|
||||
testCond txInPayload(payload, shadow.nextTx.rlpHash):
|
||||
testCond txInPayload(payload, shadow.nextTx.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId)):
|
||||
fatal "Payload built does not contain the transaction"
|
||||
|
||||
# Send the new payload and forkchoiceUpdated to it
|
||||
|
@ -273,7 +277,9 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
onNewPayloadBroadcast: proc(): bool =
|
||||
if shadow.tx.isSome:
|
||||
# Get the receipt
|
||||
let receipt = env.engine.client.txReceipt(shadow.txHash)
|
||||
let receipt = env.engine.client.txReceipt(
|
||||
shadow.tx.unsafeGet.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId))
|
||||
testCond receipt.isErr:
|
||||
fatal "Receipt obtained before tx included in block (NewPayload)"
|
||||
return true
|
||||
|
@ -282,7 +288,9 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
if cs.scenario != TransactionReOrgScenarioReOrgBackIn:
|
||||
# Transaction is now in the head of the canonical chain, re-org and verify it's removed
|
||||
# Get the receipt
|
||||
var txt = env.engine.client.txReceipt(shadow.txHash)
|
||||
var txt = env.engine.client.txReceipt(
|
||||
shadow.tx.get.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId))
|
||||
txt.expectBlockHash(ethHash env.clMock.latestForkchoice.headBlockHash)
|
||||
|
||||
testCond shadow.payload.parentHash == env.clMock.latestPayloadBuilt.parentHash:
|
||||
|
@ -311,7 +319,9 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
let p = env.engine.client.namedHeader(Head)
|
||||
p.expectHash(ethHash shadow.payload.blockHash)
|
||||
|
||||
txt = env.engine.client.txReceipt(shadow.txHash)
|
||||
txt = env.engine.client.txReceipt(
|
||||
shadow.tx.get.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId))
|
||||
if cs.scenario == TransactionReOrgScenarioReOrgOut:
|
||||
testCond txt.isErr:
|
||||
fatal "Receipt was obtained when the tx had been re-org'd out"
|
||||
|
@ -328,7 +338,9 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
|
||||
if shadow.tx.isSome:
|
||||
# Now it should be back with main payload
|
||||
let txt = env.engine.client.txReceipt(shadow.txHash)
|
||||
let txt = env.engine.client.txReceipt(
|
||||
shadow.tx.get.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId))
|
||||
txt.expectBlockHash(ethHash env.clMock.latestForkchoice.headBlockHash)
|
||||
|
||||
if cs.scenario != TransactionReOrgScenarioReOrgBackIn:
|
||||
|
@ -347,11 +359,16 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
# Produce one last block and verify that the block contains the transaction
|
||||
let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onForkchoiceBroadcast: proc(): bool =
|
||||
testCond txInPayload(env.clMock.latestPayloadBuilt, shadow.txHash):
|
||||
testCond txInPayload(
|
||||
env.clMock.latestPayloadBuilt,
|
||||
shadow.tx.get.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId)):
|
||||
fatal "Payload built does not contain the transaction"
|
||||
|
||||
# Get the receipt
|
||||
let receipt = env.engine.client.txReceipt(shadow.txHash)
|
||||
let receipt = env.engine.client.txReceipt(
|
||||
shadow.tx.get.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId))
|
||||
testCond receipt.isOk:
|
||||
fatal "Receipt not obtained after tx included in block"
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# according to those terms.
|
||||
|
||||
import
|
||||
eth/common,
|
||||
eth/[common, common/transaction],
|
||||
chronicles,
|
||||
./engine_spec
|
||||
|
||||
|
@ -64,7 +64,8 @@ method execute(cs: BlockStatus, env: TestEnv): bool =
|
|||
)
|
||||
|
||||
let tx = env.makeNextTx(tc)
|
||||
shadow.txHash = tx.rlpHash
|
||||
shadow.txHash = tx.tx.compute_tx_hash(
|
||||
env.conf.networkParams.config.chainId)
|
||||
let ok = env.sendTx(tx)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
|
|
|
@ -56,7 +56,7 @@ method execute(cs: SuggestedFeeRecipientTest, env: TestEnv): bool =
|
|||
testCond env.clMock.produceSingleBlock(BlockProcessCallbacks())
|
||||
|
||||
# Calculate the fees and check that they match the balance of the fee recipient
|
||||
let r = env.engine.client.latestBlock()
|
||||
let r = env.engine.client.latestBlock(env.conf.networkParams.config.chainId)
|
||||
testCond r.isOk:
|
||||
error "cannot get latest header", msg=r.error
|
||||
|
||||
|
@ -72,14 +72,16 @@ method execute(cs: SuggestedFeeRecipientTest, env: TestEnv): bool =
|
|||
|
||||
var feeRecipientFees = 0.u256
|
||||
for tx in blockIncluded.txs:
|
||||
let effGasTip = tx.effectiveGasTip(blockIncluded.header.fee)
|
||||
let effGasTip = tx.effectiveGasTip(
|
||||
blockIncluded.header.fee.get(UInt256.zero))
|
||||
|
||||
let r = env.engine.client.txReceipt(tx.rlpHash)
|
||||
testCond r.isOk:
|
||||
fatal "unable to obtain receipt", msg=r.error
|
||||
|
||||
let receipt = r.get
|
||||
feeRecipientFees = feeRecipientFees + effGasTip.u256 * receipt.gasUsed.u256
|
||||
feeRecipientFees =
|
||||
feeRecipientFees + effGasTip.uint64.u256 * receipt.gasUsed.u256
|
||||
|
||||
|
||||
var s = env.engine.client.balanceAt(feeRecipient)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
import
|
||||
std/[times, json, strutils],
|
||||
stew/byteutils,
|
||||
eth/[common, common/eth_types, rlp], chronos,
|
||||
eth/[common, common/eth_types, common/transaction], chronos,
|
||||
json_rpc/[rpcclient, errors, jsonmarshal],
|
||||
../../../nimbus/beacon/web3_eth_conv,
|
||||
./types
|
||||
|
@ -206,8 +206,9 @@ proc newPayloadV4*(client: RpcClient,
|
|||
proc collectBlobHashes(list: openArray[Web3Tx]): seq[Web3Hash] =
|
||||
for w3tx in list:
|
||||
let tx = ethTx(w3tx)
|
||||
for h in tx.versionedHashes:
|
||||
result.add w3Hash(h)
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
for h in tx.payload.blob_versioned_hashes.unsafeGet:
|
||||
result.add w3Hash(h)
|
||||
|
||||
proc newPayload*(client: RpcClient,
|
||||
payload: ExecutionPayload,
|
||||
|
@ -308,30 +309,95 @@ func vHashes(x: Option[seq[Web3Hash]]): seq[common.Hash256] =
|
|||
if x.isNone: return
|
||||
else: ethHashes(x.get)
|
||||
|
||||
proc toTransaction(tx: TransactionObject): Transaction =
|
||||
common.Transaction(
|
||||
txType : tx.`type`.get(0.Web3Quantity).TxType,
|
||||
chainId : tx.chainId.get(0.Web3Quantity).ChainId,
|
||||
nonce : tx.nonce.AccountNonce,
|
||||
gasPrice : tx.gasPrice.GasInt,
|
||||
maxPriorityFee : tx.maxPriorityFeePerGas.get(0.Web3Quantity).GasInt,
|
||||
maxFee : tx.maxFeePerGas.get(0.Web3Quantity).GasInt,
|
||||
gasLimit : tx.gas.GasInt,
|
||||
to : ethAddr tx.to,
|
||||
value : tx.value,
|
||||
payload : tx.input,
|
||||
accessList : ethAccessList(tx.accessList),
|
||||
maxFeePerBlobGas: tx.maxFeePerBlobGas.get(0.u256),
|
||||
versionedHashes : vHashes(tx.blobVersionedHashes),
|
||||
V : tx.v.int64,
|
||||
R : tx.r,
|
||||
S : tx.s,
|
||||
)
|
||||
proc toTransaction(tx: TransactionObject, chain_id: ChainId): Transaction =
|
||||
var
|
||||
payload: TransactionPayload
|
||||
gasPrice: Opt[UInt256]
|
||||
txChainId: Opt[ChainId]
|
||||
v: UInt256
|
||||
r: UInt256
|
||||
s: UInt256
|
||||
if tx.`type`.isSome:
|
||||
payload.tx_type.ok tx.`type`.get.TxType
|
||||
if tx.chainId.isSome:
|
||||
txChainId.ok tx.chainId.get.ChainId
|
||||
payload.nonce = tx.nonce.AccountNonce
|
||||
payload.max_fee_per_gas = distinctBase(tx.gasPrice).u256
|
||||
if tx.maxPriorityFeePerGas.isSome:
|
||||
payload.max_priority_fee_per_gas.ok(
|
||||
distinctBase(tx.maxPriorityFeePerGas.get).u256)
|
||||
if tx.maxFeePerGas.isSome:
|
||||
gasPrice.ok distinctBase(tx.maxFeePerGas.get).u256
|
||||
payload.gas = distinctBase(tx.gas)
|
||||
if tx.to.isSome:
|
||||
payload.to.ok(ethAddr(tx.to).get)
|
||||
payload.value = tx.value
|
||||
if tx.input.len > payload.input.maxLen:
|
||||
raise (ref ValueError)(msg:
|
||||
"Input cannot fit " & $tx.input.len & " bytes")
|
||||
payload.input = List[byte, Limit MAX_CALLDATA_SIZE].init tx.input
|
||||
if tx.accessList.isSome:
|
||||
payload.access_list.ok ethAccessList(tx.accessList)
|
||||
if tx.maxFeePerBlobGas.isSome:
|
||||
payload.max_fee_per_blob_gas.ok tx.maxFeePerBlobGas.get
|
||||
if tx.blobVersionedHashes.isSome:
|
||||
if tx.blobVersionedHashes.get.len > MAX_BLOB_COMMITMENTS_PER_BLOCK:
|
||||
raise (ref ValueError)(msg:
|
||||
"Access list cannot fit " & $tx.blobVersionedHashes.get.len & " bytes")
|
||||
payload.blob_versioned_hashes.ok(
|
||||
List[eth_types.VersionedHash, Limit MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init vHashes(tx.blobVersionedHashes))
|
||||
v = distinctBase(tx.v).u256
|
||||
r = tx.r
|
||||
s = tx.s
|
||||
if gasPrice.get(payload.max_fee_per_gas) != payload.max_fee_per_gas:
|
||||
raise (ref ValueError)(msg: "`gasPrice` and `maxFeePerGas` don't match")
|
||||
if payload.tx_type.get(TxLegacy) != TxLegacy and txChainId.isNone:
|
||||
raise (ref ValueError)(msg: "`chainId` is required")
|
||||
if payload.tx_type == Opt.some(TxLegacy) and txChainId.isNone:
|
||||
payload.tx_type.reset()
|
||||
if txChainId.get(chain_id) != chain_id:
|
||||
raise (ref ValueError)(msg: "Unsupported `chainId`")
|
||||
if r >= SECP256K1N:
|
||||
raise (ref ValueError)(msg: "Invalid `r`")
|
||||
if s < UInt256.one or s >= SECP256K1N:
|
||||
raise (ref ValueError)(msg: "Invalid `s`")
|
||||
let anyTx = AnyTransactionPayload.fromOneOfBase(payload).valueOr:
|
||||
raise (ref ValueError)(msg: "Invalid combination of fields")
|
||||
withTxPayloadVariant(anyTx):
|
||||
let y_parity =
|
||||
when txKind == TransactionKind.Replayable:
|
||||
if v == 27.u256:
|
||||
false
|
||||
elif v == 28.u256:
|
||||
true
|
||||
else:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
elif txKind == TransactionKind.Legacy:
|
||||
let
|
||||
res = v.isEven
|
||||
expected_v =
|
||||
distinctBase(chain_id).u256 * 2 + (if res: 36.u256 else: 35.u256)
|
||||
if v != expected_v:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
res
|
||||
else:
|
||||
if v > UInt256.one:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
v.isOdd
|
||||
var signature: TransactionSignature
|
||||
signature.ecdsa_signature = ecdsa_pack_signature(y_parity, r, s)
|
||||
signature.from_address = ecdsa_recover_from_address(
|
||||
signature.ecdsa_signature,
|
||||
txPayloadVariant.compute_sig_hash(chain_id)).valueOr:
|
||||
raise (ref ValueError)(msg: "Cannot compute `from` address")
|
||||
Transaction(payload: payload, signature: signature)
|
||||
|
||||
proc toTransactions*(txs: openArray[TxOrHash]): seq[Transaction] =
|
||||
proc toTransactions*(
|
||||
txs: openArray[TxOrHash], chain_id: ChainId): seq[Transaction] =
|
||||
for x in txs:
|
||||
doAssert x.kind == tohTx
|
||||
result.add toTransaction(x.tx)
|
||||
result.add toTransaction(x.tx, chain_id)
|
||||
|
||||
proc toWithdrawal(wd: WithdrawalObject): Withdrawal =
|
||||
Withdrawal(
|
||||
|
@ -493,14 +559,15 @@ proc latestHeader*(client: RpcClient): Result[common.BlockHeader, string] =
|
|||
return err("failed to get latest blockHeader")
|
||||
return ok(res.toBlockHeader)
|
||||
|
||||
proc latestBlock*(client: RpcClient): Result[common.EthBlock, string] =
|
||||
proc latestBlock*(
|
||||
client: RpcClient, chainId: ChainId): Result[common.EthBlock, string] =
|
||||
wrapTry:
|
||||
let res = waitFor client.eth_getBlockByNumber(blockId("latest"), true)
|
||||
if res.isNil:
|
||||
return err("failed to get latest blockHeader")
|
||||
let output = EthBlock(
|
||||
header: toBlockHeader(res),
|
||||
txs: toTransactions(res.transactions),
|
||||
txs: toTransactions(res.transactions, chainId),
|
||||
withdrawals: toWithdrawals(res.withdrawals),
|
||||
)
|
||||
return ok(output)
|
||||
|
@ -513,11 +580,13 @@ proc namedHeader*(client: RpcClient, name: string): Result[common.BlockHeader, s
|
|||
return ok(res.toBlockHeader)
|
||||
|
||||
proc sendTransaction*(
|
||||
client: RpcClient, tx: common.PooledTransaction): Result[void, string] =
|
||||
client: RpcClient,
|
||||
tx: common.PooledTransaction,
|
||||
chainId: ChainId): Result[void, string] =
|
||||
wrapTry:
|
||||
let encodedTx = rlp.encode(tx)
|
||||
let encodedTx = tx.toBytes(chainId)
|
||||
let res = waitFor client.eth_sendRawTransaction(encodedTx)
|
||||
let txHash = rlpHash(tx)
|
||||
let txHash = tx.tx.compute_tx_hash(chainId)
|
||||
let getHash = ethHash res
|
||||
if txHash != getHash:
|
||||
return err("sendTransaction: tx hash mismatch")
|
||||
|
@ -607,9 +676,10 @@ createRpcSigsFromNim(RpcClient):
|
|||
proc debugPrevRandaoTransaction*(
|
||||
client: RpcClient,
|
||||
tx: PooledTransaction,
|
||||
expectedPrevRandao: Hash256): Result[void, string] =
|
||||
expectedPrevRandao: Hash256,
|
||||
chain_id: ChainId): Result[void, string] =
|
||||
wrapTry:
|
||||
let hash = w3Hash tx.rlpHash
|
||||
let hash = tx.tx.compute_tx_hash(chain_id).data.TxHash
|
||||
# we only interested in stack, disable all other elems
|
||||
let opts = TraceOpts(
|
||||
disableStorage: true,
|
||||
|
|
|
@ -58,7 +58,7 @@ const
|
|||
|
||||
proc makeCom*(conf: NimbusConf): CommonRef =
|
||||
CommonRef.new(
|
||||
newCoreDbRef LegacyDbMemory,
|
||||
newCoreDbRef(LegacyDbMemory, conf.networkParams.config.chainId),
|
||||
conf.chainDbMode == ChainDbMode.Prune,
|
||||
conf.networkId,
|
||||
conf.networkParams
|
||||
|
|
|
@ -32,7 +32,7 @@ export
|
|||
|
||||
type
|
||||
TestEnv* = ref object
|
||||
conf : NimbusConf
|
||||
conf* : NimbusConf
|
||||
chainFile : string
|
||||
enableAuth: bool
|
||||
port : int
|
||||
|
@ -150,7 +150,7 @@ proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BigInitcodeTx, nonce: AccountNonc
|
|||
proc sendTxs*(
|
||||
env: TestEnv, eng: EngineEnv, txs: openArray[PooledTransaction]): bool =
|
||||
for tx in txs:
|
||||
if not sendTx(eng.client, tx):
|
||||
if not sendTx(eng.client, tx, env.conf.networkParams.config.chainId):
|
||||
return false
|
||||
true
|
||||
|
||||
|
@ -168,7 +168,7 @@ proc sendTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
|
|||
|
||||
proc sendTx*(env: TestEnv, tx: PooledTransaction): bool =
|
||||
let client = env.engine.client
|
||||
sendTx(client, tx)
|
||||
sendTx(client, tx, env.conf.networkParams.config.chainId)
|
||||
|
||||
proc sendTx*(
|
||||
env: TestEnv,
|
||||
|
@ -195,7 +195,8 @@ proc customizeTransaction*(env: TestEnv,
|
|||
acc: TestAccount,
|
||||
baseTx: Transaction,
|
||||
custTx: CustomTransactionData): Transaction =
|
||||
env.sender.customizeTransaction(acc, baseTx, custTx)
|
||||
env.sender.customizeTransaction(
|
||||
acc, baseTx, custTx, env.conf.networkParams.config.chainId)
|
||||
|
||||
proc generateInvalidPayload*(env: TestEnv,
|
||||
data: ExecutableData,
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
|
||||
import
|
||||
std/[tables],
|
||||
eth/keys,
|
||||
eth/[common/transaction, keys],
|
||||
stew/endians2,
|
||||
stint,
|
||||
nimcrypto/sha2,
|
||||
chronicles,
|
||||
./engine_client,
|
||||
|
@ -50,7 +51,7 @@ type
|
|||
accounts: seq[TestAccount]
|
||||
nonceMap: Table[EthAddress, uint64]
|
||||
txSent : int
|
||||
chainId : ChainID
|
||||
chainId*: ChainID
|
||||
|
||||
MakeTxParams* = object
|
||||
chainId*: ChainID
|
||||
|
@ -142,29 +143,34 @@ proc makeTxOfType(params: MakeTxParams, tc: BaseTx): PooledTransaction =
|
|||
of TxLegacy:
|
||||
PooledTransaction(
|
||||
tx: Transaction(
|
||||
txType : TxLegacy,
|
||||
nonce : params.nonce,
|
||||
to : tc.recipient,
|
||||
value : tc.amount,
|
||||
gasLimit: tc.gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
payload : tc.payload
|
||||
)
|
||||
)
|
||||
payload: TransactionPayload(
|
||||
nonce: params.nonce,
|
||||
to:
|
||||
if tc.recipient.isSome:
|
||||
Opt.some(tc.recipient.get)
|
||||
else:
|
||||
Opt.none(EthAddress),
|
||||
value: tc.amount,
|
||||
gas: tc.gasLimit.uint64,
|
||||
max_fee_per_gas: gasPrice.uint64.u256,
|
||||
input: List[byte, Limit MAX_CALLDATA_SIZE].init tc.payload)))
|
||||
|
||||
of TxEip1559:
|
||||
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
|
||||
)
|
||||
)
|
||||
payload: TransactionPayload(
|
||||
tx_type: Opt.some TxEip1559,
|
||||
nonce: params.nonce,
|
||||
gas: tc.gasLimit.uint64,
|
||||
max_fee_per_gas: gasPrice.uint64.u256,
|
||||
max_priority_fee_per_gas: Opt.some(gasTipCap.uint64.u256),
|
||||
to:
|
||||
if tc.recipient.isSome:
|
||||
Opt.some(tc.recipient.get)
|
||||
else:
|
||||
Opt.none(EthAddress),
|
||||
value: tc.amount,
|
||||
input: List[byte, Limit MAX_CALLDATA_SIZE].init tc.payload)))
|
||||
of TxEip4844:
|
||||
doAssert(tc.recipient.isSome, "recipient must be some")
|
||||
let
|
||||
|
@ -179,23 +185,33 @@ proc makeTxOfType(params: MakeTxParams, tc: BaseTx): PooledTransaction =
|
|||
|
||||
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),
|
||||
proofs: system.move(blobData.proofs),
|
||||
payload: TransactionPayload(
|
||||
tx_type: Opt.some TxEip4844,
|
||||
nonce: params.nonce,
|
||||
max_fee_per_gas: gasPrice.uint64.u256,
|
||||
max_priority_fee_per_gas: Opt.some(gasTipCap.uint64.u256),
|
||||
gas: tc.gasLimit.uint64,
|
||||
to:
|
||||
if tc.recipient.isSome:
|
||||
Opt.some(tc.recipient.get)
|
||||
else:
|
||||
Opt.none(EthAddress),
|
||||
value: tc.amount,
|
||||
input: List[byte, Limit MAX_CALLDATA_SIZE].init tc.payload,
|
||||
max_fee_per_blob_gas: Opt.some(blobFeeCap),
|
||||
blob_versioned_hashes: Opt.some(
|
||||
List[eth_types.VersionedHash, Limit MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(system.move(blobData.hashes))))),
|
||||
blob_data: Opt.some NetworkPayload(
|
||||
blobs:
|
||||
List[NetworkBlob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(system.move(blobData.blobs)),
|
||||
commitments:
|
||||
List[eth_types.KzgCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(system.move(blobData.commitments)),
|
||||
proofs:
|
||||
List[eth_types.KzgProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(system.move(blobData.proofs)),
|
||||
)
|
||||
)
|
||||
else:
|
||||
|
@ -205,8 +221,8 @@ proc makeTx(params: MakeTxParams, tc: BaseTx): PooledTransaction =
|
|||
# Build the transaction depending on the specified type
|
||||
let tx = makeTxOfType(params, tc)
|
||||
PooledTransaction(
|
||||
tx: signTransaction(tx.tx, params.key, params.chainId, eip155 = true),
|
||||
networkPayload: tx.networkPayload)
|
||||
tx: signTransaction(tx.tx.payload, params.key, params.chainId),
|
||||
blob_data: tx.blob_data)
|
||||
|
||||
proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): PooledTransaction =
|
||||
var tx = tc
|
||||
|
@ -257,7 +273,7 @@ proc makeNextTx*(sender: TxSender, tc: BaseTx): PooledTransaction =
|
|||
|
||||
proc sendNextTx*(sender: TxSender, client: RpcClient, tc: BaseTx): bool =
|
||||
let tx = sender.makeNextTx(tc)
|
||||
let rr = client.sendTransaction(tx)
|
||||
let rr = client.sendTransaction(tx, sender.chainId)
|
||||
if rr.isErr:
|
||||
error "sendNextTx: Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
|
@ -275,7 +291,7 @@ proc sendTx*(sender: TxSender, client: RpcClient, tc: BaseTx, nonce: AccountNonc
|
|||
)
|
||||
tx = params.makeTx(tc)
|
||||
|
||||
let rr = client.sendTransaction(tx)
|
||||
let rr = client.sendTransaction(tx, sender.chainId)
|
||||
if rr.isErr:
|
||||
error "sendTx: Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
|
@ -293,7 +309,7 @@ proc sendTx*(sender: TxSender, client: RpcClient, tc: BigInitcodeTx, nonce: Acco
|
|||
)
|
||||
tx = params.makeTx(tc)
|
||||
|
||||
let rr = client.sendTransaction(tx)
|
||||
let rr = client.sendTransaction(tx, sender.chainId)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
|
@ -301,8 +317,8 @@ proc sendTx*(sender: TxSender, client: RpcClient, tc: BigInitcodeTx, nonce: Acco
|
|||
inc sender.txSent
|
||||
return true
|
||||
|
||||
proc sendTx*(client: RpcClient, tx: PooledTransaction): bool =
|
||||
let rr = client.sendTransaction(tx)
|
||||
proc sendTx*(client: RpcClient, tx: PooledTransaction, chainId: ChainId): bool =
|
||||
let rr = client.sendTransaction(tx, chainId)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
|
@ -320,27 +336,36 @@ proc makeTx*(params: MakeTxParams, tc: BlobTx): PooledTransaction =
|
|||
else: gasTipPrice
|
||||
|
||||
# Collect fields for transaction
|
||||
let unsignedTx = Transaction(
|
||||
txType : TxEip4844,
|
||||
chainId : params.chainId,
|
||||
nonce : params.nonce,
|
||||
maxPriorityFee: gasTipCap,
|
||||
maxFee : gasFeeCap,
|
||||
gasLimit : tc.gasLimit,
|
||||
to : tc.recipient,
|
||||
value : tc.amount,
|
||||
payload : tc.payload,
|
||||
maxFeePerBlobGas: tc.blobGasFee,
|
||||
versionedHashes: data.hashes,
|
||||
)
|
||||
|
||||
let unsignedTx = TransactionPayload(
|
||||
tx_type: Opt.some TxEip4844,
|
||||
nonce: params.nonce,
|
||||
max_priority_fee_per_gas: Opt.some(gasTipCap.uint64.u256),
|
||||
max_fee_per_gas: gasFeeCap.uint64.u256,
|
||||
gas: tc.gasLimit.uint64,
|
||||
to:
|
||||
if tc.recipient.isSome:
|
||||
Opt.some(tc.recipient.get)
|
||||
else:
|
||||
Opt.none(EthAddress),
|
||||
value: tc.amount,
|
||||
input: List[byte, Limit MAX_CALLDATA_SIZE].init tc.payload,
|
||||
max_fee_per_blob_gas: Opt.some(tc.blobGasFee),
|
||||
blob_versioned_hashes: Opt.some(
|
||||
List[eth_types.VersionedHash, Limit MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(data.hashes)))
|
||||
PooledTransaction(
|
||||
tx: signTransaction(unsignedTx, params.key, params.chainId, eip155 = true),
|
||||
networkPayload: NetworkPayload(
|
||||
blobs : data.blobs,
|
||||
commitments: data.commitments,
|
||||
proofs : data.proofs,
|
||||
),
|
||||
tx: signTransaction(unsignedTx, params.key, params.chainId),
|
||||
blob_data: Opt.some NetworkPayload(
|
||||
blobs:
|
||||
List[NetworkBlob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(data.blobs),
|
||||
commitments:
|
||||
List[eth_types.KzgCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(data.commitments),
|
||||
proofs:
|
||||
List[eth_types.KzgProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(data.proofs),
|
||||
)
|
||||
)
|
||||
|
||||
proc getAccount*(sender: TxSender, idx: int): TestAccount =
|
||||
|
@ -359,7 +384,7 @@ proc sendTx*(
|
|||
)
|
||||
tx = params.makeTx(tc)
|
||||
|
||||
let rr = client.sendTransaction(tx)
|
||||
let rr = client.sendTransaction(tx, params.chainId)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return err()
|
||||
|
@ -380,7 +405,7 @@ proc replaceTx*(
|
|||
)
|
||||
tx = params.makeTx(tc)
|
||||
|
||||
let rr = client.sendTransaction(tx)
|
||||
let rr = client.sendTransaction(tx, params.chainId)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return err()
|
||||
|
@ -404,49 +429,86 @@ proc makeTx*(
|
|||
proc customizeTransaction*(sender: TxSender,
|
||||
acc: TestAccount,
|
||||
baseTx: Transaction,
|
||||
custTx: CustomTransactionData): Transaction =
|
||||
custTx: CustomTransactionData,
|
||||
chainId: ChainId): Transaction =
|
||||
# Create a modified transaction base, from the base transaction and custTx mix
|
||||
var modTx = baseTx
|
||||
if custTx.nonce.isSome:
|
||||
modTx.nonce = custTx.nonce.get.AccountNonce
|
||||
modTx.payload.nonce = custTx.nonce.get.AccountNonce
|
||||
|
||||
if custTx.gasPriceOrGasFeeCap.isSome:
|
||||
modTx.gasPrice = custTx.gasPriceOrGasFeeCap.get.GasInt
|
||||
modTx.payload.max_fee_per_gas = custTx.gasPriceOrGasFeeCap.get.u256
|
||||
|
||||
if custTx.gas.isSome:
|
||||
modTx.gasLimit = custTx.gas.get.GasInt
|
||||
modTx.payload.gas = custTx.gas.get.uint64
|
||||
|
||||
if custTx.to.isSome:
|
||||
modTx.to = custTx.to
|
||||
modTx.payload.to.ok custTx.to.get
|
||||
|
||||
if custTx.value.isSome:
|
||||
modTx.value = custTx.value.get
|
||||
modTx.payload.value = custTx.value.get
|
||||
|
||||
if custTx.data.isSome:
|
||||
modTx.payload = custTx.data.get
|
||||
modTx.payload.input =
|
||||
List[byte, Limit MAX_CALLDATA_SIZE].init(custTx.data.get)
|
||||
|
||||
if custTx.signature.isSome:
|
||||
let signature = custTx.signature.get
|
||||
modTx.V = signature.V
|
||||
modTx.R = signature.R
|
||||
modTx.S = signature.S
|
||||
|
||||
if baseTx.txType in {TxEip1559, TxEip4844}:
|
||||
let custChainId =
|
||||
if custTx.chainId.isSome:
|
||||
modTx.chainId = custTx.chainId.get
|
||||
custTx.chainId.get
|
||||
else:
|
||||
chainId
|
||||
|
||||
if baseTx.payload.tx_type.get(TxLegacy) in {TxEip1559, TxEip4844}:
|
||||
if custTx.gasPriceOrGasFeeCap.isSome:
|
||||
modTx.maxFee = custTx.gasPriceOrGasFeeCap.get.GasInt
|
||||
modTx.payload.max_fee_per_gas = custTx.gasPriceOrGasFeeCap.get.u256
|
||||
|
||||
if custTx.gasTipCap.isSome:
|
||||
modTx.maxPriorityFee = custTx.gasTipCap.get.GasInt
|
||||
modTx.payload.max_priority_fee_per_gas.ok custTx.gasTipCap.get.u256
|
||||
|
||||
if baseTx.txType == TxEip4844:
|
||||
if modTx.to.isNone:
|
||||
if baseTx.payload.tx_type.get(TxLegacy) == TxEip4844:
|
||||
if modTx.payload.to.isNone:
|
||||
var address: EthAddress
|
||||
modTx.to = some(address)
|
||||
modTx.payload.to.ok(address)
|
||||
|
||||
if custTx.signature.isSome:
|
||||
let
|
||||
signature = custTx.signature.get
|
||||
v = signature.V.u256
|
||||
r = signature.R
|
||||
s = signature.S
|
||||
anyTx = AnyTransactionPayload.fromOneOfBase(modTx.payload).valueOr:
|
||||
raise (ref ValueError)(msg: "Invalid combination of fields")
|
||||
return withTxPayloadVariant(anyTx):
|
||||
let y_parity =
|
||||
when txKind == TransactionKind.Replayable:
|
||||
if v == 27.u256:
|
||||
false
|
||||
elif v == 28.u256:
|
||||
true
|
||||
else:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
elif txKind == TransactionKind.Legacy:
|
||||
let
|
||||
res = v.isEven
|
||||
expected_v =
|
||||
distinctBase(custChainId).u256 * 2 +
|
||||
(if res: 36.u256 else: 35.u256)
|
||||
if v != expected_v:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
res
|
||||
else:
|
||||
if v > UInt256.one:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
v.isOdd
|
||||
var signature: TransactionSignature
|
||||
signature.ecdsa_signature = ecdsa_pack_signature(y_parity, r, s)
|
||||
signature.from_address = ecdsa_recover_from_address(
|
||||
signature.ecdsa_signature,
|
||||
txPayloadVariant.compute_sig_hash(custChainId)).valueOr:
|
||||
raise (ref ValueError)(msg: "Cannot compute `from` address")
|
||||
Transaction(payload: modTx.payload, signature: signature)
|
||||
|
||||
if custTx.signature.isNone:
|
||||
return signTransaction(modTx, acc.key, modTx.chainId, eip155 = true)
|
||||
return signTransaction(modTx.payload, acc.key, custChainId)
|
||||
|
||||
return modTx
|
||||
|
|
|
@ -26,7 +26,7 @@ proc execute*(ws: BlockValueSpec, env: TestEnv): bool =
|
|||
testCond WDBaseSpec(ws).execute(env)
|
||||
|
||||
# Get the latest block and the transactions included
|
||||
let b = env.client.latestBlock()
|
||||
let b = env.client.latestBlock(env.conf.networkParams.config.chainId)
|
||||
b.expectNoError()
|
||||
let blk = b.get
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import
|
|||
std/typetraits,
|
||||
chronos,
|
||||
chronicles,
|
||||
eth/common/eth_types_rlp,
|
||||
eth/common/[eth_types_rlp, transaction],
|
||||
./wd_base_spec,
|
||||
../test_env,
|
||||
../engine_client,
|
||||
|
@ -87,7 +87,8 @@ proc execute*(ws: MaxInitcodeSizeSpec, env: TestEnv): bool =
|
|||
testCond not env.sendTx(tx):
|
||||
error "Client accepted tx exceeding the MAX_INITCODE_SIZE"
|
||||
|
||||
let res = env.client.txByHash(rlpHash(tx))
|
||||
let res = env.client.txByHash(
|
||||
tx.tx.compute_tx_hash(env.conf.networkParams.config.chainId))
|
||||
testCond res.isErr:
|
||||
error "Invalid tx was not unknown to the client"
|
||||
|
||||
|
@ -102,7 +103,7 @@ proc execute*(ws: MaxInitcodeSizeSpec, env: TestEnv): bool =
|
|||
return true
|
||||
,
|
||||
onGetPayload: proc(): bool =
|
||||
let validTxBytes = rlp.encode(validTx)
|
||||
let validTxBytes = validTx.toBytes(env.conf.networkParams.config.chainId)
|
||||
testCond env.clMock.latestPayloadBuilt.transactions.len == 1:
|
||||
error "Client did not include valid tx with MAX_INITCODE_SIZE"
|
||||
|
||||
|
|
|
@ -78,7 +78,8 @@ proc main() =
|
|||
conf = makeConfig(@["--custom-network:" & genesisFile])
|
||||
ethCtx = newEthContext()
|
||||
ethNode = setupEthNode(conf, ethCtx, eth)
|
||||
com = CommonRef.new(newCoreDbRef LegacyDbMemory,
|
||||
com = CommonRef.new(
|
||||
newCoreDbRef(LegacyDbMemory, conf.networkParams.config.chainId),
|
||||
pruneTrie = false,
|
||||
conf.networkId,
|
||||
conf.networkParams
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# those terms.
|
||||
|
||||
import
|
||||
eth/[common, rlp],
|
||||
eth/common,
|
||||
chronos, stint,
|
||||
json_rpc/[rpcclient],
|
||||
../../../nimbus/transaction,
|
||||
|
@ -19,8 +19,10 @@ import
|
|||
export eth_api
|
||||
|
||||
proc sendTransaction*(
|
||||
client: RpcClient, tx: PooledTransaction): Future[bool] {.async.} =
|
||||
let data = rlp.encode(tx)
|
||||
client: RpcClient,
|
||||
tx: PooledTransaction,
|
||||
chainId: ChainId): Future[bool] {.async.} =
|
||||
let data = tx.toBytes(chainId)
|
||||
let txHash = keccakHash(data)
|
||||
let hex = await client.eth_sendRawTransaction(data)
|
||||
let decodedHash = ethHash(hex)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import
|
||||
std/strutils,
|
||||
eth/[common],
|
||||
eth/[common, common/transaction],
|
||||
stew/byteutils,
|
||||
stint,
|
||||
chronos,
|
||||
|
@ -88,11 +88,11 @@ proc balanceAndNonceAtTest(t: TestEnv): Future[TestStatus] {.async.} =
|
|||
let tx = vault.signTx(sourceAddr, sourceNonce, targetAddr, amount, gasLimit, gasPrice)
|
||||
inc sourceNonce
|
||||
|
||||
let txHash = rlpHash(tx)
|
||||
let txHash = tx.tx.compute_tx_hash(vault.chainId)
|
||||
echo "BalanceAt: send $1 wei from 0x$2 to 0x$3 in 0x$4" % [
|
||||
$tx.tx.value, sourceAddr.toHex, targetAddr.toHex, txHash.data.toHex]
|
||||
$tx.tx.payload.value, sourceAddr.toHex, targetAddr.toHex, txHash.data.toHex]
|
||||
|
||||
let ok = await client.sendTransaction(tx)
|
||||
let ok = await client.sendTransaction(tx, vault.chainId)
|
||||
if not ok:
|
||||
echo "failed to send transaction"
|
||||
return TestStatus.Failed
|
||||
|
@ -118,7 +118,8 @@ proc balanceAndNonceAtTest(t: TestEnv): Future[TestStatus] {.async.} =
|
|||
|
||||
# expected balance is previous balance - tx amount - tx fee (gasUsed * gasPrice)
|
||||
let exp =
|
||||
sourceAddressBalanceBefore - amount - (gasUsed * tx.tx.gasPrice).u256
|
||||
sourceAddressBalanceBefore - amount -
|
||||
gasUsed.u256 * tx.tx.payload.max_fee_per_gas
|
||||
|
||||
if exp != accountBalanceAfter:
|
||||
echo "Expected sender account to have a balance of $1, got $2" % [$exp, $accountBalanceAfter]
|
||||
|
@ -126,7 +127,7 @@ proc balanceAndNonceAtTest(t: TestEnv): Future[TestStatus] {.async.} =
|
|||
|
||||
if balanceTargetAccountAfter != amount:
|
||||
echo "Expected new account to have a balance of $1, got $2" % [
|
||||
$tx.tx.value, $balanceTargetAccountAfter]
|
||||
$tx.tx.payload.value, $balanceTargetAccountAfter]
|
||||
return TestStatus.Failed
|
||||
|
||||
# ensure nonce is incremented by 1
|
||||
|
|
|
@ -76,7 +76,8 @@ proc setupEnv*(): TestEnv =
|
|||
let
|
||||
ethCtx = newEthContext()
|
||||
ethNode = setupEthNode(conf, ethCtx, eth)
|
||||
com = CommonRef.new(newCoreDbRef LegacyDbMemory,
|
||||
com = CommonRef.new(
|
||||
newCoreDbRef(LegacyDbMemory, conf.networkParams.config.chainId),
|
||||
conf.chainDbMode == ChainDbMode.Prune,
|
||||
conf.networkId,
|
||||
conf.networkParams
|
||||
|
|
|
@ -42,7 +42,7 @@ type
|
|||
accounts: Table[EthAddress, PrivateKey]
|
||||
|
||||
rng: ref HmacDrbgContext
|
||||
chainId: ChainID
|
||||
chainId*: ChainID
|
||||
gasPrice: GasInt
|
||||
vaultKey: PrivateKey
|
||||
client: RpcClient
|
||||
|
@ -68,57 +68,49 @@ proc nextNonce*(v: Vault): AccountNonce =
|
|||
inc(v.nonce)
|
||||
nonce
|
||||
|
||||
proc sendSome(address: EthAddress, amount: UInt256): seq[byte] =
|
||||
proc sendSome(
|
||||
address: EthAddress, amount: UInt256): List[byte, Limit MAX_CALLDATA_SIZE] =
|
||||
const padding = repeat('\0', 12).toBytes
|
||||
# makeshift contract ABI construction
|
||||
# https://docs.soliditylang.org/en/develop/abi-spec.html
|
||||
let h = keccakHash("sendSome(address,uint256)".toBytes)
|
||||
result.add h.data[0..3] # first 4 bytes of hash
|
||||
result.add padding # left pad address
|
||||
result.add address
|
||||
result.add amount.toBytesBE
|
||||
doAssert result.add h.data[0..3] # first 4 bytes of hash
|
||||
doAssert result.add padding # left pad address
|
||||
doAssert result.add address
|
||||
doAssert result.add amount.toBytesBE
|
||||
doAssert(result.len == 68) # 4 + 32 + 32
|
||||
|
||||
proc makeFundingTx*(
|
||||
v: Vault, recipient: EthAddress, amount: UInt256): PooledTransaction =
|
||||
let unsignedTx = TransactionPayload(
|
||||
nonce: v.nextNonce(),
|
||||
max_fee_per_gas: v.gasPrice.uint64.u256,
|
||||
gas: 75000,
|
||||
to: Opt.some(predeployedVaultAddr),
|
||||
value: 0.u256,
|
||||
input: sendSome(recipient, amount),
|
||||
tx_type: Opt.some TxLegacy)
|
||||
PooledTransaction(tx: signTransaction(unsignedTx, v.vaultKey, v.chainId))
|
||||
|
||||
proc signTx*(
|
||||
v: Vault,
|
||||
sender: EthAddress,
|
||||
nonce: AccountNonce,
|
||||
recipient: EthAddress,
|
||||
amount: UInt256,
|
||||
gasLimit, gasPrice: GasInt,
|
||||
payload = List[byte, Limit MAX_CALLDATA_SIZE] @[]): PooledTransaction =
|
||||
let
|
||||
unsignedTx = Transaction(
|
||||
txType : TxLegacy,
|
||||
chainId : v.chainId,
|
||||
nonce : v.nextNonce(),
|
||||
gasPrice: v.gasPrice,
|
||||
gasLimit: GasInt(75000),
|
||||
to : some(predeployedVaultAddr),
|
||||
value : 0.u256,
|
||||
payload : sendSome(recipient, amount)
|
||||
)
|
||||
|
||||
PooledTransaction(
|
||||
tx: signTransaction(unsignedTx, v.vaultKey, v.chainId, eip155 = true))
|
||||
|
||||
proc signTx*(v: Vault,
|
||||
sender: EthAddress,
|
||||
nonce: AccountNonce,
|
||||
recipient: EthAddress,
|
||||
amount: UInt256,
|
||||
gasLimit, gasPrice: GasInt,
|
||||
payload: seq[byte] = @[]): PooledTransaction =
|
||||
|
||||
let
|
||||
unsignedTx = Transaction(
|
||||
txType : TxLegacy,
|
||||
chainId : v.chainId,
|
||||
nonce : nonce,
|
||||
gasPrice: gasPrice,
|
||||
gasLimit: gasLimit,
|
||||
to : some(recipient),
|
||||
value : amount,
|
||||
payload : payload
|
||||
)
|
||||
|
||||
let key = v.accounts[sender]
|
||||
PooledTransaction(
|
||||
tx: signTransaction(unsignedTx, key, v.chainId, eip155 = true))
|
||||
unsignedTx = TransactionPayload(
|
||||
nonce: nonce,
|
||||
max_fee_per_gas: gasPrice.uint64.u256,
|
||||
gas: gasLimit.uint64,
|
||||
to: Opt.some(recipient),
|
||||
value: amount,
|
||||
input: payload,
|
||||
tx_type: Opt.some TxLegacy)
|
||||
key = v.accounts[sender]
|
||||
PooledTransaction(tx: signTransaction(unsignedTx, key, v.chainId))
|
||||
|
||||
# createAccount creates a new account that is funded from the vault contract.
|
||||
# It will panic when the account could not be created and funded.
|
||||
|
@ -127,7 +119,7 @@ proc createAccount*(v: Vault, amount: UInt256): Future[EthAddress] {.async.} =
|
|||
|
||||
# order the vault to send some ether
|
||||
let tx = v.makeFundingTx(address, amount)
|
||||
let res = await v.client.sendTransaction(tx)
|
||||
let res = await v.client.sendTransaction(tx, v.chainId)
|
||||
if not res:
|
||||
raise newException(ValueError, "unable to send funding transaction")
|
||||
|
||||
|
@ -147,5 +139,5 @@ proc createAccount*(v: Vault, amount: UInt256): Future[EthAddress] {.async.} =
|
|||
let period = chronos.seconds(1)
|
||||
await sleepAsync(period)
|
||||
|
||||
let txHash = tx.rlpHash().data.toHex
|
||||
let txHash = tx.tx.compute_tx_hash(v.chainId).data.toHex
|
||||
raise newException(ValueError, "could not fund account $2 in transaction $2" % [address.toHex, txHash])
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# those terms.
|
||||
|
||||
import
|
||||
eth/common,
|
||||
eth/[common, common/transaction],
|
||||
stew/results,
|
||||
../web3_eth_conv,
|
||||
../beacon_engine,
|
||||
|
@ -20,11 +20,15 @@ import
|
|||
{.push gcsafe, raises:[CatchableError].}
|
||||
|
||||
func validateVersionedHashed(payload: ExecutionPayload,
|
||||
expected: openArray[Web3Hash]): bool =
|
||||
expected: openArray[Web3Hash],
|
||||
chainId: ChainId): bool =
|
||||
var versionedHashes: seq[common.Hash256]
|
||||
for x in payload.transactions:
|
||||
let tx = rlp.decode(distinctBase(x), Transaction)
|
||||
versionedHashes.add tx.versionedHashes
|
||||
let tx = Transaction.fromBytes(distinctBase(x), chainId).valueOr:
|
||||
raise (ref MalformedRlpError)(msg: "Invalid transaction in payload")
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
versionedHashes.add distinctBase(
|
||||
tx.payload.blob_versioned_hashes.unsafeGet)
|
||||
|
||||
if versionedHashes.len != expected.len:
|
||||
return false
|
||||
|
@ -125,7 +129,7 @@ proc newPayload*(ben: BeaconEngineRef,
|
|||
if versionedHashes.isNone:
|
||||
raise invalidParams("newPayload" & $apiVersion &
|
||||
" expect blobVersionedHashes but got none")
|
||||
if not validateVersionedHashed(payload, versionedHashes.get):
|
||||
if not validateVersionedHashed(payload, versionedHashes.get, com.chainId):
|
||||
return invalidStatus(header.parentHash, "invalid blob versionedHashes")
|
||||
|
||||
let blockHash = ethHash payload.blockHash
|
||||
|
|
|
@ -157,16 +157,18 @@ func ethTxs*(list: openArray[Web3Tx]):
|
|||
for x in list:
|
||||
result.add ethTx(x)
|
||||
|
||||
func storageKeys(list: seq[FixedBytes[32]]): seq[StorageKey] =
|
||||
func storageKeys(list: seq[FixedBytes[32]]): common.StorageKeys =
|
||||
for x in list:
|
||||
result.add StorageKey(x)
|
||||
let ok = result.add distinctBase(x)
|
||||
doAssert ok, "StorageKeys capacity exceeded"
|
||||
|
||||
func ethAccessList*(list: openArray[AccessTuple]): common.AccessList =
|
||||
for x in list:
|
||||
result.add common.AccessPair(
|
||||
let ok = result.add common.AccessPair(
|
||||
address : ethAddr x.address,
|
||||
storageKeys: storageKeys x.storageKeys,
|
||||
)
|
||||
doAssert ok, "AccessList capacity exceeded"
|
||||
|
||||
func ethAccessList*(x: Option[seq[AccessTuple]]): common.AccessList =
|
||||
if x.isSome:
|
||||
|
@ -286,10 +288,10 @@ func w3Txs*(list: openArray[common.Transaction]): seq[Web3Tx] =
|
|||
proc w3AccessTuple*(ac: AccessPair): AccessTuple =
|
||||
AccessTuple(
|
||||
address: w3Addr ac.address,
|
||||
storageKeys: w3Hash(ac.storageKeys)
|
||||
storageKeys: w3Hash(distinctBase(ac.storage_keys))
|
||||
)
|
||||
|
||||
proc w3AccessList*(list: openArray[AccessPair]): seq[AccessTuple] =
|
||||
proc w3AccessList*(list: common.AccessList): seq[AccessTuple] =
|
||||
result = newSeqOfCap[AccessTuple](list.len)
|
||||
for x in list:
|
||||
result.add w3AccessTuple(x)
|
||||
|
|
|
@ -190,7 +190,7 @@ proc init(com : CommonRef,
|
|||
time: some(genesis.timestamp)
|
||||
))
|
||||
com.genesisHeader = toGenesisHeader(genesis,
|
||||
com.currentFork, com.db, com.ldgType)
|
||||
com.currentFork, config.chainId, com.db, com.ldgType)
|
||||
com.setForkId(com.genesisHeader)
|
||||
com.pos.timestamp = genesis.timestamp
|
||||
else:
|
||||
|
|
|
@ -225,6 +225,7 @@ proc toGenesisHeader*(
|
|||
proc toGenesisHeader*(
|
||||
genesis: Genesis;
|
||||
fork: HardFork;
|
||||
chainId: ChainId;
|
||||
db = CoreDbRef(nil);
|
||||
ledgerType = GenesisLedgerTypeDefault;
|
||||
): BlockHeader
|
||||
|
@ -232,7 +233,7 @@ proc toGenesisHeader*(
|
|||
## Generate the genesis block header from the `genesis` and `config`
|
||||
## argument value.
|
||||
let
|
||||
db = if db.isNil: newCoreDbRef LegacyDbMemory else: db
|
||||
db = if db.isNil: newCoreDbRef LegacyDbMemory, chainId else: db
|
||||
sdb = newStateDB(db, pruneTrie = true, ledgerType)
|
||||
toGenesisHeader(genesis, sdb, fork)
|
||||
|
||||
|
@ -246,7 +247,7 @@ proc toGenesisHeader*(
|
|||
## argument value.
|
||||
let map = toForkTransitionTable(params.config)
|
||||
let fork = map.toHardFork(forkDeterminationInfo(0.toBlockNumber, params.genesis.timestamp))
|
||||
toGenesisHeader(params.genesis, fork, db, ledgerType)
|
||||
toGenesisHeader(params.genesis, fork, params.config.chainId, db, ledgerType)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
|
|
|
@ -83,9 +83,6 @@ const
|
|||
DEFAULT_RPC_GAS_CAP* = 50_000_000.GasInt
|
||||
|
||||
# EIP-4844 constants
|
||||
MAX_CALLDATA_SIZE* = 1 shl 24 # 2^24
|
||||
MAX_ACCESS_LIST_SIZE* = 1 shl 24 # 2^24
|
||||
MAX_ACCESS_LIST_STORAGE_KEYS* = 1 shl 24 # 2^24
|
||||
MAX_TX_WRAP_COMMITMENTS* = 1 shl 12 # 2^12
|
||||
VERSIONED_HASH_VERSION_KZG* = 0x01.byte
|
||||
FIELD_ELEMENTS_PER_BLOB* = 4096
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# according to those terms.
|
||||
|
||||
import
|
||||
std/[os, strutils],
|
||||
std/[os, strutils, typetraits],
|
||||
nimcrypto/sha2,
|
||||
kzg4844/kzg_ex as kzg,
|
||||
stew/results,
|
||||
|
@ -108,7 +108,9 @@ func fakeExponential*(factor, numerator, denominator: UInt256): UInt256 =
|
|||
output div denominator
|
||||
|
||||
proc getTotalBlobGas*(tx: Transaction): uint64 =
|
||||
GAS_PER_BLOB * tx.versionedHashes.len.uint64
|
||||
let vhs = tx.payload.blob_versioned_hashes.valueOr:
|
||||
return 0
|
||||
GAS_PER_BLOB * vhs.len.uint64
|
||||
|
||||
proc getTotalBlobGas*(versionedHashesLen: int): uint64 =
|
||||
GAS_PER_BLOB * versionedHashesLen.uint64
|
||||
|
@ -169,38 +171,44 @@ func validateEip4844Header*(
|
|||
|
||||
proc validateBlobTransactionWrapper*(tx: PooledTransaction):
|
||||
Result[void, string] {.raises: [].} =
|
||||
if tx.networkPayload.isNil:
|
||||
if tx.tx.payload.blob_versioned_hashes.isNone:
|
||||
if tx.blob_data.isSome:
|
||||
return err("tx wrapper contains unexpected blobs")
|
||||
return ok()
|
||||
if tx.blob_data.isNone:
|
||||
return err("tx wrapper is none")
|
||||
|
||||
template blob_versioned_hashes: untyped =
|
||||
tx.tx.payload.blob_versioned_hashes.unsafeGet
|
||||
template blob_data: untyped =
|
||||
tx.blob_data.unsafeGet
|
||||
|
||||
# note: assert blobs are not malformatted
|
||||
let goodFormatted = tx.tx.versionedHashes.len ==
|
||||
tx.networkPayload.commitments.len and
|
||||
tx.tx.versionedHashes.len ==
|
||||
tx.networkPayload.blobs.len and
|
||||
tx.tx.versionedHashes.len ==
|
||||
tx.networkPayload.proofs.len
|
||||
let goodFormatted = blob_versioned_hashes.len ==
|
||||
blob_data.commitments.len and
|
||||
blob_versioned_hashes.len ==
|
||||
blob_data.blobs.len and
|
||||
blob_versioned_hashes.len ==
|
||||
blob_data.proofs.len
|
||||
|
||||
if not goodFormatted:
|
||||
return err("tx wrapper is ill formatted")
|
||||
|
||||
# Verify that commitments match the blobs by checking the KZG proof
|
||||
let res = kzg.verifyBlobKzgProofBatch(tx.networkPayload.blobs,
|
||||
tx.networkPayload.commitments, tx.networkPayload.proofs)
|
||||
if res.isErr:
|
||||
return err(res.error)
|
||||
|
||||
# Actual verification result
|
||||
if not res.get():
|
||||
if not(? kzg.verifyBlobKzgProofBatch(
|
||||
distinctBase(blob_data.blobs),
|
||||
distinctBase(blob_data.commitments),
|
||||
distinctBase(blob_data.proofs))):
|
||||
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.tx.versionedHashes.len:
|
||||
# Now that all commitments have been verified, check that versionedHashes
|
||||
# matches the commitments
|
||||
for i in 0 ..< blob_versioned_hashes.len:
|
||||
# this additional check also done in tx validation
|
||||
if tx.tx.versionedHashes[i].data[0] != VERSIONED_HASH_VERSION_KZG:
|
||||
if blob_versioned_hashes[i].data[0] != VERSIONED_HASH_VERSION_KZG:
|
||||
return err("wrong kzg version in versioned hash at index " & $i)
|
||||
|
||||
if tx.tx.versionedHashes[i] !=
|
||||
kzgToVersionedHash(tx.networkPayload.commitments[i]):
|
||||
if blob_versioned_hashes[i] != kzgToVersionedHash(blob_data.commitments[i]):
|
||||
return err("tx versioned hash not match commitments at index " & $i)
|
||||
|
||||
ok()
|
||||
|
|
|
@ -42,7 +42,8 @@ proc processTransactions*(vmState: BaseVMState;
|
|||
let rc = vmState.processTransaction(tx, sender, header)
|
||||
if rc.isErr:
|
||||
return err("Error processing tx with index " & $(txIndex) & ":" & rc.error)
|
||||
vmState.receipts[txIndex] = vmState.makeReceipt(tx.txType)
|
||||
vmState.receipts[txIndex] =
|
||||
vmState.makeReceipt(tx.payload.tx_type.get(TxLegacy))
|
||||
ok()
|
||||
|
||||
proc procBlkPreamble(vmState: BaseVMState;
|
||||
|
|
|
@ -60,7 +60,7 @@ proc commitOrRollbackDependingOnGasUsed(
|
|||
|
||||
# Return remaining gas to the block gas counter so it is
|
||||
# available for the next transaction.
|
||||
vmState.gasPool += tx.gasLimit - gasBurned
|
||||
vmState.gasPool += tx.payload.gas.GasInt - gasBurned
|
||||
return ok(gasBurned)
|
||||
|
||||
proc asyncProcessTransactionImpl(
|
||||
|
@ -77,24 +77,25 @@ proc asyncProcessTransactionImpl(
|
|||
let
|
||||
roDB = vmState.readOnlyStateDB
|
||||
baseFee256 = header.eip1559BaseFee(fork)
|
||||
baseFee = baseFee256.truncate(GasInt)
|
||||
tx = eip1559TxNormalization(tx, baseFee)
|
||||
priorityFee = min(tx.maxPriorityFee, tx.maxFee - baseFee)
|
||||
baseFee = baseFee256
|
||||
priorityFee = min(
|
||||
tx.payload.max_priority_fee_per_gas.get(tx.payload.max_fee_per_gas),
|
||||
tx.payload.max_fee_per_gas - baseFee).truncate(int64)
|
||||
excessBlobGas = header.excessBlobGas.get(0'u64)
|
||||
|
||||
# Return failure unless explicitely set `ok()`
|
||||
var res: Result[GasInt, string] = err("")
|
||||
|
||||
await ifNecessaryGetAccounts(vmState, @[sender, vmState.coinbase()])
|
||||
if tx.to.isSome:
|
||||
await ifNecessaryGetCode(vmState, tx.to.get)
|
||||
if tx.payload.to.isSome:
|
||||
await ifNecessaryGetCode(vmState, tx.payload.to.unsafeGet)
|
||||
|
||||
# buy gas, then the gas goes into gasMeter
|
||||
if vmState.gasPool < tx.gasLimit:
|
||||
if vmState.gasPool < tx.payload.gas.GasInt:
|
||||
return err("gas limit reached. gasLimit=$1, gasNeeded=$2" % [
|
||||
$vmState.gasPool, $tx.gasLimit])
|
||||
$vmState.gasPool, $tx.payload.gas])
|
||||
|
||||
vmState.gasPool -= tx.gasLimit
|
||||
vmState.gasPool -= tx.payload.gas.GasInt
|
||||
|
||||
# Actually, the eip-1559 reference does not mention an early exit.
|
||||
#
|
||||
|
@ -109,11 +110,11 @@ proc asyncProcessTransactionImpl(
|
|||
vmState.stateDB.clearTransientStorage()
|
||||
|
||||
# Execute the transaction.
|
||||
vmState.captureTxStart(tx.gasLimit)
|
||||
vmState.captureTxStart(tx.payload.gas.GasInt)
|
||||
let
|
||||
accTx = vmState.stateDB.beginSavepoint
|
||||
gasBurned = tx.txCallEvm(sender, vmState, fork)
|
||||
vmState.captureTxEnd(tx.gasLimit - gasBurned)
|
||||
vmState.captureTxEnd(tx.payload.gas.GasInt - gasBurned)
|
||||
|
||||
res = commitOrRollbackDependingOnGasUsed(vmState, accTx, header, tx, gasBurned, priorityFee)
|
||||
else:
|
||||
|
|
|
@ -452,7 +452,6 @@ export
|
|||
tx_item.GasPrice,
|
||||
tx_item.`<=`,
|
||||
tx_item.`<`,
|
||||
tx_item.effectiveGasTip,
|
||||
tx_item.info,
|
||||
tx_item.itemID,
|
||||
tx_item.sender,
|
||||
|
@ -630,6 +629,7 @@ proc assembleBlock*(
|
|||
xp.packerVmExec().isOkOr: # updates vmState
|
||||
return err(error)
|
||||
|
||||
let com = xp.chain.com
|
||||
var blk = EthBlock(
|
||||
header: xp.chain.getHeader # uses updated vmState
|
||||
)
|
||||
|
@ -639,20 +639,19 @@ proc assembleBlock*(
|
|||
for item in nonceList.incNonce:
|
||||
let tx = item.pooledTx
|
||||
blk.txs.add tx.tx
|
||||
if tx.networkPayload != nil:
|
||||
for k in tx.networkPayload.commitments:
|
||||
if tx.blob_data.isSome:
|
||||
if not com.forkGTE(Cancun):
|
||||
return err("PooledTransaction contains blobs prior to Cancun")
|
||||
for k in tx.blob_data.unsafeGet.commitments:
|
||||
blobsBundle.commitments.add k
|
||||
for p in tx.networkPayload.proofs:
|
||||
for p in tx.blob_data.unsafeGet.proofs:
|
||||
blobsBundle.proofs.add p
|
||||
for blob in tx.networkPayload.blobs:
|
||||
for blob in tx.blob_data.unsafeGet.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
|
||||
|
|
|
@ -16,7 +16,7 @@ import
|
|||
../../../common/common,
|
||||
../../../constants,
|
||||
../tx_item,
|
||||
eth/eip1559
|
||||
eth/[common/transaction, eip1559]
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
|
|
|
@ -242,10 +242,10 @@ proc verify*(xp: TxPoolRef): Result[void,TxInfo]
|
|||
if not initOk or lastSender != item.sender:
|
||||
initOk = true
|
||||
lastSender = item.sender
|
||||
lastNonce = item.tx.nonce
|
||||
lastNonce = item.tx.payload.nonce
|
||||
lastSublist = xp.txDB.bySender.eq(item.sender).value.data
|
||||
elif lastNonce + 1 == item.tx.nonce:
|
||||
lastNonce = item.tx.nonce
|
||||
elif lastNonce + 1 == item.tx.payload.nonce:
|
||||
lastNonce = item.tx.payload.nonce
|
||||
else:
|
||||
return err(txInfoVfyNonceChain)
|
||||
|
||||
|
@ -254,12 +254,12 @@ proc verify*(xp: TxPoolRef): Result[void,TxInfo]
|
|||
of txItemPending:
|
||||
discard
|
||||
of txItemStaged:
|
||||
if lastSublist.eq(txItemPending).eq(item.tx.nonce - 1).isOk:
|
||||
if lastSublist.eq(txItemPending).eq(item.tx.payload.nonce - 1).isOk:
|
||||
return err(txInfoVfyNonceChain)
|
||||
of txItemPacked:
|
||||
if lastSublist.eq(txItemPending).eq(item.tx.nonce - 1).isOk:
|
||||
if lastSublist.eq(txItemPending).eq(item.tx.payload.nonce - 1).isOk:
|
||||
return err(txInfoVfyNonceChain)
|
||||
if lastSublist.eq(txItemStaged).eq(item.tx.nonce - 1).isOk:
|
||||
if lastSublist.eq(txItemStaged).eq(item.tx.payload.nonce - 1).isOk:
|
||||
return err(txInfoVfyNonceChain)
|
||||
|
||||
ok()
|
||||
|
|
|
@ -17,22 +17,14 @@ import
|
|||
../../utils/ec_recover,
|
||||
../../utils/utils,
|
||||
./tx_info,
|
||||
eth/[common, keys],
|
||||
eth/[common, common/transaction, keys],
|
||||
stew/results
|
||||
|
||||
export transaction.GasPrice, transaction.GasPriceEx
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
type
|
||||
GasPrice* = ##|
|
||||
## Handy definition distinct from `GasInt` which is a commodity unit while
|
||||
## the `GasPrice` is the commodity valuation per unit of gas, similar to a
|
||||
## kind of currency.
|
||||
distinct uint64
|
||||
|
||||
GasPriceEx* = ##\
|
||||
## Similar to `GasPrice` but is allowed to be negative.
|
||||
distinct int64
|
||||
|
||||
TxItemStatus* = enum ##\
|
||||
## Current status of a transaction as seen by the pool.
|
||||
txItemPending = 0
|
||||
|
@ -115,12 +107,9 @@ proc init*(item: TxItemRef; status: TxItemStatus; info: string) =
|
|||
proc new*(T: type TxItemRef; tx: PooledTransaction; itemID: Hash256;
|
||||
status: TxItemStatus; info: string): Result[T,void] {.gcsafe,raises: [].} =
|
||||
## Create item descriptor.
|
||||
let rc = tx.tx.ecRecover
|
||||
if rc.isErr:
|
||||
return err()
|
||||
ok(T(itemID: itemID,
|
||||
tx: tx,
|
||||
sender: rc.value,
|
||||
sender: tx.tx.signature.from_address,
|
||||
timeStamp: utcTime(),
|
||||
info: info,
|
||||
status: status))
|
||||
|
@ -157,22 +146,7 @@ proc itemID*(tx: PooledTransaction): Hash256 =
|
|||
# core/types/transaction.go(297): func (tx *Transaction) Cost() *big.Int {
|
||||
proc cost*(tx: Transaction): UInt256 =
|
||||
## Getter (go/ref compat): gas * gasPrice + value.
|
||||
(tx.gasPrice * tx.gasLimit).u256 + tx.value
|
||||
|
||||
# core/types/transaction.go(332): .. *Transaction) EffectiveGasTip(baseFee ..
|
||||
# core/types/transaction.go(346): .. EffectiveGasTipValue(baseFee ..
|
||||
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:
|
||||
(tx.gasPrice - baseFee.int64).GasPriceEx
|
||||
else:
|
||||
# London, EIP1559
|
||||
min(tx.maxPriorityFee, tx.maxFee - baseFee.int64).GasPriceEx
|
||||
|
||||
proc effectiveGasTip*(tx: Transaction; baseFee: UInt256): GasPriceEx =
|
||||
## Variant of `effectiveGasTip()`
|
||||
tx.effectiveGasTip(baseFee.truncate(uint64).GasPrice)
|
||||
tx.payload.max_fee_per_gas * tx.payload.gas.u256 + tx.payload.value
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions, item getters
|
||||
|
|
|
@ -16,7 +16,7 @@ import
|
|||
std/[math],
|
||||
../tx_info,
|
||||
../tx_item,
|
||||
eth/[common],
|
||||
eth/[common, common/transaction],
|
||||
stew/[results, keyed_queue, keyed_queue/kq_debug, sorted_set]
|
||||
|
||||
{.push raises: [].}
|
||||
|
@ -120,7 +120,7 @@ proc getRank(schedData: TxSenderSchedRef): int64 =
|
|||
|
||||
proc maxProfit(item: TxItemRef; baseFee: GasPrice): float64 =
|
||||
## Profit calculator
|
||||
item.tx.gasLimit.float64 * item.tx.effectiveGasTip(baseFee).float64
|
||||
item.tx.payload.gas.float64 * item.tx.effectiveGasTip(baseFee).float64
|
||||
|
||||
proc recalcProfit(nonceData: TxSenderNonceRef; baseFee: GasPrice) =
|
||||
## Re-calculate profit value depending on `baseFee`
|
||||
|
@ -129,7 +129,7 @@ proc recalcProfit(nonceData: TxSenderNonceRef; baseFee: GasPrice) =
|
|||
while rc.isOk:
|
||||
let item = rc.value.data
|
||||
nonceData.profit += item.maxProfit(baseFee)
|
||||
rc = nonceData.nonceList.gt(item.tx.nonce)
|
||||
rc = nonceData.nonceList.gt(item.tx.payload.nonce)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private functions
|
||||
|
@ -152,7 +152,7 @@ proc mkInxImpl(gt: var TxSenderTab; item: TxItemRef): Result[TxSenderInx,void]
|
|||
inxData.schedData.allList = inxData.allNonce
|
||||
else:
|
||||
inxData.allNonce = inxData.schedData.allList
|
||||
let rc = inxData.allNonce.nonceList.insert(item.tx.nonce)
|
||||
let rc = inxData.allNonce.nonceList.insert(item.tx.payload.nonce)
|
||||
if rc.isErr:
|
||||
return err()
|
||||
rc.value.data = item
|
||||
|
@ -165,7 +165,7 @@ proc mkInxImpl(gt: var TxSenderTab; item: TxItemRef): Result[TxSenderInx,void]
|
|||
else:
|
||||
inxData.statusNonce = inxData.schedData.statusList[item.status]
|
||||
# this is a new item, checked at `all items sub-list` above
|
||||
inxData.statusNonce.nonceList.insert(item.tx.nonce).value.data = item
|
||||
inxData.statusNonce.nonceList.insert(item.tx.payload.nonce).value.data = item
|
||||
|
||||
return ok(inxData)
|
||||
|
||||
|
@ -214,10 +214,10 @@ proc insert*(gt: var TxSenderTab; item: TxItemRef): bool
|
|||
|
||||
inx.schedData.size.inc
|
||||
|
||||
inx.statusNonce.gasLimits += item.tx.gasLimit
|
||||
inx.statusNonce.gasLimits += item.tx.payload.gas.GasInt
|
||||
inx.statusNonce.profit += tip
|
||||
|
||||
inx.allNonce.gasLimits += item.tx.gasLimit
|
||||
inx.allNonce.gasLimits += item.tx.payload.gas.GasInt
|
||||
inx.allNonce.profit += tip
|
||||
return true
|
||||
|
||||
|
@ -233,21 +233,21 @@ proc delete*(gt: var TxSenderTab; item: TxItemRef): bool
|
|||
|
||||
inx.schedData.size.dec
|
||||
|
||||
discard inx.allNonce.nonceList.delete(item.tx.nonce)
|
||||
discard inx.allNonce.nonceList.delete(item.tx.payload.nonce)
|
||||
if inx.allNonce.nonceList.len == 0:
|
||||
# this was the last nonce for that sender account
|
||||
discard gt.addrList.delete(item.sender)
|
||||
return true
|
||||
|
||||
inx.allNonce.gasLimits -= item.tx.gasLimit
|
||||
inx.allNonce.gasLimits -= item.tx.payload.gas.GasInt
|
||||
inx.allNonce.profit -= tip
|
||||
|
||||
discard inx.statusNonce.nonceList.delete(item.tx.nonce)
|
||||
discard inx.statusNonce.nonceList.delete(item.tx.payload.nonce)
|
||||
if inx.statusNonce.nonceList.len == 0:
|
||||
inx.schedData.statusList[item.status] = nil
|
||||
return true
|
||||
|
||||
inx.statusNonce.gasLimits -= item.tx.gasLimit
|
||||
inx.statusNonce.gasLimits -= item.tx.payload.gas.GasInt
|
||||
inx.statusNonce.profit -= tip
|
||||
return true
|
||||
|
||||
|
@ -293,7 +293,7 @@ proc verify*(gt: var TxSenderTab): Result[void,TxInfo]
|
|||
let (nonceKey, item) = (rcNonce.value.key, rcNonce.value.data)
|
||||
rcNonce = statusData.nonceList.gt(nonceKey)
|
||||
|
||||
statusGas += item.tx.gasLimit
|
||||
statusGas += item.tx.payload.gas.GasInt
|
||||
statusCount.inc
|
||||
|
||||
bucketProfit += item.maxProfit(gt.baseFee)
|
||||
|
@ -330,7 +330,7 @@ proc verify*(gt: var TxSenderTab): Result[void,TxInfo]
|
|||
rcNonce = allData.nonceList.gt(nonceKey)
|
||||
|
||||
allProfit += item.maxProfit(gt.baseFee)
|
||||
allGas += item.tx.gasLimit
|
||||
allGas += item.tx.payload.gas.GasInt
|
||||
allCount.inc
|
||||
|
||||
if differs(allData.profit, allProfit):
|
||||
|
|
|
@ -77,7 +77,7 @@ proc mkInxImpl(sq: var TxStatusTab; item: TxItemRef): Result[TxStatusInx,void]
|
|||
inx.addrData.addrList[item.sender] = inx.nonceData
|
||||
|
||||
# nonce sublist
|
||||
let rc = inx.nonceData.nonceList.insert(item.tx.nonce)
|
||||
let rc = inx.nonceData.nonceList.insert(item.tx.payload.nonce)
|
||||
if rc.isErr:
|
||||
return err()
|
||||
rc.value.data = item
|
||||
|
@ -120,7 +120,7 @@ proc insert*(sq: var TxStatusTab; item: TxItemRef): bool
|
|||
let inx = rc.value
|
||||
sq.size.inc
|
||||
inx.addrData.size.inc
|
||||
inx.addrData.gasLimits += item.tx.gasLimit
|
||||
inx.addrData.gasLimits += item.tx.payload.gas.GasInt
|
||||
return true
|
||||
|
||||
|
||||
|
@ -132,9 +132,9 @@ proc delete*(sq: var TxStatusTab; item: TxItemRef): bool
|
|||
|
||||
sq.size.dec
|
||||
inx.addrData.size.dec
|
||||
inx.addrData.gasLimits -= item.tx.gasLimit
|
||||
inx.addrData.gasLimits -= item.tx.payload.gas.GasInt
|
||||
|
||||
discard inx.nonceData.nonceList.delete(item.tx.nonce)
|
||||
discard inx.nonceData.nonceList.delete(item.tx.payload.nonce)
|
||||
if inx.nonceData.nonceList.len == 0:
|
||||
discard inx.addrData.addrList.delete(item.sender)
|
||||
|
||||
|
@ -174,7 +174,7 @@ proc verify*(sq: var TxStatusTab): Result[void,TxInfo]
|
|||
let (nonceKey, item) = (rcNonce.value.key, rcNonce.value.data)
|
||||
rcNonce = nonceData.nonceList.gt(nonceKey)
|
||||
|
||||
gasLimits += item.tx.gasLimit
|
||||
gasLimits += item.tx.payload.gas.GasInt
|
||||
addrCount.inc
|
||||
|
||||
if addrCount != addrData.size:
|
||||
|
|
|
@ -72,14 +72,17 @@ proc supersede(xp: TxPoolRef; item: TxItemRef): Result[void,TxInfo]
|
|||
var current: TxItemRef
|
||||
|
||||
block:
|
||||
let rc = xp.txDB.bySender.eq(item.sender).sub.eq(item.tx.nonce)
|
||||
let rc = xp.txDB.bySender.eq(item.sender).sub.eq(item.tx.payload.nonce)
|
||||
if rc.isErr:
|
||||
return err(txInfoErrUnspecified)
|
||||
current = rc.value.data
|
||||
|
||||
# verify whether replacing is allowed, at all
|
||||
let bumpPrice = (current.tx.gasPrice * xp.priceBump.GasInt + 99) div 100
|
||||
if item.tx.gasPrice < current.tx.gasPrice + bumpPrice:
|
||||
let bumpPrice = (
|
||||
current.tx.payload.max_fee_per_gas.truncate(int64) *
|
||||
xp.priceBump.GasInt + 99) div 100
|
||||
if item.tx.payload.max_fee_per_gas.truncate(int64) <
|
||||
current.tx.payload.max_fee_per_gas.truncate(int64) + bumpPrice:
|
||||
discard # return err(txInfoErrReplaceUnderpriced)
|
||||
|
||||
# make space, delete item
|
||||
|
@ -181,7 +184,7 @@ proc addTxs*(xp: TxPoolRef;
|
|||
for tx in txs.items:
|
||||
var reason: TxInfo
|
||||
|
||||
if tx.tx.txType == TxEip4844:
|
||||
if tx.blob_data.isSome:
|
||||
let res = tx.validateBlobTransactionWrapper()
|
||||
if res.isErr:
|
||||
# move item to waste basket
|
||||
|
@ -197,7 +200,7 @@ proc addTxs*(xp: TxPoolRef;
|
|||
else:
|
||||
let
|
||||
item = rcTx.value
|
||||
rcInsert = accTab.getItemList(item.sender).insert(item.tx.nonce)
|
||||
rcInsert = accTab.getItemList(item.sender).insert(item.tx.payload.nonce)
|
||||
if rcInsert.isErr:
|
||||
reason = txInfoErrSenderNonceIndex
|
||||
else:
|
||||
|
|
|
@ -53,7 +53,7 @@ proc bucketItemsReassignPending*(xp: TxPoolRef; labelFrom: TxItemStatus;
|
|||
proc bucketItemsReassignPending*(xp: TxPoolRef; item: TxItemRef)
|
||||
{.gcsafe,raises: [CatchableError].} =
|
||||
## Variant of `bucketItemsReassignPending()`
|
||||
xp.bucketItemsReassignPending(item.status, item.sender, item.tx.nonce)
|
||||
xp.bucketItemsReassignPending(item.status, item.sender, item.tx.payload.nonce)
|
||||
|
||||
|
||||
proc bucketUpdateAll*(xp: TxPoolRef): bool
|
||||
|
@ -71,10 +71,10 @@ proc bucketUpdateAll*(xp: TxPoolRef): bool
|
|||
for item in xp.pDoubleCheck:
|
||||
if item.reject == txInfoOk:
|
||||
# Check whether there was a gap when the head was moved backwards.
|
||||
let rc = xp.txDB.bySender.eq(item.sender).sub.gt(item.tx.nonce)
|
||||
let rc = xp.txDB.bySender.eq(item.sender).sub.gt(item.tx.payload.nonce)
|
||||
if rc.isOk:
|
||||
let nextItem = rc.value.data
|
||||
if item.tx.nonce + 1 < nextItem.tx.nonce:
|
||||
if item.tx.payload.nonce + 1 < nextItem.tx.payload.nonce:
|
||||
discard xp.disposeItemAndHigherNonces(
|
||||
item, txInfoErrNonceGap, txInfoErrImpliedNonceGap)
|
||||
else:
|
||||
|
|
|
@ -57,19 +57,19 @@ proc checkTxNonce(xp: TxPoolRef; item: TxItemRef): bool
|
|||
# get the next applicable nonce as registered on the account database
|
||||
let accountNonce = xp.chain.getNonce(item.sender)
|
||||
|
||||
if item.tx.nonce < accountNonce:
|
||||
if item.tx.payload.nonce < accountNonce:
|
||||
debug "invalid tx: account nonce too small",
|
||||
txNonce = item.tx.nonce,
|
||||
txNonce = item.tx.payload.nonce,
|
||||
accountNonce
|
||||
return false
|
||||
|
||||
elif accountNonce < item.tx.nonce:
|
||||
elif accountNonce < item.tx.payload.nonce:
|
||||
# for an existing account, nonces must come in increasing consecutive order
|
||||
let rc = xp.txDB.bySender.eq(item.sender)
|
||||
if rc.isOk:
|
||||
if rc.value.data.sub.eq(item.tx.nonce - 1).isErr:
|
||||
if rc.value.data.sub.eq(item.tx.payload.nonce - 1).isErr:
|
||||
debug "invalid tx: account nonces gap",
|
||||
txNonce = item.tx.nonce,
|
||||
txNonce = item.tx.payload.nonce,
|
||||
accountNonce
|
||||
return false
|
||||
|
||||
|
@ -87,7 +87,7 @@ proc txNonceActive(xp: TxPoolRef; item: TxItemRef): bool
|
|||
if rc.isErr:
|
||||
return true
|
||||
# Must not be in the `pending` bucket.
|
||||
if rc.value.data.eq(txItemPending).eq(item.tx.nonce - 1).isOk:
|
||||
if rc.value.data.eq(txItemPending).eq(item.tx.payload.nonce - 1).isOk:
|
||||
return false
|
||||
true
|
||||
|
||||
|
@ -96,30 +96,31 @@ proc txGasCovered(xp: TxPoolRef; item: TxItemRef): bool =
|
|||
## Check whether the max gas consumption is within the gas limit (aka block
|
||||
## size).
|
||||
let trgLimit = xp.chain.limits.trgLimit
|
||||
if trgLimit < item.tx.gasLimit:
|
||||
if trgLimit < item.tx.payload.gas.GasInt:
|
||||
debug "invalid tx: gasLimit exceeded",
|
||||
maxLimit = trgLimit,
|
||||
gasLimit = item.tx.gasLimit
|
||||
gasLimit = item.tx.payload.gas
|
||||
return false
|
||||
true
|
||||
|
||||
proc txFeesCovered(xp: TxPoolRef; item: TxItemRef): bool =
|
||||
## Ensure that the user was willing to at least pay the base fee
|
||||
## And to at least pay the current data gasprice
|
||||
if item.tx.txType >= TxEip1559:
|
||||
if item.tx.maxFee.GasPriceEx < xp.chain.baseFee:
|
||||
if item.tx.payload.tx_type.get(TxLegacy) >= TxEip1559:
|
||||
if item.tx.payload.max_fee_per_gas.truncate(int64).GasPriceEx <
|
||||
xp.chain.baseFee:
|
||||
debug "invalid tx: maxFee is smaller than baseFee",
|
||||
maxFee = item.tx.maxFee,
|
||||
maxFee = item.tx.payload.max_fee_per_gas,
|
||||
baseFee = xp.chain.baseFee
|
||||
return false
|
||||
|
||||
if item.tx.txType >= TxEip4844:
|
||||
if item.tx.payload.max_fee_per_blob_gas.isSome:
|
||||
let
|
||||
excessBlobGas = xp.chain.excessBlobGas
|
||||
blobGasPrice = getBlobBaseFee(excessBlobGas)
|
||||
if item.tx.maxFeePerBlobGas < blobGasPrice:
|
||||
if item.tx.payload.max_fee_per_blob_gas.unsafeGet < blobGasPrice:
|
||||
debug "invalid tx: maxFeePerBlobGas smaller than blobGasPrice",
|
||||
maxFeePerBlobGas=item.tx.maxFeePerBlobGas,
|
||||
maxFeePerBlobGas=item.tx.payload.max_fee_per_blob_gas.unsafeGet,
|
||||
blobGasPrice=blobGasPrice
|
||||
return false
|
||||
true
|
||||
|
@ -135,11 +136,11 @@ proc txCostInBudget(xp: TxPoolRef; item: TxItemRef): bool =
|
|||
require = gasCost
|
||||
return false
|
||||
let balanceOffGasCost = balance - gasCost
|
||||
if balanceOffGasCost < item.tx.value:
|
||||
if balanceOffGasCost < item.tx.payload.value:
|
||||
debug "invalid tx: not enough cash to send",
|
||||
available = balance,
|
||||
availableMinusGas = balanceOffGasCost,
|
||||
require = item.tx.value
|
||||
require = item.tx.payload.value
|
||||
return false
|
||||
true
|
||||
|
||||
|
@ -147,10 +148,11 @@ proc txCostInBudget(xp: TxPoolRef; item: TxItemRef): bool =
|
|||
proc txPreLondonAcceptableGasPrice(xp: TxPoolRef; item: TxItemRef): bool =
|
||||
## For legacy transactions check whether minimum gas price and tip are
|
||||
## high enough. These checks are optional.
|
||||
if item.tx.txType < TxEip1559:
|
||||
if item.tx.payload.tx_type.get(TxLegacy) < TxEip1559:
|
||||
|
||||
if stageItemsPlMinPrice in xp.pFlags:
|
||||
if item.tx.gasPrice.GasPriceEx < xp.pMinPlGasPrice:
|
||||
if item.tx.payload.max_fee_per_gas.truncate(int64).GasPriceEx <
|
||||
xp.pMinPlGasPrice:
|
||||
return false
|
||||
|
||||
elif stageItems1559MinTip in xp.pFlags:
|
||||
|
@ -161,14 +163,15 @@ proc txPreLondonAcceptableGasPrice(xp: TxPoolRef; item: TxItemRef): bool =
|
|||
|
||||
proc txPostLondonAcceptableTipAndFees(xp: TxPoolRef; item: TxItemRef): bool =
|
||||
## Helper for `classifyTxPacked()`
|
||||
if item.tx.txType >= TxEip1559:
|
||||
if item.tx.payload.tx_type.get(TxLegacy) >= TxEip1559:
|
||||
|
||||
if stageItems1559MinTip in xp.pFlags:
|
||||
if item.tx.effectiveGasTip(xp.chain.baseFee) < xp.pMinTipPrice:
|
||||
return false
|
||||
|
||||
if stageItems1559MinFee in xp.pFlags:
|
||||
if item.tx.maxFee.GasPriceEx < xp.pMinFeePrice:
|
||||
if item.tx.payload.max_fee_per_gas.truncate(int64).GasPriceEx <
|
||||
xp.pMinFeePrice:
|
||||
return false
|
||||
true
|
||||
|
||||
|
@ -231,11 +234,10 @@ proc classifyValidatePacked*(xp: TxPoolRef;
|
|||
xp.chain.limits.maxLimit
|
||||
else:
|
||||
xp.chain.limits.trgLimit
|
||||
tx = item.tx.eip1559TxNormalization(xp.chain.baseFee.GasInt)
|
||||
excessBlobGas = calcExcessBlobGas(vmState.parent)
|
||||
|
||||
roDB.validateTransaction(
|
||||
tx, item.sender, gasLimit, baseFee, excessBlobGas, fork).isOk
|
||||
item.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
|
||||
|
|
|
@ -46,7 +46,7 @@ proc deleteOtherNonces(xp: TxPoolRef; item: TxItemRef; newerThan: Time): bool
|
|||
{.gcsafe,raises: [KeyError].} =
|
||||
let rc = xp.txDB.bySender.eq(item.sender).sub
|
||||
if rc.isOk:
|
||||
for other in rc.value.data.incNonce(item.tx.nonce):
|
||||
for other in rc.value.data.incNonce(item.tx.payload.nonce):
|
||||
# only delete non-expired items
|
||||
if newerThan < other.timeStamp:
|
||||
discard xp.txDB.dispose(other, txInfoErrTxExpiredImplied)
|
||||
|
@ -105,7 +105,7 @@ proc disposeItemAndHigherNonces*(xp: TxPoolRef; item: TxItemRef;
|
|||
if rc.isOk:
|
||||
let nonceList = rc.value.data
|
||||
|
||||
for otherItem in nonceList.incNonce(item.tx.nonce):
|
||||
for otherItem in nonceList.incNonce(item.tx.payload.nonce):
|
||||
if xp.txDB.dispose(otherItem, otherReason):
|
||||
result.inc
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ proc insert(xp: TxPoolRef; kq: TxHeadDiffRef; blockHash: Hash256)
|
|||
{.gcsafe,raises: [CatchableError].} =
|
||||
let db = xp.chain.com.db
|
||||
for tx in db.getBlockBody(blockHash).transactions:
|
||||
if tx.versionedHashes.len > 0:
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
# 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.
|
||||
|
|
|
@ -27,7 +27,7 @@ import
|
|||
../../../transaction,
|
||||
../../../vm_state,
|
||||
../../../vm_types,
|
||||
".."/[tx_chain, tx_desc, tx_item, tx_tabs, tx_tabs/tx_status, tx_info],
|
||||
".."/[tx_chain, tx_desc, tx_item, tx_tabs, tx_tabs/tx_status],
|
||||
"."/[tx_bucket, tx_classify]
|
||||
|
||||
type
|
||||
|
@ -87,11 +87,10 @@ proc runTx(pst: TxPackerStateRef; item: TxItemRef): GasInt
|
|||
let
|
||||
fork = pst.xp.chain.nextFork
|
||||
baseFee = pst.xp.chain.baseFee
|
||||
tx = item.tx.eip1559TxNormalization(baseFee.GasInt)
|
||||
|
||||
#safeExecutor "tx_packer.runTx":
|
||||
# # Execute transaction, may return a wildcard `Exception`
|
||||
result = tx.txCallEvm(item.sender, pst.xp.chain.vmState, fork)
|
||||
result = item.tx.txCallEvm(item.sender, pst.xp.chain.vmState, fork)
|
||||
|
||||
pst.cleanState = false
|
||||
doAssert 0 <= result
|
||||
|
@ -130,14 +129,15 @@ proc runTxCommit(pst: TxPackerStateRef; item: TxItemRef; gasBurned: GasInt)
|
|||
|
||||
# Return remaining gas to the block gas counter so it is
|
||||
# available for the next transaction.
|
||||
vmState.gasPool += item.tx.gasLimit - gasBurned
|
||||
vmState.gasPool += item.tx.payload.gas.GasInt - gasBurned
|
||||
|
||||
# gasUsed accounting
|
||||
vmState.cumulativeGasUsed += gasBurned
|
||||
vmState.receipts[inx] = vmState.makeReceipt(item.tx.txType)
|
||||
vmState.receipts[inx] =
|
||||
vmState.makeReceipt(item.tx.payload.tx_type.get(TxLegacy))
|
||||
|
||||
# EIP-4844, count blobGasUsed
|
||||
if item.tx.txType >= TxEip4844:
|
||||
if item.tx.payload.max_fee_per_blob_gas.isSome:
|
||||
pst.blobGasUsed += item.tx.getTotalBlobGas
|
||||
|
||||
# Update txRoot
|
||||
|
@ -175,7 +175,7 @@ proc vmExecInit(xp: TxPoolRef): Result[TxPackerStateRef, string]
|
|||
|
||||
let packer = TxPackerStateRef( # return value
|
||||
xp: xp,
|
||||
tr: newCoreDbRef(LegacyDbMemory).mptPrune,
|
||||
tr: newCoreDbRef(LegacyDbMemory, xp.chain.com.chainId).mptPrune,
|
||||
balance: xp.chain.vmState.readOnlyStateDB.getBalance(xp.chain.feeRecipient),
|
||||
numBlobPerBlock: 0,
|
||||
)
|
||||
|
@ -189,22 +189,23 @@ proc vmExecGrabItem(pst: TxPackerStateRef; item: TxItemRef): Result[bool,void]
|
|||
xp = pst.xp
|
||||
vmState = xp.chain.vmState
|
||||
|
||||
if not item.tx.validateChainId(xp.chain.com.chainId):
|
||||
discard xp.txDB.dispose(item, txInfoChainIdMismatch)
|
||||
return ok(false) # continue with next account
|
||||
|
||||
# EIP-4844
|
||||
if pst.numBlobPerBlock + item.tx.versionedHashes.len > MAX_BLOBS_PER_BLOCK:
|
||||
numBlobVersionedHashes =
|
||||
if item.tx.payload.blob_versioned_hashes.isSome:
|
||||
item.tx.payload.blob_versioned_hashes.unsafeGet.len
|
||||
else:
|
||||
0
|
||||
if pst.numBlobPerBlock + numBlobVersionedHashes > MAX_BLOBS_PER_BLOCK:
|
||||
return err() # stop collecting
|
||||
pst.numBlobPerBlock += item.tx.versionedHashes.len
|
||||
pst.numBlobPerBlock += numBlobVersionedHashes
|
||||
|
||||
# Verify we have enough gas in gasPool
|
||||
if vmState.gasPool < item.tx.gasLimit:
|
||||
if vmState.gasPool < item.tx.payload.gas.GasInt:
|
||||
# skip this transaction and
|
||||
# continue with next account
|
||||
# if we don't have enough gas
|
||||
return ok(false)
|
||||
vmState.gasPool -= item.tx.gasLimit
|
||||
vmState.gasPool -= item.tx.payload.gas.GasInt
|
||||
|
||||
# Validate transaction relative to the current vmState
|
||||
if not xp.classifyValidatePacked(vmState, item):
|
||||
|
|
|
@ -217,12 +217,8 @@ proc validateUncles(com: CommonRef; header: BlockHeader;
|
|||
# ------------------------------------------------------------------------------
|
||||
|
||||
func gasCost*(tx: Transaction): UInt256 =
|
||||
if tx.txType >= TxEip4844:
|
||||
tx.gasLimit.u256 * tx.maxFee.u256 + tx.getTotalBlobGas.u256 * tx.maxFeePerBlobGas.u256
|
||||
elif tx.txType >= TxEip1559:
|
||||
tx.gasLimit.u256 * tx.maxFee.u256
|
||||
else:
|
||||
tx.gasLimit.u256 * tx.gasPrice.u256
|
||||
tx.payload.gas.u256 * tx.payload.max_fee_per_gas +
|
||||
tx.getTotalBlobGas.u256 * tx.payload.max_fee_per_blob_gas.get(UInt256.zero)
|
||||
|
||||
proc validateTxBasic*(
|
||||
tx: Transaction; ## tx to validate
|
||||
|
@ -230,54 +226,63 @@ proc validateTxBasic*(
|
|||
validateFork: bool = true): Result[void, string] =
|
||||
|
||||
if validateFork:
|
||||
if tx.txType == TxEip2930 and fork < FkBerlin:
|
||||
return err("invalid tx: Eip2930 Tx type detected before Berlin")
|
||||
case tx.payload.tx_type.get(TxLegacy)
|
||||
of TxEip4844:
|
||||
if fork < FkCancun:
|
||||
return err("invalid tx: Eip4844 Tx type detected before Cancun")
|
||||
of TxEip1559:
|
||||
if fork < FkLondon:
|
||||
return err("invalid tx: Eip1559 Tx type detected before London")
|
||||
of TxEip2930:
|
||||
if fork < FkBerlin:
|
||||
return err("invalid tx: Eip2930 Tx type detected before Berlin")
|
||||
of TxLegacy:
|
||||
if tx.payload.tx_type.isSome and fork < FkHomestead:
|
||||
return err("invalid tx: Tx with chain ID detected before Homestead")
|
||||
|
||||
if tx.txType == TxEip1559 and fork < FkLondon:
|
||||
return err("invalid tx: Eip1559 Tx type detected before London")
|
||||
|
||||
if tx.txType == TxEip4844 and fork < FkCancun:
|
||||
return err("invalid tx: Eip4844 Tx type detected before Cancun")
|
||||
|
||||
if fork >= FkShanghai and tx.contractCreation and tx.payload.len > EIP3860_MAX_INITCODE_SIZE:
|
||||
if fork >= FkShanghai and
|
||||
tx.contractCreation and tx.payload.input.len > EIP3860_MAX_INITCODE_SIZE:
|
||||
return err("invalid tx: initcode size exceeds maximum")
|
||||
|
||||
try:
|
||||
# The total must be the larger of the two
|
||||
if tx.maxFee < tx.maxPriorityFee:
|
||||
return err("invalid tx: maxFee is smaller than maPriorityFee. maxFee=$1, maxPriorityFee=$2" % [
|
||||
$tx.maxFee, $tx.maxPriorityFee])
|
||||
if tx.payload.max_fee_per_gas <
|
||||
tx.payload.max_priority_fee_per_gas.get(tx.payload.max_fee_per_gas):
|
||||
return err("invalid tx: maxFee is smaller than maxPriorityFee. maxFee=$1, maxPriorityFee=$2" % [
|
||||
$tx.payload.max_fee_per_gas, $tx.payload.max_priority_fee_per_gas])
|
||||
|
||||
if tx.gasLimit < tx.intrinsicGas(fork):
|
||||
if tx.payload.gas.int64 < tx.intrinsicGas(fork):
|
||||
return err("invalid tx: not enough gas to perform calculation. avail=$1, require=$2" % [
|
||||
$tx.gasLimit, $tx.intrinsicGas(fork)])
|
||||
$tx.payload.gas, $tx.intrinsicGas(fork)])
|
||||
|
||||
if fork >= FkCancun:
|
||||
if tx.payload.len > MAX_CALLDATA_SIZE:
|
||||
if tx.payload.input.len > MAX_CALLDATA_SIZE:
|
||||
return err("invalid tx: payload len exceeds MAX_CALLDATA_SIZE. len=" &
|
||||
$tx.payload.len)
|
||||
$tx.payload.input.len)
|
||||
|
||||
if tx.accessList.len > MAX_ACCESS_LIST_SIZE:
|
||||
return err("invalid tx: access list len exceeds MAX_ACCESS_LIST_SIZE. len=" &
|
||||
$tx.accessList.len)
|
||||
if tx.payload.access_list.isSome:
|
||||
if tx.payload.access_list.unsafeGet.len > MAX_ACCESS_LIST_SIZE:
|
||||
return err("invalid tx: access list len exceeds MAX_ACCESS_LIST_SIZE. len=" &
|
||||
$tx.payload.access_list.unsafeGet.len)
|
||||
|
||||
for i, acl in tx.accessList:
|
||||
if acl.storageKeys.len > MAX_ACCESS_LIST_STORAGE_KEYS:
|
||||
return err("invalid tx: access list storage keys len exceeds MAX_ACCESS_LIST_STORAGE_KEYS. " &
|
||||
"index=$1, len=$2" % [$i, $acl.storageKeys.len])
|
||||
for i, acl in tx.payload.access_list.unsafeGet:
|
||||
if acl.storage_keys.len > MAX_ACCESS_LIST_STORAGE_KEYS:
|
||||
return err("invalid tx: access list storage keys len exceeds MAX_ACCESS_LIST_STORAGE_KEYS. " &
|
||||
"index=$1, len=$2" % [$i, $acl.storage_keys.len])
|
||||
|
||||
if tx.txType >= TxEip4844:
|
||||
if tx.to.isNone:
|
||||
if tx.payload.tx_type == Opt.some TxEip4844:
|
||||
if tx.payload.to.isNone:
|
||||
return err("invalid tx: destination must be not empty")
|
||||
|
||||
if tx.versionedHashes.len == 0:
|
||||
if tx.payload.blob_versioned_hashes.isNone or
|
||||
tx.payload.blob_versioned_hashes.unsafeGet.len == 0:
|
||||
return err("invalid tx: there must be at least one blob")
|
||||
|
||||
if tx.versionedHashes.len > MAX_BLOBS_PER_BLOCK:
|
||||
if tx.payload.blob_versioned_hashes.unsafeGet.len > MAX_BLOBS_PER_BLOCK:
|
||||
return err("invalid tx: versioned hashes len exceeds MAX_BLOBS_PER_BLOCK=" & $MAX_BLOBS_PER_BLOCK &
|
||||
". get=" & $tx.versionedHashes.len)
|
||||
". get=" & $tx.payload.blob_versioned_hashes.unsafeGet.len)
|
||||
|
||||
for i, bv in tx.versionedHashes:
|
||||
for i, bv in tx.payload.blob_versioned_hashes.unsafeGet:
|
||||
if bv.data[0] != VERSIONED_HASH_VERSION_KZG:
|
||||
return err("invalid tx: one of blobVersionedHash has invalid version. " &
|
||||
"get=$1, expect=$2" % [$bv.data[0].int, $VERSIONED_HASH_VERSION_KZG.int])
|
||||
|
@ -320,14 +325,14 @@ proc validateTransaction*(
|
|||
# The parallel lowGasLimit.json test never triggers the case checked below
|
||||
# as the paricular transaction is omitted (the txs list is just set empty.)
|
||||
try:
|
||||
if maxLimit < tx.gasLimit:
|
||||
if maxLimit < tx.payload.gas.int64:
|
||||
return err("invalid tx: block header gasLimit exceeded. maxLimit=$1, gasLimit=$2" % [
|
||||
$maxLimit, $tx.gasLimit])
|
||||
$maxLimit, $tx.payload.gas])
|
||||
|
||||
# ensure that the user was willing to at least pay the base fee
|
||||
if tx.maxFee < baseFee.truncate(int64):
|
||||
if tx.payload.max_fee_per_gas < baseFee:
|
||||
return err("invalid tx: maxFee is smaller than baseFee. maxFee=$1, baseFee=$2" % [
|
||||
$tx.maxFee, $baseFee])
|
||||
$tx.payload.max_fee_per_gas, $baseFee])
|
||||
|
||||
# the signer must be able to fully afford the transaction
|
||||
let gasCost = tx.gasCost()
|
||||
|
@ -336,15 +341,15 @@ proc validateTransaction*(
|
|||
return err("invalid tx: not enough cash for gas. avail=$1, require=$2" % [
|
||||
$balance, $gasCost])
|
||||
|
||||
if balance - gasCost < tx.value:
|
||||
if balance - gasCost < tx.payload.value:
|
||||
return err("invalid tx: not enough cash to send. avail=$1, availMinusGas=$2, require=$3" % [
|
||||
$balance, $(balance-gasCost), $tx.value])
|
||||
$balance, $(balance-gasCost), $tx.payload.value])
|
||||
|
||||
if tx.nonce != nonce:
|
||||
if tx.payload.nonce != nonce:
|
||||
return err("invalid tx: account nonce mismatch. txNonce=$1, accNonce=$2" % [
|
||||
$tx.nonce, $nonce])
|
||||
$tx.payload.nonce, $nonce])
|
||||
|
||||
if tx.nonce == high(uint64):
|
||||
if tx.payload.nonce == high(uint64):
|
||||
return err("invalid tx: nonce at maximum")
|
||||
|
||||
# EIP-3607 Reject transactions from senders with deployed code
|
||||
|
@ -357,12 +362,13 @@ proc validateTransaction*(
|
|||
return err("invalid tx: sender is not an EOA. sender=$1, codeHash=$2" % [
|
||||
sender.toHex, codeHash.data.toHex])
|
||||
|
||||
if tx.txType >= TxEip4844:
|
||||
if tx.payload.max_fee_per_blob_gas.isSome:
|
||||
# ensure that the user was willing to at least pay the current data gasprice
|
||||
let blobGasPrice = getBlobBaseFee(excessBlobGas)
|
||||
if tx.maxFeePerBlobGas < blobGasPrice:
|
||||
if tx.payload.max_fee_per_blob_gas.unsafeGet < blobGasPrice:
|
||||
return err("invalid tx: maxFeePerBlobGas smaller than blobGasPrice. " &
|
||||
"maxFeePerBlobGas=$1, blobGasPrice=$2" % [$tx.maxFeePerBlobGas, $blobGasPrice])
|
||||
"maxFeePerBlobGas=$1, blobGasPrice=$2" %
|
||||
[$tx.payload.max_fee_per_blob_gas, $blobGasPrice])
|
||||
|
||||
except CatchableError as ex:
|
||||
return err(ex.msg)
|
||||
|
|
|
@ -23,9 +23,10 @@ type
|
|||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
func toStorageKeys(slots: SlotSet): seq[StorageKey] =
|
||||
func toStorageKeys(slots: SlotSet): StorageKeys =
|
||||
for slot in slots:
|
||||
result.add slot.toBytesBE
|
||||
let ok = result.add slot.toBytesBE()
|
||||
doAssert ok, "StorageKeys capacity exceeded"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public constructors
|
||||
|
@ -71,10 +72,11 @@ proc clear*(ac: var AccessList) {.inline.} =
|
|||
|
||||
func getAccessList*(ac: AccessList): common.AccessList =
|
||||
for address, slots in ac.slots:
|
||||
result.add common.AccessPair(
|
||||
let ok = result.add common.AccessPair(
|
||||
address : address,
|
||||
storageKeys: slots.toStorageKeys,
|
||||
)
|
||||
doAssert ok, "AccessList capacity exceeded"
|
||||
|
||||
func equal*(ac: AccessList, other: var AccessList): bool =
|
||||
if ac.slots.len != other.slots.len:
|
||||
|
|
|
@ -287,6 +287,7 @@ type
|
|||
profTab*: CoreDbProfListRef ## Profiling data (if any)
|
||||
ledgerHook*: RootRef ## Debugging/profiling, to be used by ledger
|
||||
methods*: CoreDbBaseFns
|
||||
chainId*: ChainId
|
||||
|
||||
CoreDbErrorRef* = ref object of RootRef
|
||||
## Generic error object
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
import
|
||||
std/[algorithm, options, sequtils],
|
||||
chronicles,
|
||||
eth/[common, rlp],
|
||||
eth/[common, common/transaction, rlp],
|
||||
results,
|
||||
stew/byteutils,
|
||||
"../.."/[errors, constants],
|
||||
|
@ -150,7 +150,8 @@ iterator getBlockTransactions*(
|
|||
): Transaction
|
||||
{.gcsafe, raises: [RlpError].} =
|
||||
for encodedTx in db.getBlockTransactionData(header.txRoot):
|
||||
yield rlp.decode(encodedTx, Transaction)
|
||||
yield Transaction.fromBytes(encodedTx, db.chainId).valueOr:
|
||||
raise (ref MalformedRlpError)(msg: "Invalid transaction in block")
|
||||
|
||||
|
||||
iterator getBlockTransactionHashes*(
|
||||
|
@ -161,8 +162,9 @@ iterator getBlockTransactionHashes*(
|
|||
## Returns an iterable of the transaction hashes from th block specified
|
||||
## by the given block header.
|
||||
for encodedTx in db.getBlockTransactionData(blockHeader.txRoot):
|
||||
let tx = rlp.decode(encodedTx, Transaction)
|
||||
yield rlpHash(tx) # beware EIP-4844
|
||||
let tx = Transaction.fromBytes(encodedTx, db.chainId).valueOr:
|
||||
raise (ref MalformedRlpError)(msg: "Invalid transaction in block")
|
||||
yield tx.compute_tx_hash(db.chainId)
|
||||
|
||||
|
||||
iterator getWithdrawalsData*(
|
||||
|
|
|
@ -53,6 +53,7 @@ export
|
|||
|
||||
proc newCoreDbRef*(
|
||||
db: TrieDatabaseRef;
|
||||
chainId: ChainId;
|
||||
): CoreDbRef
|
||||
{.gcsafe, deprecated: "use newCoreDbRef(LegacyDbPersistent,<path>)".} =
|
||||
## Legacy constructor.
|
||||
|
@ -60,10 +61,13 @@ proc newCoreDbRef*(
|
|||
## Note: Using legacy notation `newCoreDbRef()` rather than
|
||||
## `CoreDbRef.init()` because of compiler coughing.
|
||||
##
|
||||
db.newLegacyPersistentCoreDbRef()
|
||||
let res = db.newLegacyPersistentCoreDbRef()
|
||||
res.chainId = chainId
|
||||
res
|
||||
|
||||
proc newCoreDbRef*(
|
||||
dbType: static[CoreDbType]; # Database type symbol
|
||||
chainId: ChainId;
|
||||
): CoreDbRef =
|
||||
## Constructor for volatile/memory type DB
|
||||
##
|
||||
|
@ -71,19 +75,23 @@ proc newCoreDbRef*(
|
|||
## `CoreDbRef.init()` because of compiler coughing.
|
||||
##
|
||||
when dbType == LegacyDbMemory:
|
||||
newLegacyMemoryCoreDbRef()
|
||||
let res = newLegacyMemoryCoreDbRef()
|
||||
|
||||
elif dbType == AristoDbMemory:
|
||||
newAristoMemoryCoreDbRef()
|
||||
let res = newAristoMemoryCoreDbRef()
|
||||
|
||||
elif dbType == AristoDbVoid:
|
||||
newAristoVoidCoreDbRef()
|
||||
let res = newAristoVoidCoreDbRef()
|
||||
|
||||
else:
|
||||
{.error: "Unsupported constructor " & $dbType & ".newCoreDbRef()".}
|
||||
|
||||
res.chainId = chainId
|
||||
res
|
||||
|
||||
proc newCoreDbRef*(
|
||||
dbType: static[CoreDbType]; # Database type symbol
|
||||
chainId: ChainId;
|
||||
qidLayout: QidLayoutRef; # `Aristo` only
|
||||
): CoreDbRef =
|
||||
## Constructor for volatile/memory type DB
|
||||
|
@ -92,15 +100,18 @@ proc newCoreDbRef*(
|
|||
## `CoreDbRef.init()` because of compiler coughing.
|
||||
##
|
||||
when dbType == AristoDbMemory:
|
||||
newAristoMemoryCoreDbRef(DefaultQidLayoutRef)
|
||||
let res = newAristoMemoryCoreDbRef(DefaultQidLayoutRef)
|
||||
|
||||
elif dbType == AristoDbVoid:
|
||||
newAristoVoidCoreDbRef()
|
||||
let res = newAristoVoidCoreDbRef()
|
||||
|
||||
else:
|
||||
{.error: "Unsupported constructor " & $dbType & ".newCoreDbRef()" &
|
||||
" with qidLayout argument".}
|
||||
|
||||
res.chainId = chainId
|
||||
res
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -35,23 +35,28 @@ export
|
|||
proc newCoreDbRef*(
|
||||
dbType: static[CoreDbType]; # Database type symbol
|
||||
path: string; # Storage path for database
|
||||
chainId: ChainId;
|
||||
): CoreDbRef =
|
||||
## Constructor for persistent type DB
|
||||
##
|
||||
## Note: Using legacy notation `newCoreDbRef()` rather than
|
||||
## `CoreDbRef.init()` because of compiler coughing.
|
||||
when dbType == LegacyDbPersistent:
|
||||
newLegacyPersistentCoreDbRef path
|
||||
let res = newLegacyPersistentCoreDbRef path
|
||||
|
||||
elif dbType == AristoDbRocks:
|
||||
newAristoRocksDbCoreDbRef path
|
||||
let res = newAristoRocksDbCoreDbRef path
|
||||
|
||||
else:
|
||||
{.error: "Unsupported dbType for persistent newCoreDbRef()".}
|
||||
|
||||
res.chainId = chainId
|
||||
res
|
||||
|
||||
proc newCoreDbRef*(
|
||||
dbType: static[CoreDbType]; # Database type symbol
|
||||
path: string; # Storage path for database
|
||||
chainId: ChainId;
|
||||
qidLayout: QidLayoutRef; # Optional for `Aristo`, ignored by others
|
||||
): CoreDbRef =
|
||||
## Constructor for persistent type DB
|
||||
|
@ -59,10 +64,13 @@ proc newCoreDbRef*(
|
|||
## Note: Using legacy notation `newCoreDbRef()` rather than
|
||||
## `CoreDbRef.init()` because of compiler coughing.
|
||||
when dbType == AristoDbRocks:
|
||||
newAristoRocksDbCoreDbRef(path, qlr)
|
||||
let res = newAristoRocksDbCoreDbRef(path, qlr)
|
||||
|
||||
else:
|
||||
{.error: "Unsupported dbType for persistent newCoreDbRef()" &
|
||||
" with qidLayout argument".}
|
||||
|
||||
res.chainId = chainId
|
||||
res
|
||||
|
||||
# End
|
||||
|
|
|
@ -49,6 +49,7 @@ type
|
|||
db: ReadOnlyStateDB
|
||||
|
||||
TxNode = ref object of Node
|
||||
chainId: ChainId
|
||||
tx: Transaction
|
||||
index: int
|
||||
blockNumber: common.BlockNumber
|
||||
|
@ -113,6 +114,7 @@ proc txNode(ctx: GraphqlContextRef, tx: Transaction, index: int, blockNumber: co
|
|||
kind: nkMap,
|
||||
typeName: ctx.ids[ethTransaction],
|
||||
pos: Pos(),
|
||||
chainId: ctx.com.chainId,
|
||||
tx: tx,
|
||||
index: index,
|
||||
blockNumber: blockNumber,
|
||||
|
@ -282,7 +284,8 @@ proc getTxs(ctx: GraphqlContextRef, header: common.BlockHeader): RespResult =
|
|||
var list = respList()
|
||||
var index = 0
|
||||
for n in getBlockTransactionData(ctx.chainDB, header.txRoot):
|
||||
let tx = decodeTx(n)
|
||||
let tx = Transaction.fromBytes(n, ctx.com.chainId).valueOr:
|
||||
raise (ref MalformedRlpError)(msg: "Invalid transaction in block")
|
||||
list.add txNode(ctx, tx, index, header.blockNumber, header.fee)
|
||||
inc index
|
||||
|
||||
|
@ -614,7 +617,7 @@ proc txHash(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
|||
|
||||
proc txNonce(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
longNode(tx.tx.nonce)
|
||||
longNode(tx.tx.payload.nonce)
|
||||
|
||||
proc txIndex(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
|
@ -653,61 +656,65 @@ proc txTo(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
|||
if hres.isErr:
|
||||
return hres
|
||||
let h = HeaderNode(hres.get())
|
||||
ctx.accountNode(h.header, tx.tx.to.get())
|
||||
ctx.accountNode(h.header, tx.tx.payload.to.get())
|
||||
|
||||
proc txValue(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
bigIntNode(tx.tx.value)
|
||||
bigIntNode(tx.tx.payload.value)
|
||||
|
||||
proc txGasPrice(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
if tx.tx.txType == TxEip1559:
|
||||
if tx.tx.payload.max_priority_fee_per_gas.isSome:
|
||||
if tx.baseFee.isNone:
|
||||
return bigIntNode(tx.tx.gasPrice)
|
||||
return bigIntNode(tx.tx.payload.max_fee_per_gas)
|
||||
|
||||
let baseFee = tx.baseFee.get().truncate(GasInt)
|
||||
let priorityFee = min(tx.tx.maxPriorityFee, tx.tx.maxFee - baseFee)
|
||||
let priorityFee = min(
|
||||
tx.tx.payload.max_priority_fee_per_gas.unsafeGet.truncate(int64),
|
||||
tx.tx.payload.max_fee_per_gas.truncate(int64) - baseFee)
|
||||
bigIntNode(priorityFee + baseFee)
|
||||
else:
|
||||
bigIntNode(tx.tx.gasPrice)
|
||||
bigIntNode(tx.tx.payload.max_fee_per_gas)
|
||||
|
||||
proc txMaxFeePerGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
if tx.tx.txType == TxEip1559:
|
||||
bigIntNode(tx.tx.maxFee)
|
||||
if tx.tx.payload.max_priority_fee_per_gas.isSome:
|
||||
bigIntNode(tx.tx.payload.max_fee_per_gas)
|
||||
else:
|
||||
ok(respNull())
|
||||
|
||||
proc txMaxPriorityFeePerGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
if tx.tx.txType == TxEip1559:
|
||||
bigIntNode(tx.tx.maxPriorityFee)
|
||||
if tx.tx.payload.max_priority_fee_per_gas.isSome:
|
||||
bigIntNode(tx.tx.payload.max_priority_fee_per_gas.unsafeGet)
|
||||
else:
|
||||
ok(respNull())
|
||||
|
||||
proc txEffectiveGasPrice(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
if tx.baseFee.isNone:
|
||||
return bigIntNode(tx.tx.gasPrice)
|
||||
return bigIntNode(tx.tx.payload.max_fee_per_gas.truncate(int64))
|
||||
|
||||
let baseFee = tx.baseFee.get().truncate(GasInt)
|
||||
let priorityFee = min(tx.tx.maxPriorityFee, tx.tx.maxFee - baseFee)
|
||||
let priorityFee = min(
|
||||
tx.tx.payload.max_priority_fee_per_gas.unsafeGet.truncate(int64),
|
||||
tx.tx.payload.max_fee_per_gas.truncate(int64) - baseFee)
|
||||
bigIntNode(priorityFee + baseFee)
|
||||
|
||||
proc txChainId(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
if tx.tx.txType == TxLegacy:
|
||||
if tx.tx.payload.tx_type.isNone:
|
||||
ok(respNull())
|
||||
else:
|
||||
longNode(tx.tx.chainId.uint64)
|
||||
longNode(tx.chainId.uint64)
|
||||
|
||||
proc txGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
longNode(tx.tx.gasLimit)
|
||||
longNode(tx.tx.payload.gas)
|
||||
|
||||
proc txInputData(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
resp(tx.tx.payload)
|
||||
resp(distinctBase(tx.tx.payload.input))
|
||||
|
||||
proc txBlock(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let ctx = GraphqlContextRef(ud)
|
||||
|
@ -743,7 +750,7 @@ proc txCreatedContract(ud: RootRef, params: Args, parent: Node): RespResult {.ap
|
|||
if hres.isErr:
|
||||
return hres
|
||||
let h = HeaderNode(hres.get())
|
||||
let contractAddress = generateAddress(sender, tx.tx.nonce)
|
||||
let contractAddress = generateAddress(sender, tx.tx.payload.nonce)
|
||||
ctx.accountNode(h.header, contractAddress)
|
||||
|
||||
proc txLogs(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
|
@ -756,46 +763,55 @@ proc txLogs(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
|||
|
||||
proc txR(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
bigIntNode(tx.tx.R)
|
||||
bigIntNode(ecdsa_unpack_signature(tx.tx.signature.ecdsa_signature).r)
|
||||
|
||||
proc txS(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
bigIntNode(tx.tx.S)
|
||||
bigIntNode(ecdsa_unpack_signature(tx.tx.signature.ecdsa_signature).s)
|
||||
|
||||
proc txV(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
bigIntNode(tx.tx.V)
|
||||
let
|
||||
tx = TxNode(parent)
|
||||
yParity = ecdsa_unpack_signature(tx.tx.signature.ecdsa_signature).y_parity
|
||||
v =
|
||||
if tx.tx.payload.tx_type.isNone:
|
||||
if yParity: 28.u256 else: 27.u256
|
||||
elif tx.tx.payload.tx_type == Opt.some TxLegacy:
|
||||
distinctBase(tx.chainId).u256 * 2 + (if yParity: 36.u256 else: 35.u256)
|
||||
else:
|
||||
if yParity: UInt256.one else: UInt256.zero
|
||||
bigIntNode(v)
|
||||
|
||||
proc txType(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
let typ = resp(ord(tx.tx.txType))
|
||||
let typ = resp(ord(tx.tx.payload.tx_type.get(TxLegacy)))
|
||||
ok(typ)
|
||||
|
||||
proc txAccessList(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let ctx = GraphqlContextRef(ud)
|
||||
let tx = TxNode(parent)
|
||||
if tx.tx.txType == TxLegacy:
|
||||
if tx.tx.payload.access_list.isNone:
|
||||
ok(respNull())
|
||||
else:
|
||||
var list = respList()
|
||||
for x in tx.tx.accessList:
|
||||
for x in tx.tx.payload.access_list.unsafeGet:
|
||||
list.add aclNode(ctx, x)
|
||||
ok(list)
|
||||
|
||||
proc txMaxFeePerBlobGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
if tx.tx.txType < TxEIP4844:
|
||||
if tx.tx.payload.max_fee_per_blob_gas.isNone:
|
||||
ok(respNull())
|
||||
else:
|
||||
longNode(tx.tx.maxFeePerBlobGas)
|
||||
longNode(tx.tx.payload.max_fee_per_blob_gas.unsafeGet)
|
||||
|
||||
proc txVersionedHashes(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
|
||||
let tx = TxNode(parent)
|
||||
if tx.tx.txType < TxEIP4844:
|
||||
if tx.tx.payload.blob_versioned_hashes.isNone:
|
||||
ok(respNull())
|
||||
else:
|
||||
var list = respList()
|
||||
for hs in tx.tx.versionedHashes:
|
||||
for hs in tx.tx.payload.blob_versioned_hashes.unsafeGet:
|
||||
list.add resp("0x" & hs.data.toHex)
|
||||
ok(list)
|
||||
|
||||
|
@ -1364,8 +1380,10 @@ proc sendRawTransaction(ud: RootRef, params: Args, parent: Node): RespResult {.a
|
|||
let ctx = GraphqlContextRef(ud)
|
||||
try:
|
||||
let data = hexToSeqByte(params[0].val.stringVal)
|
||||
let tx = decodePooledTx(data) # we want to know if it is a valid tx blob
|
||||
let txHash = rlpHash(tx)
|
||||
# we want to know if it is a valid tx blob
|
||||
let tx = PooledTransaction.fromBytes(data, ctx.com.chainId).valueOr:
|
||||
return err("Invalid `PooledTransaction`")
|
||||
let txHash = tx.tx.compute_tx_hash(ctx.com.chainId)
|
||||
|
||||
ctx.txPool.add(tx)
|
||||
|
||||
|
|
|
@ -283,8 +283,12 @@ proc start(nimbus: NimbusNode, conf: NimbusConf) =
|
|||
let coreDB =
|
||||
# Resolve statically for database type
|
||||
case conf.chainDbMode:
|
||||
of Prune,Archive: LegacyDbPersistent.newCoreDbRef(string conf.dataDir)
|
||||
of Aristo: AristoDbRocks.newCoreDbRef(string conf.dataDir)
|
||||
of Prune,Archive:
|
||||
LegacyDbPersistent.newCoreDbRef(
|
||||
string conf.dataDir, conf.networkParams.config.chainId)
|
||||
of Aristo:
|
||||
AristoDbRocks.newCoreDbRef(
|
||||
string conf.dataDir, conf.networkParams.config.chainId)
|
||||
let com = CommonRef.new(
|
||||
db = coreDB,
|
||||
pruneTrie = (conf.chainDbMode == ChainDbMode.Prune),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# those terms.
|
||||
|
||||
import
|
||||
std/[hashes, algorithm, strutils],
|
||||
std/[hashes, algorithm, strutils, typetraits],
|
||||
eth/eip1559,
|
||||
stew/keyed_queue,
|
||||
stew/endians2,
|
||||
|
@ -125,11 +125,11 @@ proc processBlock(oracle: Oracle, bc: BlockContent, percentiles: openArray[float
|
|||
|
||||
for i, tx in bc.txs:
|
||||
let
|
||||
reward = tx.effectiveGasTip(bc.header.fee)
|
||||
reward = tx.effectiveGasTip(bc.header.fee.get(UInt256.zero))
|
||||
gasUsed = bc.receipts[i].cumulativeGasUsed - prevUsed
|
||||
sorter[i] = TxGasAndReward(
|
||||
gasUsed: gasUsed.uint64,
|
||||
reward: reward.u256
|
||||
reward: distinctBase(reward).u256
|
||||
)
|
||||
prevUsed = bc.receipts[i].cumulativeGasUsed
|
||||
|
||||
|
|
|
@ -259,9 +259,9 @@ proc setupEthRpc*(
|
|||
|
||||
let
|
||||
accDB = stateDBFromTag(blockId("latest"))
|
||||
tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1)
|
||||
eip155 = com.isEIP155(com.syncCurrent)
|
||||
signedTx = signTransaction(tx, acc.privateKey, com.chainId, eip155)
|
||||
tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1, eip155)
|
||||
signedTx = signTransaction(tx, acc.privateKey, com.chainId)
|
||||
result = rlp.encode(signedTx)
|
||||
|
||||
server.rpc("eth_sendTransaction") do(data: TransactionArgs) -> Web3Hash:
|
||||
|
@ -279,31 +279,40 @@ proc setupEthRpc*(
|
|||
|
||||
let
|
||||
accDB = stateDBFromTag(blockId("latest"))
|
||||
tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1)
|
||||
eip155 = com.isEIP155(com.syncCurrent)
|
||||
signedTx = signTransaction(tx, acc.privateKey, com.chainId, eip155)
|
||||
tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1, eip155)
|
||||
signedTx = signTransaction(tx, acc.privateKey, com.chainId)
|
||||
networkPayload =
|
||||
if signedTx.txType == TxEip4844:
|
||||
if signedTx.payload.blob_versioned_hashes.isSome:
|
||||
template vhs: untyped =
|
||||
signedTx.payload.blob_versioned_hashes.unsafeGet
|
||||
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:
|
||||
if data.blobs.get.len != vhs.len:
|
||||
raise newException(ValueError, "Incorrect number of blobs")
|
||||
if data.commitments.get.len != signedTx.versionedHashes.len:
|
||||
if data.commitments.get.len != vhs.len:
|
||||
raise newException(ValueError, "Incorrect number of commitments")
|
||||
if data.proofs.get.len != signedTx.versionedHashes.len:
|
||||
if data.proofs.get.len != vhs.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))
|
||||
|
||||
Opt.some NetworkPayload(
|
||||
blobs:
|
||||
List[NetworkBlob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(data.blobs.get.mapIt it.NetworkBlob),
|
||||
commitments:
|
||||
List[eth_types.KzgCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(data.commitments.get.mapIt eth_types.KzgCommitment(it)),
|
||||
proofs:
|
||||
List[eth_types.KzgProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
.init(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)
|
||||
Opt.none NetworkPayload
|
||||
pooledTx = PooledTransaction(tx: signedTx, blob_data: networkPayload)
|
||||
|
||||
txPool.add(pooledTx)
|
||||
result = rlpHash(signedTx).w3Hash
|
||||
result = signedTx.compute_tx_hash(com.chainId).w3Hash
|
||||
|
||||
server.rpc("eth_sendRawTransaction") do(txBytes: seq[byte]) -> Web3Hash:
|
||||
## Creates new message call transaction or a contract creation for signed transactions.
|
||||
|
@ -312,8 +321,9 @@ 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
|
||||
pooledTx = decodePooledTx(txBytes)
|
||||
txHash = rlpHash(pooledTx)
|
||||
pooledTx = PooledTransaction.fromBytes(txBytes, com.chainId).valueOr:
|
||||
raise (ref MalformedRlpError)(msg: "Invalid `PooledTransaction`")
|
||||
txHash = pooledTx.tx.compute_tx_hash(com.chainId)
|
||||
|
||||
txPool.add(pooledTx)
|
||||
let res = txPool.inPoolAndReason(txHash)
|
||||
|
@ -381,7 +391,7 @@ proc setupEthRpc*(
|
|||
let txHash = data.ethHash()
|
||||
let res = txPool.getItem(txHash)
|
||||
if res.isOk:
|
||||
return populateTransactionObject(res.get().tx)
|
||||
return populateTransactionObject(res.get().tx, com.chainId)
|
||||
|
||||
let txDetails = chainDB.getTransactionKey(txHash)
|
||||
if txDetails.index < 0:
|
||||
|
@ -390,7 +400,8 @@ proc setupEthRpc*(
|
|||
let header = chainDB.getBlockHeader(txDetails.blockNumber)
|
||||
var tx: Transaction
|
||||
if chainDB.getTransaction(header.txRoot, txDetails.index, tx):
|
||||
result = populateTransactionObject(tx, some(header), some(txDetails.index))
|
||||
result = populateTransactionObject(
|
||||
tx, com.chainId, some(header), some(txDetails.index))
|
||||
|
||||
server.rpc("eth_getTransactionByBlockHashAndIndex") do(data: Web3Hash, quantity: Web3Quantity) -> TransactionObject:
|
||||
## Returns information about a transaction by block hash and transaction index position.
|
||||
|
@ -405,7 +416,8 @@ proc setupEthRpc*(
|
|||
|
||||
var tx: Transaction
|
||||
if chainDB.getTransaction(header.txRoot, index, tx):
|
||||
result = populateTransactionObject(tx, some(header), some(index))
|
||||
result = populateTransactionObject(
|
||||
tx, com.chainId, some(header), some(index))
|
||||
else:
|
||||
result = nil
|
||||
|
||||
|
@ -420,7 +432,8 @@ proc setupEthRpc*(
|
|||
|
||||
var tx: Transaction
|
||||
if chainDB.getTransaction(header.txRoot, index, tx):
|
||||
result = populateTransactionObject(tx, some(header), some(index))
|
||||
result = populateTransactionObject(
|
||||
tx, com.chainId, some(header), some(index))
|
||||
else:
|
||||
result = nil
|
||||
|
||||
|
|
|
@ -62,9 +62,8 @@ proc calculateMedianGasPrice*(chain: CoreDbRef): GasInt
|
|||
{.gcsafe, raises: [CatchableError].} =
|
||||
var prices = newSeqOfCap[GasInt](64)
|
||||
let header = chain.getCanonicalHead()
|
||||
for encodedTx in chain.getBlockTransactionData(header.txRoot):
|
||||
let tx = decodeTx(encodedTx)
|
||||
prices.add(tx.gasPrice)
|
||||
for tx in chain.getBlockTransactions(header):
|
||||
prices.add(tx.payload.max_fee_per_gas.truncate(int64))
|
||||
|
||||
if prices.len > 0:
|
||||
sort(prices)
|
||||
|
@ -79,32 +78,48 @@ proc calculateMedianGasPrice*(chain: CoreDbRef): GasInt
|
|||
const minGasPrice = 30_000_000_000.GasInt
|
||||
result = max(result, minGasPrice)
|
||||
|
||||
proc unsignedTx*(tx: TransactionArgs, chain: CoreDbRef, defaultNonce: AccountNonce): Transaction
|
||||
{.gcsafe, raises: [CatchableError].} =
|
||||
if tx.to.isSome:
|
||||
result.to = some(ethAddr(tx.to.get))
|
||||
|
||||
if tx.gas.isSome:
|
||||
result.gasLimit = tx.gas.get.GasInt
|
||||
else:
|
||||
result.gasLimit = 90000.GasInt
|
||||
|
||||
if tx.gasPrice.isSome:
|
||||
result.gasPrice = tx.gasPrice.get.GasInt
|
||||
else:
|
||||
result.gasPrice = calculateMedianGasPrice(chain)
|
||||
|
||||
if tx.value.isSome:
|
||||
result.value = tx.value.get
|
||||
else:
|
||||
result.value = 0.u256
|
||||
|
||||
if tx.nonce.isSome:
|
||||
result.nonce = tx.nonce.get.AccountNonce
|
||||
else:
|
||||
result.nonce = defaultNonce
|
||||
|
||||
result.payload = tx.payload
|
||||
proc unsignedTx*(
|
||||
tx: TransactionArgs,
|
||||
chain: CoreDbRef,
|
||||
defaultNonce: AccountNonce,
|
||||
eip155 = true
|
||||
): TransactionPayload {.gcsafe, raises: [CatchableError].} =
|
||||
TransactionPayload(
|
||||
nonce:
|
||||
if tx.nonce.isSome:
|
||||
tx.nonce.get.AccountNonce
|
||||
else:
|
||||
defaultNonce,
|
||||
max_fee_per_gas:
|
||||
if tx.gasPrice.isSome:
|
||||
distinctBase(tx.gasPrice.get).u256
|
||||
else:
|
||||
calculateMedianGasPrice(chain).uint64.u256,
|
||||
gas:
|
||||
if tx.gas.isSome:
|
||||
distinctBase(tx.gas.get)
|
||||
else:
|
||||
90000,
|
||||
to:
|
||||
if tx.to.isSome:
|
||||
Opt.some ethAddr(tx.to.get)
|
||||
else:
|
||||
Opt.none(EthAddress),
|
||||
value:
|
||||
if tx.value.isSome:
|
||||
tx.value.get
|
||||
else:
|
||||
UInt256.zero,
|
||||
input:
|
||||
if tx.payload.len > MAX_CALL_DATA_SIZE:
|
||||
raise (ref ValueError)(msg: "tx.payload exceeds MAX_CALL_DATA_SIZE")
|
||||
else:
|
||||
List[byte, Limit MAX_CALL_DATA_SIZE].init(tx.payload),
|
||||
tx_type:
|
||||
if eip155:
|
||||
Opt.some TxLegacy
|
||||
else:
|
||||
Opt.none TxType)
|
||||
|
||||
proc toWd(wd: Withdrawal): WithdrawalObject =
|
||||
WithdrawalObject(
|
||||
|
@ -120,39 +135,61 @@ proc toWdList(list: openArray[Withdrawal]): seq[WithdrawalObject] =
|
|||
result.add toWd(x)
|
||||
|
||||
proc populateTransactionObject*(tx: Transaction,
|
||||
chainId: ChainId,
|
||||
optionalHeader: Option[BlockHeader] = none(BlockHeader),
|
||||
txIndex: Option[int] = none(int)): TransactionObject
|
||||
{.gcsafe, raises: [ValidationError].} =
|
||||
result = TransactionObject()
|
||||
result.`type` = some w3Qty(tx.txType.ord)
|
||||
if optionalHeader.isSome:
|
||||
let header = optionalHeader.get
|
||||
result.blockHash = some(w3Hash header.hash)
|
||||
result.blockNumber = some(w3BlockNumber(header.blockNumber))
|
||||
let anyTx = AnyTransaction.fromOneOfBase(tx).valueOr:
|
||||
raiseAssert "Cannot convert invalid `Transaction`: " & $tx
|
||||
withTxVariant(anyTx):
|
||||
result = TransactionObject()
|
||||
when txKind >= TransactionKind.Legacy:
|
||||
result.`type` = options.some w3Qty(txVariant.payload.tx_type.ord)
|
||||
if optionalHeader.isSome:
|
||||
let header = optionalHeader.get
|
||||
result.blockHash = some(w3Hash header.hash)
|
||||
result.blockNumber = some(w3BlockNumber(header.blockNumber))
|
||||
|
||||
result.`from` = w3Addr tx.getSender()
|
||||
result.gas = w3Qty(tx.gasLimit)
|
||||
result.gasPrice = w3Qty(tx.gasPrice)
|
||||
result.hash = w3Hash tx.rlpHash
|
||||
result.input = tx.payload
|
||||
result.nonce = w3Qty(tx.nonce)
|
||||
result.to = some(w3Addr tx.destination)
|
||||
if txIndex.isSome:
|
||||
result.transactionIndex = some(w3Qty(txIndex.get))
|
||||
result.value = tx.value
|
||||
result.v = w3Qty(tx.V)
|
||||
result.r = u256(tx.R)
|
||||
result.s = u256(tx.S)
|
||||
result.maxFeePerGas = some w3Qty(tx.maxFee)
|
||||
result.maxPriorityFeePerGas = some w3Qty(tx.maxPriorityFee)
|
||||
result.`from` = w3Addr txVariant.signature.from_address
|
||||
result.gas = w3Qty(txVariant.payload.gas)
|
||||
result.gasPrice = w3Qty(txVariant.payload.max_fee_per_gas)
|
||||
result.hash = w3Hash txVariant.payload.compute_sig_hash(chainId)
|
||||
result.input = distinctBase(txVariant.payload.input)
|
||||
result.nonce = w3Qty(txVariant.payload.nonce)
|
||||
when txKind == TransactionKind.Eip4844:
|
||||
result.to = some(w3Addr txVariant.payload.to)
|
||||
else:
|
||||
if txVariant.payload.to.isSome:
|
||||
result.to = some(w3Addr txVariant.payload.to.unsafeGet)
|
||||
if txIndex.isSome:
|
||||
result.transactionIndex = some(w3Qty(txIndex.get))
|
||||
result.value = txVariant.payload.value
|
||||
let
|
||||
(yParity, r, s) = ecdsa_unpack_signature(
|
||||
txVariant.signature.ecdsa_signature)
|
||||
v =
|
||||
when txKind == TransactionKind.Replayable:
|
||||
if yParity: 28.u256 else: 27.u256
|
||||
elif txKind == TransactionKind.Legacy:
|
||||
distinctBase(chainId).u256 * 2 + (if yParity: 36.u256 else: 35.u256)
|
||||
else:
|
||||
if yParity: UInt256.one else: UInt256.zero
|
||||
result.v = w3Qty(v)
|
||||
result.r = u256(r)
|
||||
result.s = u256(s)
|
||||
when txKind >= TransactionKind.Eip1559:
|
||||
result.maxFeePerGas = some w3Qty(txVariant.payload.max_fee_per_gas)
|
||||
result.maxPriorityFeePerGas = some w3Qty(
|
||||
txVariant.payload.max_priority_fee_per_gas)
|
||||
|
||||
if tx.txType >= TxEip2930:
|
||||
result.chainId = some(Web3Quantity(tx.chainId))
|
||||
result.accessList = some(w3AccessList(tx.accessList))
|
||||
when txKind >= TransactionKind.Eip2930:
|
||||
result.chainId = some(Web3Quantity(chainId))
|
||||
result.accessList = some(w3AccessList(txVariant.payload.access_list))
|
||||
|
||||
if tx.txType >= TxEIP4844:
|
||||
result.maxFeePerBlobGas = some(tx.maxFeePerBlobGas)
|
||||
result.blobVersionedHashes = some(w3Hashes tx.versionedHashes)
|
||||
when txKind == TransactionKind.Eip4844:
|
||||
result.maxFeePerBlobGas = some(txVariant.payload.max_fee_per_blob_gas)
|
||||
result.blobVersionedHashes =
|
||||
some(w3Hashes distinctBase(txVariant.payload.blob_versioned_hashes))
|
||||
|
||||
proc populateBlockObject*(header: BlockHeader, chain: CoreDbRef, fullTx: bool, isUncle = false): BlockObject
|
||||
{.gcsafe, raises: [CatchableError].} =
|
||||
|
@ -191,7 +228,8 @@ proc populateBlockObject*(header: BlockHeader, chain: CoreDbRef, fullTx: bool, i
|
|||
if fullTx:
|
||||
var i = 0
|
||||
for tx in chain.getBlockTransactions(header):
|
||||
result.transactions.add txOrHash(populateTransactionObject(tx, some(header), some(i)))
|
||||
result.transactions.add txOrHash(
|
||||
populateTransactionObject(tx, chain.chainId, some(header), some(i)))
|
||||
inc i
|
||||
else:
|
||||
for x in chain.getBlockTransactionHashes(header):
|
||||
|
@ -227,7 +265,7 @@ proc populateReceipt*(receipt: Receipt, gasUsed: GasInt, tx: Transaction,
|
|||
if tx.contractCreation:
|
||||
var sender: EthAddress
|
||||
if tx.getSender(sender):
|
||||
let contractAddress = generateAddress(sender, tx.nonce)
|
||||
let contractAddress = generateAddress(sender, tx.payload.nonce)
|
||||
result.contractAddress = some(w3Addr contractAddress)
|
||||
|
||||
for log in receipt.logs:
|
||||
|
@ -263,11 +301,15 @@ proc populateReceipt*(receipt: Receipt, gasUsed: GasInt, tx: Transaction,
|
|||
# 1 = success, 0 = failure.
|
||||
result.status = some(w3Qty(receipt.status.uint64))
|
||||
|
||||
let normTx = eip1559TxNormalization(tx, header.baseFee.truncate(GasInt))
|
||||
result.effectiveGasPrice = w3Qty(normTx.gasPrice)
|
||||
result.effectiveGasPrice = w3Qty(
|
||||
(header.baseFee + min(
|
||||
tx.payload.max_priority_fee_per_gas.get(tx.payload.max_fee_per_gas),
|
||||
tx.payload.max_fee_per_gas - header.baseFee)).truncate(int64))
|
||||
|
||||
if tx.txType == TxEip4844:
|
||||
result.blobGasUsed = some(w3Qty(tx.versionedHashes.len.uint64 * GAS_PER_BLOB.uint64))
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
result.blobGasUsed = some(w3Qty(
|
||||
tx.payload.blob_versioned_hashes.unsafeGet.len.uint64 *
|
||||
GAS_PER_BLOB.uint64))
|
||||
result.blobGasPrice = some(getBlobBaseFee(header.excessBlobGas.get(0'u64)))
|
||||
|
||||
proc createAccessList*(header: BlockHeader,
|
||||
|
|
|
@ -11,9 +11,10 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[tables, times, hashes, sets],
|
||||
std/[tables, times, hashes, sets, typetraits],
|
||||
chronicles, chronos,
|
||||
stew/endians2,
|
||||
eth/common/transaction,
|
||||
eth/p2p,
|
||||
eth/p2p/peer_pool,
|
||||
".."/[types, protocol],
|
||||
|
@ -258,7 +259,7 @@ proc sendTransactions(ctx: EthWireRef,
|
|||
# This is used to avoid re-sending along pooledTxHashes
|
||||
# announcements/re-broadcasts
|
||||
ctx.addToKnownByPeer(txHashes, peer)
|
||||
await peer.transactions(txs)
|
||||
await peer.transactions(RawRlp txs.toBytes(ctx.chainId))
|
||||
|
||||
except TransportError:
|
||||
debug "Transport got closed during sendTransactions"
|
||||
|
@ -276,16 +277,19 @@ proc fetchTransactions(ctx: EthWireRef, reqHashes: seq[Hash256], peer: Peer): Fu
|
|||
error "not able to get pooled transactions"
|
||||
return
|
||||
|
||||
let txs = res.get()
|
||||
let txs = seq[PooledTransaction].fromBytes(
|
||||
distinctBase(res.get().transactions), ctx.chainId).valueOr:
|
||||
error "invalid pooled transactions"
|
||||
return
|
||||
debug "fetchTx: received requested txs",
|
||||
number = txs.transactions.len
|
||||
number = txs.len
|
||||
|
||||
# Remove from pending list regardless if tx is in result
|
||||
for tx in txs.transactions:
|
||||
let txHash = rlpHash(tx)
|
||||
for tx in txs:
|
||||
let txHash = tx.tx.compute_tx_hash(ctx.chainId)
|
||||
ctx.pending.excl txHash
|
||||
|
||||
ctx.txPool.add(txs.transactions)
|
||||
ctx.txPool.add(txs)
|
||||
|
||||
except TransportError:
|
||||
debug "Transport got closed during fetchTransactions"
|
||||
|
@ -353,6 +357,7 @@ proc new*(_: type EthWireRef,
|
|||
txPool: TxPoolRef,
|
||||
peerPool: PeerPool): EthWireRef =
|
||||
let ctx = EthWireRef(
|
||||
chainId: chain.com.chainId,
|
||||
db: chain.db,
|
||||
chain: chain,
|
||||
txPool: txPool,
|
||||
|
@ -523,7 +528,7 @@ method handleAnnouncedTxs*(ctx: EthWireRef,
|
|||
|
||||
ctx.addToKnownByPeer(txHashes, peer)
|
||||
for tx in txs:
|
||||
if tx.versionedHashes.len > 0:
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
# EIP-4844 blobs are not persisted and cannot be broadcasted
|
||||
continue
|
||||
ctx.txPool.add PooledTransaction(tx: tx)
|
||||
|
@ -533,7 +538,8 @@ method handleAnnouncedTxs*(ctx: EthWireRef,
|
|||
for i, txHash in txHashes:
|
||||
# Nodes must not automatically broadcast blob transactions to
|
||||
# their peers. per EIP-4844 spec
|
||||
if ctx.txPool.inPoolAndOk(txHash) and txs[i].txType != TxEip4844:
|
||||
if ctx.txPool.inPoolAndOk(txHash) and
|
||||
txs[i].payload.blob_versioned_hashes.isNone:
|
||||
newTxHashes.add txHash
|
||||
validTxs.add txs[i]
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ type
|
|||
forkNext*: uint64 # The RLP encoding must be variable-length
|
||||
|
||||
EthWireBase* = ref object of RootRef
|
||||
chainId*: ChainId
|
||||
|
||||
EthState* = object
|
||||
totalDifficulty*: DifficultyInt
|
||||
|
|
|
@ -263,11 +263,11 @@ p2pProtocol eth66(version = ethVersion,
|
|||
trace trEthSendReplying & "EMPTY PooledTransactions (0x0a)", peer,
|
||||
sent=0, requested=txHashes.len
|
||||
|
||||
await response.send(txs.get)
|
||||
await response.send(RawRlp(txs.get.toBytes(ctx.chainId)))
|
||||
|
||||
# User message 0x0a: PooledTransactions.
|
||||
proc pooledTransactions(
|
||||
peer: Peer, transactions: openArray[PooledTransaction])
|
||||
peer: Peer, transactions: RawRlp)
|
||||
|
||||
nextId 0x0d
|
||||
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
## `eth/67 <https://github.com/ethereum/devp2p/blob/master/caps/eth.md>`_
|
||||
|
||||
import
|
||||
std/typetraits,
|
||||
stint,
|
||||
chronicles,
|
||||
chronos,
|
||||
eth/[common, p2p, p2p/private/p2p_types],
|
||||
eth/[common, common/transaction, p2p, p2p/private/p2p_types],
|
||||
stew/byteutils,
|
||||
./trace_config,
|
||||
./eth/eth_types,
|
||||
|
@ -161,12 +162,17 @@ p2pProtocol eth67(version = ethVersion,
|
|||
handleHandlerError(res)
|
||||
|
||||
# User message 0x02: Transactions.
|
||||
proc transactions(peer: Peer, transactions: openArray[Transaction]) =
|
||||
proc transactions(peer: Peer, encodedTransactions: RawRlp) =
|
||||
let
|
||||
ctx = peer.networkState()
|
||||
transactions = seq[Transaction].fromBytes(
|
||||
distinctBase(encodedTransactions), ctx.chainId).valueOr:
|
||||
raise (ref MalformedRlpError)(msg: "Invalid transaction in message")
|
||||
|
||||
when trEthTraceGossipOk:
|
||||
trace trEthRecvReceived & "Transactions (0x02)", peer,
|
||||
transactions=transactions.len
|
||||
|
||||
let ctx = peer.networkState()
|
||||
let res = ctx.handleAnnouncedTxs(peer, transactions)
|
||||
handleHandlerError(res)
|
||||
|
||||
|
@ -264,11 +270,11 @@ p2pProtocol eth67(version = ethVersion,
|
|||
trace trEthSendReplying & "EMPTY PooledTransactions (0x0a)", peer,
|
||||
sent=0, requested=txHashes.len
|
||||
|
||||
await response.send(txs.get)
|
||||
await response.send(RawRlp(txs.get.toBytes(ctx.chainId)))
|
||||
|
||||
# User message 0x0a: PooledTransactions.
|
||||
proc pooledTransactions(
|
||||
peer: Peer, transactions: openArray[PooledTransaction])
|
||||
peer: Peer, transactions: RawRlp)
|
||||
|
||||
# User message 0x0d: GetNodeData -- removed, was so 66ish
|
||||
# User message 0x0e: NodeData -- removed, was so 66ish
|
||||
|
|
|
@ -267,11 +267,11 @@ p2pProtocol eth68(version = ethVersion,
|
|||
trace trEthSendReplying & "EMPTY PooledTransactions (0x0a)", peer,
|
||||
sent=0, requested=txHashes.len
|
||||
|
||||
await response.send(txs.get)
|
||||
await response.send(RawRlp(txs.get.toBytes(ctx.chainId)))
|
||||
|
||||
# User message 0x0a: PooledTransactions.
|
||||
proc pooledTransactions(
|
||||
peer: Peer, transactions: openArray[PooledTransaction])
|
||||
peer: Peer, transactions: RawRlp)
|
||||
|
||||
# User message 0x0d: GetNodeData -- removed, was so 66ish
|
||||
# User message 0x0e: NodeData -- removed, was so 66ish
|
||||
|
|
|
@ -313,6 +313,7 @@ proc dumpDebuggingMetaData*(vmState: BaseVMState, header: BlockHeader,
|
|||
}
|
||||
|
||||
var metaData = %{
|
||||
"chainId": %distinctBase(com.chainId),
|
||||
"blockNumber": %blockNumber.toHex,
|
||||
"txTraces": traceTransactions(captureCom, header, blockBody),
|
||||
"stateDump": dumpBlockState(captureCom, header, blockBody),
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
std/[sequtils, typetraits],
|
||||
./constants, ./errors, eth/[common, keys], ./utils/utils,
|
||||
common/evmforks, ./vm_gas_costs
|
||||
|
||||
|
@ -32,40 +33,25 @@ proc intrinsicGas*(tx: Transaction, fork: EVMFork): GasInt =
|
|||
# Compute the baseline gas cost for this transaction. This is the amount
|
||||
# of gas needed to send this transaction (but that is not actually used
|
||||
# for computation)
|
||||
result = tx.payload.intrinsicGas(fork)
|
||||
result = distinctBase(tx.payload.input).intrinsicGas(fork)
|
||||
|
||||
if tx.contractCreation:
|
||||
result = result + gasFees[fork][GasTXCreate]
|
||||
if fork >= FkShanghai:
|
||||
# cannot use wordCount here, it will raise unlisted exception
|
||||
let numWords = toWordSize(tx.payload.len)
|
||||
let numWords = toWordSize(tx.payload.input.len)
|
||||
result = result + (gasFees[fork][GasInitcodeWord] * numWords)
|
||||
|
||||
if tx.txType > TxLegacy:
|
||||
result = result + tx.accessList.len * ACCESS_LIST_ADDRESS_COST
|
||||
if tx.payload.access_list.isSome:
|
||||
template access_list: untyped = tx.payload.access_list.unsafeGet
|
||||
result = result + access_list.len * ACCESS_LIST_ADDRESS_COST
|
||||
var numKeys = 0
|
||||
for n in tx.accessList:
|
||||
inc(numKeys, n.storageKeys.len)
|
||||
for n in access_list:
|
||||
inc(numKeys, n.storage_keys.len)
|
||||
result = result + numKeys * ACCESS_LIST_STORAGE_KEY_COST
|
||||
|
||||
proc getSignature*(tx: Transaction, output: var Signature): bool =
|
||||
var bytes: array[65, byte]
|
||||
bytes[0..31] = tx.R.toBytesBE()
|
||||
bytes[32..63] = tx.S.toBytesBE()
|
||||
|
||||
if tx.txType == TxLegacy:
|
||||
var v = tx.V
|
||||
if v >= EIP155_CHAIN_ID_OFFSET:
|
||||
v = 28 - (v and 0x01)
|
||||
elif v == 27 or v == 28:
|
||||
discard
|
||||
else:
|
||||
return false
|
||||
bytes[64] = byte(v - 27)
|
||||
else:
|
||||
bytes[64] = tx.V.byte
|
||||
|
||||
let sig = Signature.fromRaw(bytes)
|
||||
let sig = Signature.fromRaw(tx.signature.ecdsa_signature)
|
||||
if sig.isOk:
|
||||
output = sig[]
|
||||
return true
|
||||
|
@ -77,13 +63,8 @@ proc toSignature*(tx: Transaction): Signature =
|
|||
|
||||
proc getSender*(tx: Transaction, output: var EthAddress): bool =
|
||||
## Find the address the transaction was sent from.
|
||||
var sig: Signature
|
||||
if tx.getSignature(sig):
|
||||
var txHash = tx.txHashNoSignature
|
||||
let pubkey = recover(sig, SkMessage(txHash.data))
|
||||
if pubkey.isOk:
|
||||
output = pubkey[].toCanonicalAddress()
|
||||
result = true
|
||||
output = tx.signature.from_address
|
||||
true
|
||||
|
||||
proc getSender*(tx: Transaction): EthAddress =
|
||||
## Raises error on failure to recover public key
|
||||
|
@ -92,150 +73,30 @@ proc getSender*(tx: Transaction): EthAddress =
|
|||
|
||||
proc getRecipient*(tx: Transaction, sender: EthAddress): EthAddress =
|
||||
if tx.contractCreation:
|
||||
result = generateAddress(sender, tx.nonce)
|
||||
result = generateAddress(sender, tx.payload.nonce)
|
||||
else:
|
||||
result = tx.to.get()
|
||||
result = tx.payload.to.get()
|
||||
|
||||
proc validateTxLegacy(tx: Transaction, fork: EVMFork) =
|
||||
var
|
||||
vMin = 27'i64
|
||||
vMax = 28'i64
|
||||
|
||||
if tx.V >= EIP155_CHAIN_ID_OFFSET:
|
||||
let chainId = (tx.V - EIP155_CHAIN_ID_OFFSET) div 2
|
||||
vMin = 35 + (2 * chainId)
|
||||
vMax = vMin + 1
|
||||
|
||||
var isValid = tx.R >= UInt256.one
|
||||
isValid = isValid and tx.S >= UInt256.one
|
||||
isValid = isValid and tx.V >= vMin
|
||||
isValid = isValid and tx.V <= vMax
|
||||
isValid = isValid and tx.S < SECPK1_N
|
||||
isValid = isValid and tx.R < SECPK1_N
|
||||
|
||||
if fork >= FkHomestead:
|
||||
isValid = isValid and tx.S < SECPK1_N div 2
|
||||
|
||||
if not isValid:
|
||||
raise newException(ValidationError, "Invalid legacy transaction")
|
||||
|
||||
proc validateTxEip2930(tx: Transaction) =
|
||||
var isValid = tx.V == 0'i64 or tx.V == 1'i64
|
||||
isValid = isValid and tx.S >= UInt256.one
|
||||
isValid = isValid and tx.S < SECPK1_N
|
||||
isValid = isValid and tx.R < SECPK1_N
|
||||
|
||||
if not isValid:
|
||||
raise newException(ValidationError, "Invalid typed transaction")
|
||||
|
||||
proc validateTxEip4844(tx: Transaction) =
|
||||
validateTxEip2930(tx)
|
||||
|
||||
var isValid = tx.payload.len <= MAX_CALLDATA_SIZE
|
||||
isValid = isValid and tx.accessList.len <= MAX_ACCESS_LIST_SIZE
|
||||
|
||||
for acl in tx.accessList:
|
||||
isValid = isValid and
|
||||
(acl.storageKeys.len <= MAX_ACCESS_LIST_STORAGE_KEYS)
|
||||
|
||||
isValid = isValid and
|
||||
tx.versionedHashes.len <= MAX_BLOBS_PER_BLOCK
|
||||
|
||||
for bv in tx.versionedHashes:
|
||||
isValid = isValid and
|
||||
bv.data[0] == VERSIONED_HASH_VERSION_KZG
|
||||
|
||||
if not isValid:
|
||||
raise newException(ValidationError, "Invalid EIP-4844 transaction")
|
||||
|
||||
proc validate*(tx: Transaction, fork: EVMFork) =
|
||||
proc validate*(tx: Transaction, fork: EVMFork, chainId: ChainId) =
|
||||
# parameters pass validation rules
|
||||
if tx.intrinsicGas(fork) > tx.gasLimit:
|
||||
if tx.intrinsicGas(fork).uint64 > tx.payload.gas:
|
||||
raise newException(ValidationError, "Insufficient gas")
|
||||
|
||||
if fork >= FkShanghai and tx.contractCreation and tx.payload.len > EIP3860_MAX_INITCODE_SIZE:
|
||||
if fork >= FkShanghai and tx.contractCreation and
|
||||
tx.payload.input.len > EIP3860_MAX_INITCODE_SIZE:
|
||||
raise newException(ValidationError, "Initcode size exceeds max")
|
||||
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
template vhs: untyped = tx.payload.blob_versioned_hashes.unsafeGet
|
||||
if vhs.len > MAX_BLOBS_PER_BLOCK:
|
||||
raise newException(ValidationError, "Too many blob versioned hashes")
|
||||
if not vhs.allIt(it.data[0] == VERSIONED_HASH_VERSION_KZG):
|
||||
raise newException(ValidationError, "Invalid blob versioned hash")
|
||||
|
||||
# check signature validity
|
||||
var sender: EthAddress
|
||||
if not tx.getSender(sender):
|
||||
raise newException(ValidationError, "Invalid signature or failed message verification")
|
||||
|
||||
case tx.txType
|
||||
of TxLegacy:
|
||||
validateTxLegacy(tx, fork)
|
||||
of TxEip4844:
|
||||
validateTxEip4844(tx)
|
||||
of TxEip2930, TxEip1559:
|
||||
validateTxEip2930(tx)
|
||||
|
||||
proc signTransaction*(tx: Transaction, privateKey: PrivateKey, chainId: ChainId, eip155: bool): Transaction =
|
||||
result = tx
|
||||
if eip155:
|
||||
# trigger rlpEncodeEIP155 in nim-eth
|
||||
result.V = chainId.int64 * 2'i64 + 35'i64
|
||||
|
||||
let
|
||||
rlpTx = rlpEncode(result)
|
||||
sig = sign(privateKey, rlpTx).toRaw
|
||||
|
||||
case tx.txType
|
||||
of TxLegacy:
|
||||
if eip155:
|
||||
result.V = sig[64].int64 + result.V
|
||||
else:
|
||||
result.V = sig[64].int64 + 27'i64
|
||||
else:
|
||||
result.V = sig[64].int64
|
||||
|
||||
result.R = UInt256.fromBytesBE(sig[0..31])
|
||||
result.S = UInt256.fromBytesBE(sig[32..63])
|
||||
|
||||
# deriveChainId derives the chain id from the given v parameter
|
||||
func deriveChainId*(v: int64, chainId: ChainId): ChainId =
|
||||
if v == 27 or v == 28:
|
||||
chainId
|
||||
else:
|
||||
((v - 35) div 2).ChainId
|
||||
|
||||
func validateChainId*(tx: Transaction, chainId: ChainId): bool =
|
||||
if tx.txType == TxLegacy:
|
||||
chainId.uint64 == deriveChainId(tx.V, chainId).uint64
|
||||
else:
|
||||
chainId.uint64 == tx.chainId.uint64
|
||||
|
||||
func eip1559TxNormalization*(tx: Transaction;
|
||||
baseFee: GasInt): Transaction =
|
||||
## This function adjusts a legacy transaction to EIP-1559 standard. This
|
||||
## is needed particularly when using the `validateTransaction()` utility
|
||||
## with legacy transactions.
|
||||
result = tx
|
||||
if tx.txType < TxEip1559:
|
||||
result.maxPriorityFee = tx.gasPrice
|
||||
result.maxFee = tx.gasPrice
|
||||
else:
|
||||
result.gasPrice = baseFee + min(result.maxPriorityFee, result.maxFee - baseFee)
|
||||
|
||||
func effectiveGasTip*(tx: Transaction; baseFee: Option[UInt256]): GasInt =
|
||||
var
|
||||
maxPriorityFee = tx.maxPriorityFee
|
||||
maxFee = tx.maxFee
|
||||
baseFee = baseFee.get(0.u256).truncate(GasInt)
|
||||
|
||||
if tx.txType < TxEip1559:
|
||||
maxPriorityFee = tx.gasPrice
|
||||
maxFee = tx.gasPrice
|
||||
|
||||
min(maxPriorityFee, maxFee - baseFee)
|
||||
|
||||
proc decodeTx*(bytes: openArray[byte]): Transaction =
|
||||
var rlp = rlpFromBytes(bytes)
|
||||
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")
|
||||
let anyTx = AnyTransaction.fromOneOfBase(tx).valueOr:
|
||||
raise newException(ValidationError, "Invalid combination of fields")
|
||||
withTxVariant(anyTx):
|
||||
if txVariant.validate_transaction(chainId).isErr:
|
||||
raise newException(ValidationError,
|
||||
"Invalid signature or failed message verification")
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/typetraits,
|
||||
eth/common/eth_types, stint, options, stew/ptrops,
|
||||
chronos,
|
||||
".."/[vm_types, vm_state, vm_computation, vm_state_transactions],
|
||||
|
@ -124,7 +125,7 @@ proc initialAccessListEIP2929(call: CallParams) =
|
|||
# EIP2930 optional access list.
|
||||
for account in call.accessList:
|
||||
db.accessList(account.address)
|
||||
for key in account.storageKeys:
|
||||
for key in distinctBase(account.storageKeys):
|
||||
db.accessList(account.address, UInt256.fromBytesBE(key))
|
||||
|
||||
proc setupHost(call: CallParams): TransactionHost =
|
||||
|
|
|
@ -153,40 +153,42 @@ proc callParamsForTx(tx: Transaction, sender: EthAddress, vmState: BaseVMState,
|
|||
result = CallParams(
|
||||
vmState: vmState,
|
||||
forkOverride: some(fork),
|
||||
gasPrice: tx.gasPrice,
|
||||
gasLimit: tx.gasLimit,
|
||||
gasPrice: tx.payload.max_fee_per_gas.truncate(int64).GasInt,
|
||||
gasLimit: tx.payload.gas.int64,
|
||||
sender: sender,
|
||||
to: tx.destination,
|
||||
to: tx.payload.to.get(default(EthAddress)),
|
||||
isCreate: tx.contractCreation,
|
||||
value: tx.value,
|
||||
input: tx.payload
|
||||
value: tx.payload.value,
|
||||
input: distinctBase(tx.payload.input)
|
||||
)
|
||||
if tx.txType > TxLegacy:
|
||||
result.accessList = tx.accessList
|
||||
if tx.payload.access_list.isSome:
|
||||
result.accessList = tx.payload.access_list.unsafeGet
|
||||
|
||||
if tx.txType >= TxEip4844:
|
||||
result.versionedHashes = tx.versionedHashes
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
result.versionedHashes =
|
||||
distinctBase(tx.payload.blob_versioned_hashes.unsafeGet)
|
||||
|
||||
proc callParamsForTest(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: EVMFork): CallParams =
|
||||
result = CallParams(
|
||||
vmState: vmState,
|
||||
forkOverride: some(fork),
|
||||
gasPrice: tx.gasPrice,
|
||||
gasLimit: tx.gasLimit,
|
||||
gasPrice: tx.payload.max_fee_per_gas.truncate(int64).GasInt,
|
||||
gasLimit: tx.payload.gas.int64,
|
||||
sender: sender,
|
||||
to: tx.destination,
|
||||
to: tx.payload.to.get(default(EthAddress)),
|
||||
isCreate: tx.contractCreation,
|
||||
value: tx.value,
|
||||
input: tx.payload,
|
||||
value: tx.payload.value,
|
||||
input: distinctBase(tx.payload.input),
|
||||
|
||||
noIntrinsic: true, # Don't charge intrinsic gas.
|
||||
noRefund: true, # Don't apply gas refund/burn rule.
|
||||
)
|
||||
if tx.txType > TxLegacy:
|
||||
result.accessList = tx.accessList
|
||||
if tx.payload.access_list.isSome:
|
||||
result.accessList = tx.payload.access_list.unsafeGet
|
||||
|
||||
if tx.txType >= TxEip4844:
|
||||
result.versionedHashes = tx.versionedHashes
|
||||
if tx.payload.blob_versioned_hashes.isSome:
|
||||
result.versionedHashes =
|
||||
distinctBase(tx.payload.blob_versioned_hashes.unsafeGet)
|
||||
|
||||
proc txCallEvm*(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: EVMFork): GasInt
|
||||
{.gcsafe, raises: [CatchableError].} =
|
||||
|
|
|
@ -51,23 +51,6 @@ type
|
|||
# Private helpers
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc vrsSerialised(tx: Transaction): Result[array[65,byte],UtilsError] =
|
||||
## Parts copied from `transaction.getSignature`.
|
||||
var data: array[65,byte]
|
||||
data[0..31] = tx.R.toBytesBE
|
||||
data[32..63] = tx.S.toBytesBE
|
||||
|
||||
if tx.txType != TxLegacy:
|
||||
data[64] = tx.V.byte
|
||||
elif tx.V >= EIP155_CHAIN_ID_OFFSET:
|
||||
data[64] = byte(1 - (tx.V and 1))
|
||||
elif tx.V == 27 or tx.V == 28:
|
||||
data[64] = byte(tx.V - 27)
|
||||
else:
|
||||
return err((errSigPrefixError,"")) # legacy error
|
||||
|
||||
ok(data)
|
||||
|
||||
proc encodePreSealed(header: BlockHeader): seq[byte] =
|
||||
## Cut sigature off `extraData` header field.
|
||||
if header.extraData.len < EXTRA_SEAL:
|
||||
|
@ -113,22 +96,6 @@ proc ecRecover*(header: BlockHeader): EcAddrResult =
|
|||
## the argument header.
|
||||
header.extraData.recoverImpl(header.hashPreSealed)
|
||||
|
||||
proc ecRecover*(tx: var Transaction): EcAddrResult =
|
||||
## Extracts sender address from transaction. This function has similar
|
||||
## functionality as `transaction.getSender()`.
|
||||
let txSig = tx.vrsSerialised
|
||||
if txSig.isErr:
|
||||
return err(txSig.error)
|
||||
try:
|
||||
result = txSig.value.recoverImpl(tx.txHashNoSignature)
|
||||
except ValueError as ex:
|
||||
return err((errTxEncError, ex.msg))
|
||||
|
||||
proc ecRecover*(tx: Transaction): EcAddrResult =
|
||||
## Variant of `ecRecover()` for call-by-value header.
|
||||
var ty = tx
|
||||
ty.ecRecover
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public constructor for caching ecRecover version
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -69,7 +69,8 @@ proc main() =
|
|||
|
||||
let
|
||||
blockEnv = json.parseFile(paramStr(1))
|
||||
memoryDB = newCoreDbRef(LegacyDbMemory)
|
||||
chainId = blockEnv["chainId"].getInt().ChainId
|
||||
memoryDB = newCoreDbRef(LegacyDbMemory, chainId)
|
||||
blockNumber = UInt256.fromHex(blockEnv["blockNumber"].getStr())
|
||||
|
||||
prepareBlockEnv(blockEnv, memoryDB)
|
||||
|
|
|
@ -49,12 +49,13 @@ proc request*(
|
|||
proc requestBlockBody(
|
||||
n: JsonNode,
|
||||
blockNumber: BlockNumber,
|
||||
chainId: ChainId,
|
||||
client: Option[RpcClient] = none[RpcClient]()): BlockBody =
|
||||
let txs = n["transactions"]
|
||||
if txs.len > 0:
|
||||
result.transactions = newSeqOfCap[Transaction](txs.len)
|
||||
for tx in txs:
|
||||
let txn = parseTransaction(tx)
|
||||
let txn = parseTransaction(tx, chainId)
|
||||
validateTxSenderAndHash(tx, txn)
|
||||
result.transactions.add txn
|
||||
|
||||
|
@ -108,12 +109,13 @@ proc requestHeader*(
|
|||
|
||||
proc requestBlock*(
|
||||
blockNumber: BlockNumber,
|
||||
chainId: ChainId,
|
||||
flags: set[DownloadFlags] = {},
|
||||
client: Option[RpcClient] = none[RpcClient]()): Block =
|
||||
let header = requestHeader(blockNumber, client)
|
||||
result.jsonData = header
|
||||
result.header = parseBlockHeader(header)
|
||||
result.body = requestBlockBody(header, blockNumber, client)
|
||||
result.body = requestBlockBody(header, blockNumber, chainId, client)
|
||||
|
||||
if DownloadTxTrace in flags:
|
||||
result.traces = requestTxTraces(header, client)
|
||||
|
|
|
@ -46,8 +46,12 @@ proc dumpDebug(com: CommonRef, blockNumber: UInt256) =
|
|||
vmState.dumpDebuggingMetaData(header, body, false)
|
||||
|
||||
proc main() {.used.} =
|
||||
let conf = getConfiguration()
|
||||
let com = CommonRef.new(newCoreDbRef(LegacyDbPersistent, conf.dataDir), false)
|
||||
let
|
||||
conf = getConfiguration()
|
||||
params = networkParams(conf.netId)
|
||||
com = CommonRef.new(
|
||||
newCoreDbRef(LegacyDbPersistent, conf.dataDir, params.config.chainId),
|
||||
false)
|
||||
|
||||
if conf.head != 0.u256:
|
||||
dumpDebug(com, conf.head)
|
||||
|
|
|
@ -35,10 +35,11 @@ proc parseAddress(address: string): EthAddress =
|
|||
proc parseU256(val: string): UInt256 =
|
||||
UInt256.fromHex(val)
|
||||
|
||||
proc prepareBlockEnv(parent: BlockHeader, thisBlock: Block): CoreDbRef =
|
||||
proc prepareBlockEnv(
|
||||
parent: BlockHeader, thisBlock: Block, chainId: ChainId): CoreDbRef =
|
||||
var
|
||||
accounts = requestPostState(thisBlock)
|
||||
memoryDB = newCoreDbRef LegacyDbMemory
|
||||
accounts = requestPostState(thisBlock, chainId)
|
||||
memoryDB = newCoreDbRef(LegacyDbMemory, chainId)
|
||||
accountDB = newAccountStateDB(memoryDB, parent.stateRoot, false)
|
||||
parentNumber = %(parent.blockNumber.prefixHex)
|
||||
|
||||
|
@ -95,13 +96,14 @@ proc putAncestorsIntoDB(vmState: HunterVMState, db: CoreDbRef) =
|
|||
for header in vmState.headers.values:
|
||||
db.addBlockNumberToHashLookup(header)
|
||||
|
||||
proc huntProblematicBlock(blockNumber: UInt256): ValidationResult =
|
||||
proc huntProblematicBlock(
|
||||
blockNumber: UInt256, chainId: ChainId): ValidationResult =
|
||||
let
|
||||
# prepare needed state from previous block
|
||||
parentNumber = blockNumber - 1
|
||||
thisBlock = requestBlock(blockNumber)
|
||||
parentBlock = requestBlock(parentNumber)
|
||||
memoryDB = prepareBlockEnv(parentBlock.header, thisBlock)
|
||||
thisBlock = requestBlock(blockNumber, chainId)
|
||||
parentBlock = requestBlock(parentNumber, chainId)
|
||||
memoryDB = prepareBlockEnv(parentBlock.header, thisBlock, chainId)
|
||||
|
||||
# try to execute current block
|
||||
com = CommonRef.new(memoryDB, false)
|
||||
|
@ -122,7 +124,10 @@ proc huntProblematicBlock(blockNumber: UInt256): ValidationResult =
|
|||
result = validationResult
|
||||
|
||||
proc main() {.used.} =
|
||||
let conf = getConfiguration()
|
||||
let
|
||||
conf = getConfiguration()
|
||||
params = networkParams(conf.netId)
|
||||
chainId = params.config.chainId
|
||||
|
||||
if conf.head == 0.u256:
|
||||
echo "please specify the starting block with `--head:blockNumber`"
|
||||
|
@ -138,7 +143,7 @@ proc main() {.used.} =
|
|||
|
||||
while true:
|
||||
echo blockNumber
|
||||
if huntProblematicBlock(blockNumber) != ValidationResult.OK:
|
||||
if huntProblematicBlock(blockNumber, chainId) != ValidationResult.OK:
|
||||
echo "shot down problematic block: ", blockNumber
|
||||
problematicBlocks.add blockNumber
|
||||
blockNumber = blockNumber + 1
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
# according to those terms.
|
||||
|
||||
import
|
||||
json, strutils, options, os,
|
||||
std/[json, options, os, strutils, typetraits],
|
||||
eth/common, httputils, nimcrypto/utils,
|
||||
stint, stew/byteutils
|
||||
results, stint, stew/byteutils
|
||||
|
||||
import ../nimbus/transaction, ../nimbus/utils/ec_recover
|
||||
|
||||
|
@ -96,15 +96,36 @@ proc fromJson*(n: JsonNode, name: string, x: var SomeInteger) =
|
|||
x = T(node.getInt)
|
||||
doAssert($x == $node.getInt, name)
|
||||
|
||||
func fromJson(n: JsonNode, name: string, x: var ChainId) =
|
||||
n.fromJson(name, distinctBase(x))
|
||||
|
||||
proc fromJson*(n: JsonNode, name: string, x: var EthTime) =
|
||||
x = EthTime(hexToInt(n[name].getStr(), uint64))
|
||||
doAssert(x.uint64.prefixHex == toLowerAscii(n[name].getStr()), name)
|
||||
|
||||
proc fromJson*[T](n: JsonNode, name: string, x: var Option[T]) =
|
||||
if name in n:
|
||||
if name in n and n[name].kind != JNull:
|
||||
var val: T
|
||||
n.fromJson(name, val)
|
||||
x = some(val)
|
||||
x = options.some(val)
|
||||
else:
|
||||
x = options.none(T)
|
||||
|
||||
func fromJson*[T](n: JsonNode, name: string, x: var Opt[T]) =
|
||||
if name in n and n[name].kind != JNull:
|
||||
var val: T
|
||||
n.fromJson(name, val)
|
||||
x.ok val
|
||||
else:
|
||||
x.err()
|
||||
|
||||
func fromJson*[E, N](n: JsonNode, name: string, x: var List[E, N]) =
|
||||
var v: seq[E]
|
||||
n.fromJson(name, v)
|
||||
if v.len > N:
|
||||
raise (ref ValueError)(msg:
|
||||
"List[" & $E & ", Limit " & $N & "] cannot fit " & $v.len & " items")
|
||||
x = List[E, N].init(v)
|
||||
|
||||
proc fromJson*(n: JsonNode, name: string, x: var TxType) =
|
||||
let node = n[name]
|
||||
|
@ -121,6 +142,20 @@ proc fromJson*(n: JsonNode, name: string, x: var seq[Hash256]) =
|
|||
hexToByteArray(v.getStr(), h.data)
|
||||
x.add h
|
||||
|
||||
func fromJson*(n: JsonNode, name: string, x: var common.AccessList) =
|
||||
x.reset()
|
||||
let node = n[name]
|
||||
for innerNode in node:
|
||||
var entry: common.AccessPair
|
||||
innerNode.fromJson "address", entry.address
|
||||
for storageKeyVal in innerNode["storageKeys"]:
|
||||
var storageKey: common.StorageKey
|
||||
hexToByteArray(storageKeyVal.getStr(), storageKey)
|
||||
if not entry.storage_keys.add storageKey:
|
||||
raise (ref ValueError)(msg: "StorageKeys capacity exceeded")
|
||||
if not x.add entry:
|
||||
raise (ref ValueError)(msg: "AccessList capacity exceeded")
|
||||
|
||||
proc parseBlockHeader*(n: JsonNode): BlockHeader =
|
||||
n.fromJson "parentHash", result.parentHash
|
||||
n.fromJson "sha3Uncles", result.ommersHash
|
||||
|
@ -147,53 +182,72 @@ proc parseBlockHeader*(n: JsonNode): BlockHeader =
|
|||
# probably geth bug
|
||||
result.fee = none(UInt256)
|
||||
|
||||
proc parseAccessPair(n: JsonNode): AccessPair =
|
||||
n.fromJson "address", result.address
|
||||
let keys = n["storageKeys"]
|
||||
for kn in keys:
|
||||
result.storageKeys.add hexToByteArray[32](kn.getStr())
|
||||
|
||||
proc parseTransaction*(n: JsonNode): Transaction =
|
||||
var tx = Transaction(txType: TxLegacy)
|
||||
proc parseTransaction*(n: JsonNode, chain_id: ChainId): Transaction =
|
||||
var
|
||||
tx: TransactionPayload
|
||||
gasPrice: Opt[UInt256]
|
||||
txChainId: Opt[ChainId]
|
||||
v: UInt256
|
||||
r: UInt256
|
||||
s: UInt256
|
||||
n.fromJson "nonce", tx.nonce
|
||||
n.fromJson "gasPrice", tx.gasPrice
|
||||
n.fromJson "gas", tx.gasLimit
|
||||
|
||||
if n["to"].kind != JNull:
|
||||
var to: EthAddress
|
||||
n.fromJson "to", to
|
||||
tx.to = some(to)
|
||||
|
||||
n.fromJson "gasPrice", gasPrice
|
||||
n.fromJson "gas", tx.gas
|
||||
n.fromJson "to", tx.to
|
||||
n.fromJson "value", tx.value
|
||||
n.fromJson "input", tx.payload
|
||||
n.fromJson "v", tx.V
|
||||
n.fromJson "r", tx.R
|
||||
n.fromJson "s", tx.S
|
||||
|
||||
if n.hasKey("type") and n["type"].kind != JNull:
|
||||
n.fromJson "type", tx.txType
|
||||
|
||||
if tx.txType >= TxEip1559:
|
||||
n.fromJson "maxPriorityFeePerGas", tx.maxPriorityFee
|
||||
n.fromJson "maxFeePerGas", tx.maxFee
|
||||
|
||||
if tx.txType >= TxEip2930:
|
||||
if n.hasKey("chainId"):
|
||||
let id = hexToInt(n["chainId"].getStr(), int)
|
||||
tx.chainId = ChainId(id)
|
||||
|
||||
let accessList = n["accessList"]
|
||||
if accessList.len > 0:
|
||||
for acn in accessList:
|
||||
tx.accessList.add parseAccessPair(acn)
|
||||
|
||||
if tx.txType >= TxEip4844:
|
||||
n.fromJson "maxFeePerBlobGas", tx.maxFeePerBlobGas
|
||||
|
||||
if n.hasKey("versionedHashes") and n["versionedHashes"].kind != JNull:
|
||||
n.fromJson "versionedHashes", tx.versionedHashes
|
||||
|
||||
tx
|
||||
n.fromJson "input", tx.input
|
||||
n.fromJson "v", v
|
||||
n.fromJson "r", r
|
||||
n.fromJson "s", s
|
||||
n.fromJson "chainId", txChainId
|
||||
n.fromJson "type", tx.tx_type
|
||||
n.fromJson "accessList", tx.access_list
|
||||
n.fromJson "maxPriorityFeePerGas", tx.max_priority_fee_per_gas
|
||||
n.fromJson "maxFeePerGas", tx.max_fee_per_gas
|
||||
n.fromJson "maxFeePerBlobGas", tx.max_fee_per_blob_gas
|
||||
n.fromJson "versionedHashes", tx.blob_versioned_hashes
|
||||
if gasPrice.get(tx.max_fee_per_gas) != tx.max_fee_per_gas:
|
||||
raise (ref ValueError)(msg: "`gasPrice` and `maxFeePerGas` don't match")
|
||||
if tx.tx_type.get(TxLegacy) != TxLegacy and txChainId.isNone:
|
||||
raise (ref ValueError)(msg: "`chainId` is required")
|
||||
if tx.tx_type == Opt.some(TxLegacy) and txChainId.isNone:
|
||||
tx.tx_type.reset()
|
||||
if txChainId.get(chain_id) != chain_id:
|
||||
raise (ref ValueError)(msg: "Unsupported `chainId`")
|
||||
if r >= SECP256K1N:
|
||||
raise (ref ValueError)(msg: "Invalid `r`")
|
||||
if s < UInt256.one or s >= SECP256K1N:
|
||||
raise (ref ValueError)(msg: "Invalid `s`")
|
||||
let anyTx = AnyTransactionPayload.fromOneOfBase(tx).valueOr:
|
||||
raise (ref ValueError)(msg: "Invalid combination of fields")
|
||||
withTxPayloadVariant(anyTx):
|
||||
let y_parity =
|
||||
when txKind == TransactionKind.Replayable:
|
||||
if v == 27.u256:
|
||||
false
|
||||
elif v == 28.u256:
|
||||
true
|
||||
else:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
elif txKind == TransactionKind.Legacy:
|
||||
let
|
||||
res = v.isEven
|
||||
expected_v =
|
||||
distinctBase(chain_id).u256 * 2 + (if res: 36.u256 else: 35.u256)
|
||||
if v != expected_v:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
res
|
||||
else:
|
||||
if v > UInt256.one:
|
||||
raise (ref ValueError)(msg: "Invalid `v`")
|
||||
v.isOdd
|
||||
var signature: TransactionSignature
|
||||
signature.ecdsa_signature = ecdsa_pack_signature(y_parity, r, s)
|
||||
signature.from_address = ecdsa_recover_from_address(
|
||||
signature.ecdsa_signature,
|
||||
txPayloadVariant.compute_sig_hash(chain_id)).valueOr:
|
||||
raise (ref ValueError)(msg: "Cannot compute `from` address")
|
||||
Transaction(payload: tx, signature: signature)
|
||||
|
||||
proc parseWithdrawal*(n: JsonNode): Withdrawal =
|
||||
n.fromJson "index", result.index
|
||||
|
|
|
@ -52,14 +52,17 @@ proc main() {.used.} =
|
|||
# 52029 first block with receipts logs
|
||||
# 66407 failed transaction
|
||||
|
||||
let conf = configuration.getConfiguration()
|
||||
let com = CommonRef.new(
|
||||
newCoreDbRef(LegacyDbPersistent, conf.dataDir),
|
||||
false, conf.netId, networkParams(conf.netId))
|
||||
let
|
||||
conf = configuration.getConfiguration()
|
||||
params = networkParams(conf.netId)
|
||||
com = CommonRef.new(
|
||||
newCoreDbRef(LegacyDbPersistent, conf.dataDir, params.config.chainId),
|
||||
false, conf.netId, networkParams(conf.netId))
|
||||
|
||||
# move head to block number ...
|
||||
if conf.head != 0.u256:
|
||||
var parentBlock = requestBlock(conf.head, { DownloadAndValidate })
|
||||
var parentBlock = requestBlock(
|
||||
conf.head, com.chainId, { DownloadAndValidate })
|
||||
discard com.db.setHead(parentBlock.header)
|
||||
|
||||
if canonicalHeadHashKey().toOpenArray notin com.db.kvt:
|
||||
|
@ -86,7 +89,8 @@ proc main() {.used.} =
|
|||
|
||||
var thisBlock: Block
|
||||
try:
|
||||
thisBlock = requestBlock(blockNumber, { DownloadAndValidate })
|
||||
thisBlock = requestBlock(
|
||||
blockNumber, com.chainId, { DownloadAndValidate })
|
||||
except CatchableError as e:
|
||||
if retryCount < 3:
|
||||
warn "Unable to get block data via JSON-RPC API", error = e.msg
|
||||
|
|
|
@ -48,12 +48,14 @@ proc main() =
|
|||
try:
|
||||
let
|
||||
nimbus = json.parseFile(paramStr(1))
|
||||
chainId = nimbus["chainId"].getInt().ChainId
|
||||
blockNumber = UInt256.fromHex(nimbus["blockNumber"].getStr())
|
||||
thisBlock = requestBlock(blockNumber, {DownloadReceipts, DownloadTxTrace})
|
||||
accounts = requestPostState(thisBlock)
|
||||
thisBlock = requestBlock(
|
||||
blockNumber, chainId, {DownloadReceipts, DownloadTxTrace})
|
||||
accounts = requestPostState(thisBlock, chainId)
|
||||
geth = generateGethData(thisBlock, blockNumber, accounts)
|
||||
parentNumber = blockNumber - 1.u256
|
||||
parentBlock = requestBlock(parentNumber)
|
||||
parentBlock = requestBlock(parentNumber, chainId)
|
||||
|
||||
processNimbusData(nimbus)
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ proc hasInternalTx(tx: Transaction, blockNumber: UInt256, sender: EthAddress): b
|
|||
recipientHasCode = code.getStr.len > 2 # "0x"
|
||||
|
||||
if tx.contractCreation:
|
||||
return recipientHasCode or tx.payload.len > 0
|
||||
return recipientHasCode or tx.payload.input.len > 0
|
||||
|
||||
recipientHasCode
|
||||
|
||||
|
@ -143,7 +143,8 @@ proc updateAccount*(address: string, account: JsonNode, blockNumber: UInt256) =
|
|||
x["value"] = padding(x["value"].getStr())
|
||||
account["storage"][x["key"].getStr] = x["value"]
|
||||
|
||||
proc requestPostState*(premix, n: JsonNode, blockNumber: UInt256) =
|
||||
proc requestPostState*(
|
||||
premix, n: JsonNode, blockNumber: UInt256, chainId: ChainId) =
|
||||
type
|
||||
TxKind {.pure.} = enum
|
||||
Regular
|
||||
|
@ -156,7 +157,7 @@ proc requestPostState*(premix, n: JsonNode, blockNumber: UInt256) =
|
|||
let tracer = jsonTracer(postStateTracer)
|
||||
for t in txs:
|
||||
var txKind = TxKind.Regular
|
||||
let tx = parseTransaction(t)
|
||||
let tx = parseTransaction(t, chainId)
|
||||
let sender = tx.getSender
|
||||
if tx.contractCreation: txKind = TxKind.ContractCreation
|
||||
if hasInternalTx(tx, blockNumber, sender):
|
||||
|
@ -171,11 +172,11 @@ proc requestPostState*(premix, n: JsonNode, blockNumber: UInt256) =
|
|||
|
||||
t["txKind"] = %($txKind)
|
||||
|
||||
proc requestPostState*(thisBlock: Block): JsonNode =
|
||||
proc requestPostState*(thisBlock: Block, chainId: ChainId): JsonNode =
|
||||
let blockNumber = thisBlock.header.blockNumber
|
||||
var premix = newJArray()
|
||||
|
||||
premix.requestPostState(thisBlock.jsonData, blockNumber)
|
||||
premix.requestPostState(thisBlock.jsonData, blockNumber, chainId)
|
||||
premix.requestAccount(blockNumber, thisBlock.header.coinbase)
|
||||
for uncle in thisBlock.body.uncles:
|
||||
premix.requestAccount(blockNumber, uncle.coinbase)
|
||||
|
|
|
@ -9,17 +9,19 @@
|
|||
# according to those terms.
|
||||
|
||||
import
|
||||
std/typetraits,
|
||||
json, stint, stew/byteutils,
|
||||
../nimbus/db/[core_db, storage_types], eth/[rlp, common],
|
||||
../nimbus/tracer
|
||||
|
||||
proc generatePrestate*(nimbus, geth: JsonNode, blockNumber: UInt256, parent, header: BlockHeader, body: BlockBody) =
|
||||
let
|
||||
chainId = nimbus["chainId"].getInt().ChainId
|
||||
state = nimbus["state"]
|
||||
headerHash = rlpHash(header)
|
||||
|
||||
var
|
||||
chainDB = newCoreDbRef(LegacyDbMemory)
|
||||
chainDB = newCoreDbRef(LegacyDbMemory, chainId)
|
||||
|
||||
discard chainDB.setHead(parent, true)
|
||||
discard chainDB.persistTransactions(blockNumber, body.transactions)
|
||||
|
@ -34,6 +36,7 @@ proc generatePrestate*(nimbus, geth: JsonNode, blockNumber: UInt256, parent, hea
|
|||
chainDB.kvt.put(key, value)
|
||||
|
||||
var metaData = %{
|
||||
"chainId": %distinctBase(chainId),
|
||||
"blockNumber": %blockNumber.toHex,
|
||||
"geth": geth
|
||||
}
|
||||
|
|
|
@ -52,7 +52,10 @@ proc validateBlock(com: CommonRef, blockNumber: BlockNumber): BlockNumber =
|
|||
proc main() {.used.} =
|
||||
let
|
||||
conf = getConfiguration()
|
||||
com = CommonRef.new(newCoreDbRef(LegacyDbPersistent, conf.dataDir), false)
|
||||
params = networkParams(conf.netId)
|
||||
com = CommonRef.new(
|
||||
newCoreDbRef(LegacyDbPersistent, conf.dataDir, params.config.chainId),
|
||||
false)
|
||||
|
||||
# move head to block number ...
|
||||
if conf.head == 0.u256:
|
||||
|
|
|
@ -54,11 +54,12 @@ proc buildAccountsTableFromKeys(
|
|||
proc verifyWitness*(
|
||||
trustedStateRoot: KeccakHash,
|
||||
witness: BlockWitness,
|
||||
flags: WitnessFlags): Result[TableRef[EthAddress, AccountData], string] =
|
||||
flags: WitnessFlags,
|
||||
chainId: ChainId): Result[TableRef[EthAddress, AccountData], string] =
|
||||
if witness.len() == 0:
|
||||
return err("witness is empty")
|
||||
|
||||
let db = newCoreDbRef(LegacyDbMemory)
|
||||
let db = newCoreDbRef(LegacyDbMemory, chainId)
|
||||
var tb = initTreeBuilder(witness, db, flags)
|
||||
|
||||
try:
|
||||
|
|
|
@ -57,9 +57,12 @@ proc main() {.used.} =
|
|||
|
||||
# nimbus --rpcapi: eth, debug --prune: archive
|
||||
|
||||
var conf = makeConfig()
|
||||
let db = newCoreDbRef(DefaultDbPersistent, string conf.dataDir)
|
||||
let com = CommonRef.new(db, false)
|
||||
let
|
||||
conf = makeConfig()
|
||||
params = networkParams(conf.networkId)
|
||||
db = newCoreDbRef(
|
||||
DefaultDbPersistent, string conf.dataDir, params.config.chainId)
|
||||
com = CommonRef.new(db, false)
|
||||
|
||||
com.dumpTest(97)
|
||||
com.dumpTest(98) # no uncles and no tx
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
# according to those terms.
|
||||
|
||||
import
|
||||
std/typetraits,
|
||||
json,
|
||||
../nimbus/common/common, # must be early (compilation annoyance)
|
||||
../nimbus/db/core_db/persistent,
|
||||
|
@ -32,6 +33,7 @@ proc dumpTest(com: CommonRef, blockNumber: int) =
|
|||
receipts = dumpReceipts(captureCom.db, header)
|
||||
|
||||
var metaData = %{
|
||||
"chainId": %distinctBase(com.chainId),
|
||||
"blockNumber": %blockNumber.toHex,
|
||||
"txTraces": txTrace,
|
||||
"stateDump": stateDump,
|
||||
|
@ -56,9 +58,12 @@ proc main() {.used.} =
|
|||
|
||||
# nimbus --rpc-api: eth, debug --prune: archive
|
||||
|
||||
var conf = makeConfig()
|
||||
let db = newCoreDbRef(LegacyDbPersistent, string conf.dataDir)
|
||||
let com = CommonRef.new(db, false)
|
||||
let
|
||||
conf = makeConfig()
|
||||
params = networkParams(conf.networkId)
|
||||
db = newCoreDbRef(
|
||||
LegacyDbPersistent, string conf.dataDir, params.config.chainId)
|
||||
com = CommonRef.new(db, false)
|
||||
|
||||
com.dumpTest(97)
|
||||
com.dumpTest(46147)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 34adff98a6c5ae9d5a59bd8859a848b1cb0ae80d
|
||||
Subproject commit 24ed2afba77249546d3c41485d7d37241681ad35
|
|
@ -1 +1 @@
|
|||
Subproject commit 248f2bdca2d65ff920920c72b764d0622d522596
|
||||
Subproject commit 338a47f5664868a3a9183d93fd46dbad9d183ff6
|
|
@ -1 +1 @@
|
|||
Subproject commit de87f860874be944cdc3dfd08765c687fff736c4
|
||||
Subproject commit 77bfa128f316c1959b92b2c4e7041c861e3ef2e6
|
|
@ -1 +1 @@
|
|||
Subproject commit fc9bc1da3ae7dde04f4591eba302f9e8b20c3924
|
||||
Subproject commit 9202e336e4427ee16dd7ca9da4a0f93379b08b22
|
Loading…
Reference in New Issue