Skip to content

Commit 5baf071

Browse files
committed
common,util,tx: implement aip 6493 stable container txs
debug and fix the legacy ssz encoding decoding add a spec test for legacy ssz encoding decoding add the ssztx boilerplate to other tx types implement sszRaw value for 2930 tx add 2930 spec test and debug/fix ssz encoding/decoding add the ssz encoding decoding to 1559 tx add eip 1559 testcase and get it working add 4844 ssz encoding decoding add eip 4844 testcase and get it working define block transactions ssz type and test ssz transactionsRoot handle ssz roots for transactions and withdrawals in block when 6493 activated handle the roots gen in the build block fix the transaction stable container update the execution payload serialization deserialization for 6493 add 6493 hardfork for the testing/devnet refactor the transaction factory ssz tx deserialization add ssz profile<>stablecontaiber conversion spec test add eip6493 support to common debug and fix the block transaction withdrawal root comparision by removing null keccak hash hardcoding enhance eip6493 tx test by testing transaction factory deserialization which uses stable container add client eip6493 end to end spec and fix the payload generation refactor tx serialization deserializion with respect to execution/beacon payload add, debug and fix the transactionv1 or hex transactions validator and debug/fix the newpayloadeip6493 spec test add 6493 to electra for kurtosis testing console log error for debugging console log error for debugging txpool fix attempt add more descriptive checks for nulloroptional add more descriptive checks for nulloroptional log full error debug and fix handling of replay vs legacy tx w.r.t. v/ypartity and confirm via spec test build fix dev and add transaction inclusion proof to the getTransactionX apis workaround to get the proof since stable container impl for proof seems buggy and breaking refactor the proof format based on feedback debug, discuss and fix the signature packing scheme add hack to schedule 6493 on prague in cli for stablecontainer devnets debug and fix newpayload eip6493 spec debug rebase and spec fixes in tx utils debug and fix block build fix the vm build debug and get 6493 end to end client spec working rebase 4844 fixes add ssz blockheader type and update the blockhash to use when ssz activated debug and update client spec with ssz blockhash update ssz field to receiptstrie updates after discussion with etan update test
1 parent 3136bd2 commit 5baf071

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1875
-79
lines changed

