Skip to content

Commit

Permalink
monorepo: work on eip7002 exits
Browse files Browse the repository at this point in the history
  • Loading branch information
jochem-brouwer committed Mar 28, 2024
1 parent c1e6809 commit 3866a3a
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 11 deletions.
65 changes: 59 additions & 6 deletions packages/block/src/block.ts
Expand Up @@ -5,6 +5,7 @@ import { BlobEIP4844Transaction, Capability, TransactionFactory } from '@ethereu
import {
BIGINT_0,
Deposit,
Exit,
KECCAK256_RLP,
Withdrawal,
bigIntToHex,
Expand Down Expand Up @@ -41,7 +42,7 @@ import type {
TxOptions,
TypedTransaction,
} from '@ethereumjs/tx'
import type { DepositBytes, EthersProvider, WithdrawalBytes } from '@ethereumjs/util'
import type { DepositBytes, EthersProvider, ExitBytes, WithdrawalBytes } from '@ethereumjs/util'

/**
* An object that represents the block.
Expand All @@ -52,6 +53,7 @@ export class Block {
public readonly uncleHeaders: BlockHeader[] = []
public readonly withdrawals?: Withdrawal[]
public readonly deposits?: Deposit[]
public readonly exits?: Exit[]
public readonly common: Common
protected keccakFunction: (msg: Uint8Array) => Uint8Array

Expand Down Expand Up @@ -112,6 +114,7 @@ export class Block {
transactions: txsData,
uncleHeaders: uhsData,
withdrawals: withdrawalsData,
exits: exitData,
executionWitness: executionWitnessData,
deposits: depositData,
} = blockData
Expand Down Expand Up @@ -151,6 +154,7 @@ export class Block {
// The witness data is planned to come in rlp serialized bytes so leave this
// stub till that time
const executionWitness = executionWitnessData
const exits = exitData?.map(Exit.fromExitData)

const deposits = depositData?.map(Deposit.fromDepositData)

Expand All @@ -161,7 +165,8 @@ export class Block {
withdrawals,
opts,
executionWitness,
deposits
deposits,
exits
)
}

Expand Down Expand Up @@ -196,8 +201,15 @@ export class Block {

// First try to load header so that we can use its common (in case of setHardfork being activated)
// to correctly make checks on the hardforks
const [headerData, txsData, uhsData, withdrawalBytes, depositBytes, executionWitnessBytes] =
values
const [
headerData,
txsData,
uhsData,
withdrawalBytes,
depositBytes,
exitBytes,
executionWitnessBytes,
] = values
const header = BlockHeader.fromValuesArray(headerData, opts)

if (
Expand All @@ -217,6 +229,14 @@ export class Block {
'Invalid serialized block input: EIP-6110 is active, and no deposits were provided as array'
)
}
if (
header.common.isActivatedEIP(7002) &&
(exitBytes === undefined || !Array.isArray(exitBytes))

Check warning on line 234 in packages/block/src/block.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/block.ts#L234

Added line #L234 was not covered by tests
) {
throw new Error(
'Invalid serialized block input: EIP-7002 is active, and no exits were provided as array'
)

Check warning on line 238 in packages/block/src/block.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/block.ts#L236-L238

Added lines #L236 - L238 were not covered by tests
}

// parse transactions
const transactions = []
Expand Down Expand Up @@ -267,6 +287,16 @@ export class Block {
}))
?.map(Deposit.fromDepositData)
: undefined
// TODO fix my type, also figure out why we cannot parse this the same as WithdrawalBytes
// @ts-ignore
const exits = header.common.isActivatedEIP(7002)
? (exitBytes as ExitBytes[])
?.map(([address, validatorPubkey]) => ({
address,
validatorPubkey,
}))

Check warning on line 297 in packages/block/src/block.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/block.ts#L293-L297

Added lines #L293 - L297 were not covered by tests
?.map(Exit.fromExitData)
: undefined

// executionWitness are not part of the EL fetched blocks via eth_ bodies method
// they are currently only available via the engine api constructed blocks
Expand All @@ -290,7 +320,8 @@ export class Block {
withdrawals,
opts,
executionWitness,
deposits
deposits,
exits
)
}

Expand Down Expand Up @@ -464,7 +495,8 @@ export class Block {
withdrawals?: Withdrawal[],
opts: BlockOptions = {},
executionWitness?: VerkleExecutionWitness | null,
deposits?: Deposit[]
deposits?: Deposit[],
exits?: Exit[]
) {
this.header = header ?? BlockHeader.fromHeaderData({}, opts)
this.common = this.header.common
Expand All @@ -473,6 +505,7 @@ export class Block {
this.transactions = transactions
this.withdrawals = withdrawals ?? (this.common.isActivatedEIP(4895) ? [] : undefined)
this.deposits = deposits ?? (this.common.isActivatedEIP(6110) ? [] : undefined)
this.exits = exits ?? (this.common.isActivatedEIP(7002) ? [] : undefined)
this.executionWitness = executionWitness
// null indicates an intentional absence of value or unavailability
// undefined indicates that the executionWitness should be initialized with the default state
Expand Down Expand Up @@ -526,6 +559,10 @@ export class Block {
throw new Error(`Cannot have executionWitness field if EIP 6800 is not active `)
}

if (!this.common.isActivatedEIP(7002) && exits !== undefined && exits !== null) {
throw new Error(`Cannot have exits field if EIP 7002 is not active`)

Check warning on line 563 in packages/block/src/block.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/block.ts#L563

Added line #L563 was not covered by tests
}

const freeze = opts?.freeze ?? true
if (freeze) {
Object.freeze(this)
Expand All @@ -551,6 +588,10 @@ export class Block {
if (depositsRaw) {
bytesArray.push(depositsRaw)
}
const exitsRaw = this.exits?.map((ex) => ex.raw())
if (exitsRaw) {
bytesArray.push(exitsRaw)
}

Check warning on line 594 in packages/block/src/block.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/block.ts#L593-L594

Added lines #L593 - L594 were not covered by tests
if (this.executionWitness !== undefined && this.executionWitness !== null) {
const executionWitnessBytes = RLP.encode(JSON.stringify(this.executionWitness))
bytesArray.push(executionWitnessBytes as any)
Expand Down Expand Up @@ -724,6 +765,12 @@ export class Block {
throw new Error(`Invalid block: ethereumjs stateless client needs executionWitness`)
}
}

if (this.common.isActivatedEIP(7002)) {
if (this.exits === undefined || this.exits === null) {
throw new Error('Invalid block: missing exits')
}
}

Check warning on line 773 in packages/block/src/block.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/block.ts#L770-L773

Added lines #L770 - L773 were not covered by tests
}

/**
Expand Down Expand Up @@ -873,12 +920,18 @@ export class Block {
deposits: this.deposits.map((deposit) => deposit.toJSON()),
}
: {}
const exitsAttr = this.exits
? {
exits: this.exits.map((exit) => exit.toJSON()),
}

Check warning on line 926 in packages/block/src/block.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/block.ts#L924-L926

Added lines #L924 - L926 were not covered by tests
: {}
return {
header: this.header.toJSON(),
transactions: this.transactions.map((tx) => tx.toJSON()),
uncleHeaders: this.uncleHeaders.map((uh) => uh.toJSON()),
...withdrawalsAttr,
...depositsAttr,
...exitsAttr,
}
}

Expand Down
9 changes: 8 additions & 1 deletion packages/block/src/from-rpc.ts
Expand Up @@ -55,7 +55,14 @@ export function blockFromRpc(
const uncleHeaders = uncles.map((uh) => blockHeaderFromRpc(uh, options))

return Block.fromBlockData(
{ header, transactions, uncleHeaders, withdrawals: blockParams.withdrawals },
{
header,
transactions,
uncleHeaders,
withdrawals: blockParams.withdrawals,
deposits: blockParams.deposits,
exits: blockParams.exits,
},
options
)
}
34 changes: 33 additions & 1 deletion packages/block/src/header.ts
Expand Up @@ -64,6 +64,7 @@ export class BlockHeader {
public readonly blobGasUsed?: bigint
public readonly excessBlobGas?: bigint
public readonly parentBeaconBlockRoot?: Uint8Array
public readonly exitsRoot?: Uint8Array

public readonly common: Common

Expand Down Expand Up @@ -118,7 +119,8 @@ export class BlockHeader {
*/
public static fromValuesArray(values: BlockHeaderBytes, opts: BlockOptions = {}) {
const headerData = valuesArrayToHeaderData(values)
const { number, baseFeePerGas, excessBlobGas, blobGasUsed, parentBeaconBlockRoot } = headerData
const { number, baseFeePerGas, excessBlobGas, blobGasUsed, parentBeaconBlockRoot, exitsRoot } =
headerData
const header = BlockHeader.fromHeaderData(headerData, opts)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (header.common.isActivatedEIP(1559) && baseFeePerGas === undefined) {
Expand All @@ -139,6 +141,9 @@ export class BlockHeader {
if (header.common.isActivatedEIP(4788) && parentBeaconBlockRoot === undefined) {
throw new Error('invalid header. parentBeaconBlockRoot should be provided')
}
if (header.common.isActivatedEIP(7002) && exitsRoot === undefined) {
throw new Error('invalid header. exitsRoot should be provided')

Check warning on line 145 in packages/block/src/header.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/header.ts#L145

Added line #L145 was not covered by tests
}
return header
}
/**
Expand Down Expand Up @@ -224,6 +229,7 @@ export class BlockHeader {
blobGasUsed: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined,
excessBlobGas: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined,
parentBeaconBlockRoot: this.common.isActivatedEIP(4788) ? zeros(32) : undefined,
exitsRoot: this.common.isActivatedEIP(7002) ? zeros(32) : undefined,
}

const baseFeePerGas =
Expand All @@ -239,6 +245,8 @@ export class BlockHeader {
const parentBeaconBlockRoot =
toType(headerData.parentBeaconBlockRoot, TypeOutput.Uint8Array) ??
hardforkDefaults.parentBeaconBlockRoot
const exitsRoot =
toType(headerData.exitsRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.exitsRoot

if (!this.common.isActivatedEIP(1559) && baseFeePerGas !== undefined) {
throw new Error('A base fee for a block can only be set with EIP1559 being activated')
Expand Down Expand Up @@ -272,6 +280,10 @@ export class BlockHeader {
)
}

if (!this.common.isActivatedEIP(7200) && exitsRoot !== undefined) {
throw new Error('A exitsRoot for a header can only be provided with EIP7002 being activated')

Check warning on line 284 in packages/block/src/header.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/header.ts#L284

Added line #L284 was not covered by tests
}

this.parentHash = parentHash
this.uncleHash = uncleHash
this.coinbase = coinbase
Expand All @@ -293,6 +305,7 @@ export class BlockHeader {
this.blobGasUsed = blobGasUsed
this.excessBlobGas = excessBlobGas
this.parentBeaconBlockRoot = parentBeaconBlockRoot
this.exitsRoot = exitsRoot
this._genericFormatValidation()
this._validateDAOExtraData()

Expand Down Expand Up @@ -431,6 +444,19 @@ export class BlockHeader {
throw new Error(msg)
}
}

if (this.common.isActivatedEIP(7002) === true) {
if (this.exitsRoot === undefined) {
const msg = this._errorMsg('EIP7002 has no exitsRoot field')
throw new Error(msg)
}
if (this.exitsRoot.length !== 32) {
const msg = this._errorMsg(
`exitsRoot must be 32 bytes, received ${this.exitsRoot.length} bytes`
)
throw new Error(msg)
}
}

Check warning on line 459 in packages/block/src/header.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/header.ts#L449-L459

Added lines #L449 - L459 were not covered by tests
}

/**
Expand Down Expand Up @@ -721,6 +747,9 @@ export class BlockHeader {
if (this.common.isActivatedEIP(4788) === true) {
rawItems.push(this.parentBeaconBlockRoot!)
}
if (this.common.isActivatedEIP(7002)) {
rawItems.push(this.exitsRoot!)

Check warning on line 751 in packages/block/src/header.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/header.ts#L751

Added line #L751 was not covered by tests
}

return rawItems
}
Expand Down Expand Up @@ -990,6 +1019,9 @@ export class BlockHeader {
if (this.common.isActivatedEIP(4788) === true) {
jsonDict.parentBeaconBlockRoot = bytesToHex(this.parentBeaconBlockRoot!)
}
if (this.common.isActivatedEIP(7002)) {
jsonDict.exitsRoot = bytesToHex(this.exitsRoot!)
}

Check warning on line 1024 in packages/block/src/header.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/header.ts#L1023-L1024

Added lines #L1023 - L1024 were not covered by tests
return jsonDict
}

Expand Down
29 changes: 26 additions & 3 deletions packages/block/src/types.ts
Expand Up @@ -7,7 +7,10 @@ import type {
BytesLike,
DepositBytes,
DepositData,
ExitBytes,
ExitData,
JsonRpcDeposit,
JsonRpcExit,
JsonRpcWithdrawal,
PrefixedHexString,
WithdrawalBytes,
Expand Down Expand Up @@ -140,6 +143,7 @@ export interface HeaderData {
blobGasUsed?: BigIntLike
excessBlobGas?: BigIntLike
parentBeaconBlockRoot?: BytesLike
exitsRoot?: BytesLike
}

/**
Expand All @@ -161,10 +165,15 @@ export interface BlockData {
* EIP-6100: Deposits in EL (experimental)
*/
deposits?: Array<DepositData>
/**
* EIP-7002: Exits in EL (experimental)
*/
exits?: Array<ExitData>
}

export type WithdrawalsBytes = WithdrawalBytes[]
export type DepositsBytes = DepositBytes[]
export type ExitsBytes = ExitBytes[]
export type ExecutionWitnessBytes = Uint8Array

export type BlockBytes =
Expand All @@ -175,16 +184,26 @@ export type BlockBytes =
TransactionsBytes,
UncleHeadersBytes,
WithdrawalsBytes,
ExecutionWitnessBytes
DepositsBytes,
ExitsBytes
// The type here is the BlockBytes without 6800, but with 6110 + 7002
]
| [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes, DepositsBytes]
| [
BlockHeaderBytes,
TransactionsBytes,
UncleHeadersBytes,
WithdrawalsBytes,
DepositsBytes,
ExecutionWitnessBytes
ExitBytes,
ExecutionWitnessBytes // This includes 6800 with 6110 + 7002
]
| [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes, DepositsBytes]
| [
BlockHeaderBytes,
TransactionsBytes,
UncleHeadersBytes,
WithdrawalsBytes,
ExecutionWitnessBytes // This only includes 6800 sso without 6110 + 7002
]

/**
Expand Down Expand Up @@ -215,6 +234,7 @@ export interface JsonBlock {
uncleHeaders?: JsonHeader[]
withdrawals?: JsonRpcWithdrawal[]
deposits?: JsonRpcDeposit[]
exits?: JsonRpcExit[]
executionWitness?: VerkleExecutionWitness | null
}

Expand Down Expand Up @@ -243,6 +263,7 @@ export interface JsonHeader {
blobGasUsed?: string
excessBlobGas?: string
parentBeaconBlockRoot?: string
exitsRoot?: string
}

/*
Expand Down Expand Up @@ -274,6 +295,8 @@ export interface JsonRpcBlock {
withdrawalsRoot?: string // If EIP-4895 is enabled for this block, the root of the withdrawal trie of the block.
deposits?: Array<JsonRpcDeposit> // If EIP-6110 is enabled for this block, array of deposits
depositsRoot?: string // If EIP-6110 is enabled for this block, the root of the deposit trie of the block.
exits?: Array<JsonRpcExit> // If EIP-7002 is enabled for this block, array of exits
exitsRoot?: string // If EIP-7002 is enabled for this block, the root of the exits trie of the block.
blobGasUsed?: string // If EIP-4844 is enabled for this block, returns the blob gas used for the block
excessBlobGas?: string // If EIP-4844 is enabled for this block, returns the excess blob gas for the block
parentBeaconBlockRoot?: string // If EIP-4788 is enabled for this block, returns parent beacon block root
Expand Down

0 comments on commit 3866a3a

Please sign in to comment.