package-lock.json

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/block/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"tsc": "../../config/cli/ts-compile.sh"
4848
},
4949
"dependencies": {
50+
"@chainsafe/ssz": "https://github.com/ChainSafe/ssz/raw/cayman/stable-container/packages/ssz/package.tgz",
5051
"@ethereumjs/common": "^4.4.0",
5152
"@ethereumjs/rlp": "^5.0.2",
5253
"@ethereumjs/trie": "^6.2.1",

packages/block/src/block/block.ts

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak.js'
1818
// TODO: See if there is an easier way to achieve the same result.
1919
// See: https://github.com/microsoft/TypeScript/issues/47558
2020
// (situation will eventually improve on Typescript and/or Eslint update)
21+
import { genTransactionsSszRoot, genWithdrawalsSszRoot } from '../helpers.js'
2122
import {
2223
genRequestsTrieRoot,
2324
genTransactionsTrieRoot,
@@ -226,10 +227,9 @@ export class Block {
226227
* Generates transaction trie for validation.
227228
*/
228229
async genTxTrie(): Promise<Uint8Array> {
229-
return genTransactionsTrieRoot(
230-
this.transactions,
231-
new MerklePatriciaTrie({ common: this.common }),
232-
)
230+
return this.common.isActivatedEIP(6493)
231+
? genTransactionsSszRoot(this.transactions)
232+
: genTransactionsTrieRoot(this.transactions, new MerklePatriciaTrie({ common: this.common }))
233233
}
234234

235235
/**
@@ -238,16 +238,10 @@ export class Block {
238238
* @returns True if the transaction trie is valid, false otherwise
239239
*/
240240
async transactionsTrieIsValid(): Promise<boolean> {
241-
let result
242-
if (this.transactions.length === 0) {
243-
result = equalsBytes(this.header.transactionsTrie, KECCAK256_RLP)
244-
return result
245-
}
246-
247241
if (this.cache.txTrieRoot === undefined) {
248242
this.cache.txTrieRoot = await this.genTxTrie()
249243
}
250-
result = equalsBytes(this.cache.txTrieRoot, this.header.transactionsTrie)
244+
const result = equalsBytes(this.cache.txTrieRoot, this.header.transactionsTrie)
251245
return result
252246
}
253247

@@ -367,7 +361,9 @@ export class Block {
367361
}
368362

369363
if (!(await this.transactionsTrieIsValid())) {
370-
const msg = this._errorMsg('invalid transaction trie')
364+
const msg = this._errorMsg(
365+
`invalid transaction trie expected=${bytesToHex(this.cache.txTrieRoot!)}`,
366+
)
371367
throw new Error(msg)
372368
}
373369

@@ -456,6 +452,12 @@ export class Block {
456452
return equalsBytes(this.keccakFunction(raw), this.header.uncleHash)
457453
}
458454

455+
async genWithdrawalsTrie(): Promise<Uint8Array> {
456+
return this.common.isActivatedEIP(6493)
457+
? genWithdrawalsSszRoot(this.withdrawals!)
458+
: genWithdrawalsTrieRoot(this.withdrawals!, new MerklePatriciaTrie({ common: this.common }))
459+
}
460+
459461
/**
460462
* Validates the withdrawal root
461463
* @returns true if the withdrawals trie root is valid, false otherwise
@@ -465,19 +467,10 @@ export class Block {
465467
throw new Error('EIP 4895 is not activated')
466468
}
467469

468-
let result
469-
if (this.withdrawals!.length === 0) {
470-
result = equalsBytes(this.header.withdrawalsRoot!, KECCAK256_RLP)
471-
return result
472-
}
473-
474470
if (this.cache.withdrawalsTrieRoot === undefined) {
475-
this.cache.withdrawalsTrieRoot = await genWithdrawalsTrieRoot(
476-
this.withdrawals!,
477-
new MerklePatriciaTrie({ common: this.common }),
478-
)
471+
this.cache.withdrawalsTrieRoot = await this.genWithdrawalsTrie()
479472
}
480-
result = equalsBytes(this.cache.withdrawalsTrieRoot, this.header.withdrawalsRoot!)
473+
const result = equalsBytes(this.cache.withdrawalsTrieRoot, this.header.withdrawalsRoot!)
481474
return result
482475
}
483476

@@ -546,7 +539,9 @@ export class Block {
546539
toExecutionPayload(): ExecutionPayload {
547540
const blockJSON = this.toJSON()
548541
const header = blockJSON.header!
549-
const transactions = this.transactions.map((tx) => bytesToHex(tx.serialize())) ?? []
542+
const transactions = this.common.isActivatedEIP(6493)
543+
? this.transactions.map((tx) => tx.toExecutionPayloadTx())
544+
: this.transactions.map((tx) => bytesToHex(tx.serialize()))
550545
const withdrawalsArr = blockJSON.withdrawals ? { withdrawals: blockJSON.withdrawals } : {}
551546

552547
const executionPayload: ExecutionPayload = {

packages/block/src/block/constructors.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
type TxOptions,
55
createTx,
66
createTxFromBlockBodyData,
7+
createTxFromExecutionPayloadTx,
78
createTxFromRLP,
89
normalizeTxParams,
910
} from '@ethereumjs/tx'
@@ -25,7 +26,13 @@ import {
2526
} from '@ethereumjs/util'
2627

2728
import { generateCliqueBlockExtraData } from '../consensus/clique.js'
28-
import { genRequestsTrieRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot } from '../helpers.js'
29+
import {
30+
genRequestsTrieRoot,
31+
genTransactionsSszRoot,
32+
genTransactionsTrieRoot,
33+
genWithdrawalsSszRoot,
34+
genWithdrawalsTrieRoot,
35+
} from '../helpers.js'
2936
import {
3037
Block,
3138
createBlockHeader,
@@ -46,6 +53,7 @@ import type {
4653
RequestsBytes,
4754
WithdrawalsBytes,
4855
} from '../types.js'
56+
import type { Common } from '@ethereumjs/common'
4957
import type { TypedTransaction } from '@ethereumjs/tx'
5058
import type {
5159
CLRequest,
@@ -373,7 +381,7 @@ export const createBlockFromJSONRPCProvider = async (
373381
*/
374382
export async function createBlockFromExecutionPayload(
375383
payload: ExecutionPayload,
376-
opts?: BlockOptions,
384+
opts: BlockOptions & { common: Common },
377385
): Promise<Block> {
378386
const {
379387
blockNumber: number,
@@ -389,25 +397,39 @@ export async function createBlockFromExecutionPayload(
389397
} = payload
390398

391399
const txs = []
392-
for (const [index, serializedTx] of transactions.entries()) {
400+
for (const [index, serializedTxOrPayload] of transactions.entries()) {
393401
try {
394-
const tx = createTxFromRLP(hexToBytes(serializedTx as PrefixedHexString), {
395-
common: opts?.common,
396-
})
402+
let tx
403+
if (opts.common.isActivatedEIP(6493)) {
404+
if (typeof serializedTxOrPayload === 'string') {
405+
throw Error('EIP 6493 activated for transaction bytes')
406+
}
407+
tx = createTxFromExecutionPayloadTx(serializedTxOrPayload, {
408+
common: opts?.common,
409+
})
410+
} else {
411+
if (typeof serializedTxOrPayload !== 'string') {
412+
throw Error('EIP 6493 not activated for transaction payload')
413+
}
414+
tx = createTxFromRLP(hexToBytes(serializedTxOrPayload as PrefixedHexString), {
415+
common: opts?.common,
416+
})
417+
}
397418
txs.push(tx)
398419
} catch (error) {
399420
const validationError = `Invalid tx at index ${index}: ${error}`
400421
throw validationError
401422
}
402423
}
403424

404-
const transactionsTrie = await genTransactionsTrieRoot(
405-
txs,
406-
new MerklePatriciaTrie({ common: opts?.common }),
407-
)
425+
const transactionsTrie = opts.common.isActivatedEIP(6493)
426+
? await genTransactionsSszRoot(txs)
427+
: await genTransactionsTrieRoot(txs, new MerklePatriciaTrie({ common: opts?.common }))
408428
const withdrawals = withdrawalsData?.map((wData) => createWithdrawal(wData))
409429
const withdrawalsRoot = withdrawals
410-
? await genWithdrawalsTrieRoot(withdrawals, new MerklePatriciaTrie({ common: opts?.common }))
430+
? opts.common.isActivatedEIP(6493)
431+
? genWithdrawalsSszRoot(withdrawals)
432+
: await genWithdrawalsTrieRoot(withdrawals, new MerklePatriciaTrie({ common: opts?.common }))
411433
: undefined
412434

413435
const hasDepositRequests = depositRequests !== undefined && depositRequests !== null
@@ -481,7 +503,7 @@ export async function createBlockFromExecutionPayload(
481503
*/
482504
export async function createBlockFromBeaconPayloadJSON(
483505
payload: BeaconPayloadJSON,
484-
opts?: BlockOptions,
506+
opts: BlockOptions & { common: Common },
485507
): Promise<Block> {
486508
const executionPayload = executionPayloadFromBeaconPayload(payload)
487509
return createBlockFromExecutionPayload(executionPayload, opts)

packages/block/src/from-beacon-payload.ts

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { bigIntToHex } from '@ethereumjs/util'
22

33
import type { ExecutionPayload } from './types.js'
4-
import type { NumericString, PrefixedHexString, VerkleExecutionWitness } from '@ethereumjs/util'
4+
import type {
5+
NumericString,
6+
PrefixedHexString,
7+
VerkleExecutionWitness,
8+
ssz,
9+
} from '@ethereumjs/util'
510

611
type BeaconWithdrawal = {
712
index: PrefixedHexString
@@ -30,7 +35,41 @@ type BeaconConsolidationRequest = {
3035
target_pubkey: PrefixedHexString
3136
}
3237

33-
// Payload JSON that one gets using the beacon apis
38+
export type BeaconFeesPerGasV1 = {
39+
regular: PrefixedHexString | null // Quantity 64 bytes
40+
blob: PrefixedHexString | null // Quantity 64 bytes
41+
}
42+
43+
export type BeaconAccessTupleV1 = {
44+
address: PrefixedHexString // DATA 20 bytes
45+
storage_keys: PrefixedHexString[] // Data 32 bytes MAX_ACCESS_LIST_STORAGE_KEYS array
46+
}
47+
48+
export type BeaconTransactionPayloadV1 = {
49+
type: PrefixedHexString | null // Quantity, 1 byte
50+
chain_id: PrefixedHexString | null // Quantity 8 bytes
51+
nonce: PrefixedHexString | null // Quantity 8 bytes
52+
max_fees_per_gas: BeaconFeesPerGasV1 | null
53+
gas: PrefixedHexString | null // Quantity 8 bytes
54+
to: PrefixedHexString | null // DATA 20 bytes
55+
value: PrefixedHexString | null // Quantity 64 bytes
56+
input: PrefixedHexString | null // max MAX_CALLDATA_SIZE bytes,
57+
access_list: BeaconAccessTupleV1[] | null
58+
max_priority_fees_per_gas: BeaconFeesPerGasV1 | null
59+
blob_versioned_hashes: PrefixedHexString[] | null // DATA 32 bytes array
60+
}
61+
62+
export type BeaconTransactionSignatureV1 = {
63+
from: PrefixedHexString | null // DATA 20 bytes
64+
ecdsa_signature: PrefixedHexString | null // DATA 65 bytes or null
65+
}
66+
67+
type BeaconTransactionV1 = {
68+
payload: BeaconTransactionPayloadV1
69+
signature: BeaconTransactionSignatureV1
70+
}
71+
72+
// Payload json that one gets using the beacon apis
3473
// curl localhost:5052/eth/v2/beacon/blocks/56610 | jq .data.message.body.execution_payload
3574
export type BeaconPayloadJSON = {
3675
parent_hash: PrefixedHexString
@@ -46,7 +85,7 @@ export type BeaconPayloadJSON = {
4685
extra_data: PrefixedHexString
4786
base_fee_per_gas: NumericString
4887
block_hash: PrefixedHexString
49-
transactions: PrefixedHexString[]
88+
transactions: PrefixedHexString[] | BeaconTransactionV1[]
5089
withdrawals?: BeaconWithdrawal[]
5190
blob_gas_used?: NumericString
5291
excess_blob_gas?: NumericString
@@ -121,6 +160,36 @@ function parseExecutionWitnessFromSnakeJSON({
121160
* The JSON data can be retrieved from a consensus layer (CL) client on this Beacon API `/eth/v2/beacon/blocks/[block number]`
122161
*/
123162
export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJSON): ExecutionPayload {
163+
const transactions =
164+
typeof payload.transactions[0] === 'object'
165+
? (payload.transactions as BeaconTransactionV1[]).map((btxv1) => {
166+
return {
167+
payload: {
168+
type: btxv1.payload.type,
169+
chainId: btxv1.payload.chain_id,
170+
nonce: btxv1.payload.nonce,
171+
maxFeesPerGas: btxv1.payload.max_fees_per_gas,
172+
to: btxv1.payload.to,
173+
value: btxv1.payload.value,
174+
input: btxv1.payload.input,
175+
accessList:
176+
btxv1.payload.access_list?.map((bal: BeaconAccessTupleV1) => {
177+
return {
178+
address: bal.address,
179+
storageKeys: bal.storage_keys,
180+
}
181+
}) ?? null,
182+
maxPriorityFeesPerGas: btxv1.payload.max_priority_fees_per_gas,
183+
blobVersionedHashes: btxv1.payload.blob_versioned_hashes,
184+
},
185+
signature: {
186+
from: btxv1.signature.from,
187+
ecdsaSignature: btxv1.signature.ecdsa_signature,
188+
},
189+
} as ssz.TransactionV1
190+
})
191+
: (payload.transactions as PrefixedHexString[])
192+
124193
const executionPayload: ExecutionPayload = {
125194
parentHash: payload.parent_hash,
126195
feeRecipient: payload.fee_recipient,
@@ -135,7 +204,7 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJSON): E
135204
extraData: payload.extra_data,
136205
baseFeePerGas: bigIntToHex(BigInt(payload.base_fee_per_gas)),
137206
blockHash: payload.block_hash,
138-
transactions: payload.transactions,
207+
transactions,
139208
}
140209

141210
if (payload.withdrawals !== undefined && payload.withdrawals !== null) {

0 commit comments

Comments
 (0)