Skip to content

Commit

Permalink
dev changes for eip 7742 newpayload and fcu all through the stack
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Dec 7, 2024
1 parent caa9e1c commit 3bd746f
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 37 deletions.
4 changes: 4 additions & 0 deletions packages/block/src/from-beacon-payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type BeaconPayloadJSON = {
excess_blob_gas?: NumericString
parent_beacon_block_root?: PrefixedHexString
requests_hash?: PrefixedHexString
target_blobs_per_block?: PrefixedHexString
// the casing of VerkleExecutionWitness remains same camel case for now
execution_witness?: VerkleExecutionWitness
}
Expand Down Expand Up @@ -135,6 +136,9 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJSON): E
if (payload.requests_hash !== undefined && payload.requests_hash !== null) {
executionPayload.requestsHash = payload.requests_hash
}
if (payload.target_blobs_per_block !== undefined && payload.target_blobs_per_block !== null) {
executionPayload.targetBlobsPerBlock = payload.target_blobs_per_block
}

Check warning on line 141 in packages/block/src/from-beacon-payload.ts

View check run for this annotation

Codecov / codecov/patch

packages/block/src/from-beacon-payload.ts#L140-L141

Added lines #L140 - L141 were not covered by tests

if (payload.execution_witness !== undefined && payload.execution_witness !== null) {
// the casing structure in payload could be camel case or snake depending upon the CL
Expand Down
12 changes: 11 additions & 1 deletion packages/block/src/header/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class BlockHeader {
public readonly excessBlobGas?: bigint
public readonly parentBeaconBlockRoot?: Uint8Array
public readonly requestsHash?: Uint8Array
public readonly targetBlobsPerBlock?: bigint

public readonly common: Common

Expand Down Expand Up @@ -165,6 +166,7 @@ export class BlockHeader {
// Note: as of devnet-4 we stub the null SHA256 hash, but for devnet5 this will actually
// be the correct hash for empty requests.
requestsHash: this.common.isActivatedEIP(7685) ? SHA256_NULL : undefined,
targetBlobsPerBlock: this.common.isActivatedEIP(7442)?this.common.param('targetBlobGasPerBlock'):undefined,
}

const baseFeePerGas =
Expand All @@ -180,6 +182,7 @@ export class BlockHeader {
hardforkDefaults.parentBeaconBlockRoot
const requestsHash =
toType(headerData.requestsHash, TypeOutput.Uint8Array) ?? hardforkDefaults.requestsHash
const targetBlobsPerBlock = toType(headerData.targetBlobsPerBlock, TypeOutput.BigInt) ?? hardforkDefaults.targetBlobsPerBlock

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 @@ -211,6 +214,10 @@ export class BlockHeader {
throw new Error('requestsHash can only be provided with EIP 7685 activated')
}

if(!this.common.isActivatedEIP(7442) && targetBlobsPerBlock!==undefined){
throw new Error('targetBlobsPerBlock can only be provided with EIP 7742 activated')

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

View check run for this annotation

Codecov / codecov/patch

packages/block/src/header/header.ts#L218

Added line #L218 was not covered by tests
}

this.parentHash = parentHash
this.uncleHash = uncleHash
this.coinbase = coinbase
Expand All @@ -232,6 +239,7 @@ export class BlockHeader {
this.excessBlobGas = excessBlobGas
this.parentBeaconBlockRoot = parentBeaconBlockRoot
this.requestsHash = requestsHash
this.targetBlobsPerBlock = targetBlobsPerBlock
this._genericFormatValidation()
this._validateDAOExtraData()

Expand Down Expand Up @@ -567,7 +575,9 @@ export class BlockHeader {
public calcNextExcessBlobGas(): bigint {
// The validation of the fields and 4844 activation is already taken care in BlockHeader constructor
const targetGasConsumed = (this.excessBlobGas ?? BIGINT_0) + (this.blobGasUsed ?? BIGINT_0)
const targetBlobGasPerBlock = this.common.param('targetBlobGasPerBlock')

const blobGasPerBlob = this.common.param('blobGasPerBlob')
const targetBlobGasPerBlock = this.targetBlobsPerBlock!==undefined? this.targetBlobsPerBlock * blobGasPerBlob : this.common.param('targetBlobGasPerBlock')

if (targetGasConsumed <= targetBlobGasPerBlock) {
return BIGINT_0
Expand Down
4 changes: 4 additions & 0 deletions packages/block/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export interface HeaderData {
excessBlobGas?: BigIntLike
parentBeaconBlockRoot?: BytesLike
requestsHash?: BytesLike
targetBlobsPerBlock?: BigIntLike
}

/**
Expand Down Expand Up @@ -192,6 +193,7 @@ export interface JSONHeader {
excessBlobGas?: PrefixedHexString
parentBeaconBlockRoot?: PrefixedHexString
requestsHash?: PrefixedHexString
targetBlobsPerBlock?: PrefixedHexString
}

/*
Expand Down Expand Up @@ -226,6 +228,7 @@ export interface JSONRPCBlock {
parentBeaconBlockRoot?: PrefixedHexString // If EIP-4788 is enabled for this block, returns parent beacon block root
executionWitness?: VerkleExecutionWitness | null // If Verkle is enabled for this block
requestsHash?: PrefixedHexString // If EIP-7685 is enabled for this block, returns the requests root
targetBlobsPerBlock?: PrefixedHexString // if EIP-7742 is enabled for this block, returns the target blobs per block
}

export type WithdrawalV1 = {
Expand Down Expand Up @@ -256,6 +259,7 @@ export type ExecutionPayload = {
excessBlobGas?: PrefixedHexString // QUANTITY, 64 Bits
parentBeaconBlockRoot?: PrefixedHexString // QUANTITY, 64 Bits
requestsHash?: PrefixedHexString
targetBlobsPerBlock?: PrefixedHexString // QUANTITY 64 Bits
// VerkleExecutionWitness is already a hex serialized object
executionWitness?: VerkleExecutionWitness | null // QUANTITY, 64 Bits, null implies not available
}
59 changes: 35 additions & 24 deletions packages/client/src/miner/pendingBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import type { Config } from '../config.js'
import type { TxPool } from '../service/txpool.js'
import type { Block, HeaderData } from '@ethereumjs/block'
import type { TypedTransaction } from '@ethereumjs/tx'
import type { CLRequest, CLRequestType, PrefixedHexString, WithdrawalData } from '@ethereumjs/util'
import type { BigIntLike, CLRequest, CLRequestType, PrefixedHexString ,
WithdrawalData} from '@ethereumjs/util'
import type { BlockBuilder, TxReceipt, VM } from '@ethereumjs/vm'

interface PendingBlockOpts {
Expand Down Expand Up @@ -57,12 +58,15 @@ enum AddTxResult {
RemovedByErrors,
}

type CLData = {withdrawals?: WithdrawalData[],maxBlobsPerBlock?:BigIntLike}

export class PendingBlock {
config: Config
txPool: TxPool

pendingPayloads: Map<string, BlockBuilder> = new Map()
blobsBundles: Map<string, BlobsBundle> = new Map()
maxBlobsPerBlock?: number

private skipHardForkValidation?: boolean

Expand Down Expand Up @@ -97,7 +101,7 @@ export class PendingBlock {
vm: VM,
parentBlock: Block,
headerData: Partial<HeaderData> = {},
withdrawals?: WithdrawalData[],
{withdrawals,maxBlobsPerBlock}:CLData={},
) {
const number = parentBlock.header.number + BIGINT_1
const { timestamp, mixHash, parentBeaconBlockRoot, coinbase } = headerData
Expand All @@ -116,6 +120,19 @@ export class PendingBlock {
gasLimit = gasLimit * BIGINT_2
}

// Get if and how many blobs are allowed in the tx
if(vm.common.isActivatedEIP(7742)){
this.maxBlobsPerBlock = toType(maxBlobsPerBlock!, TypeOutput.Number)
}else{
if (vm.common.isActivatedEIP(4844)) {
const blobGasLimit = vm.common.param('maxblobGasPerBlock')
const blobGasPerBlob = vm.common.param('blobGasPerBlob')
this.maxBlobsPerBlock = Number(blobGasLimit / blobGasPerBlob)
} else {
this.maxBlobsPerBlock = 0
}
}

// payload is uniquely defined by timestamp, parent and mixHash, gasLimit can also be
// potentially included in the fcU in future and can be safely added in uniqueness calc
const timestampBuf = bigIntToUnpaddedBytes(toType(timestamp ?? 0, TypeOutput.BigInt))
Expand All @@ -140,6 +157,15 @@ export class PendingBlock {
}
withdrawalsBuf = concatBytes(...withdrawalsBufTemp)
}
const targetBlobsPerBlock = toType(headerData.targetBlobsPerBlock??0, TypeOutput.BigInt)

// some validations for correct target and max
if(targetBlobsPerBlock > this.maxBlobsPerBlock){
throw Error(`Invalid targetBlobsPerBlock=${targetBlobsPerBlock} > maxBlobsPerBlock=${this.maxBlobsPerBlock}`)
}

const targetBlobsPerBlockBuf = bigIntToUnpaddedBytes(targetBlobsPerBlock)
const maxBlobsPerBlockBuf =bigIntToUnpaddedBytes(BigInt(this.maxBlobsPerBlock))

const keccakFunction = this.config.chainCommon.customCrypto.keccak256 ?? keccak256

Expand All @@ -153,6 +179,8 @@ export class PendingBlock {
parentBeaconBlockRootBuf,
coinbaseBuf,
withdrawalsBuf,
targetBlobsPerBlockBuf,
maxBlobsPerBlockBuf,
),
).subarray(0, 8),
)
Expand Down Expand Up @@ -189,19 +217,11 @@ export class PendingBlock {

this.pendingPayloads.set(payloadId, builder)

// Get if and how many blobs are allowed in the tx
let allowedBlobs
if (vm.common.isActivatedEIP(4844)) {
const blobGasLimit = vm.common.param('maxblobGasPerBlock')
const blobGasPerBlob = vm.common.param('blobGasPerBlob')
allowedBlobs = Number(blobGasLimit / blobGasPerBlob)
} else {
allowedBlobs = 0
}

// Add current txs in pool
const txs = await this.txPool.txsByPriceAndNonce(vm, {
baseFee: baseFeePerGas,
allowedBlobs,
allowedBlobs:this.maxBlobsPerBlock!,
})
this.config.logger.info(
`Pending: Assembling block from ${txs.length} eligible txs (baseFee: ${baseFeePerGas})`,
Expand Down Expand Up @@ -265,23 +285,14 @@ export class PendingBlock {
]
}
const { vm, headerData } = builder as unknown as { vm: VM; headerData: HeaderData }

// get the number of blobs that can be further added
let allowedBlobs
if (vm.common.isActivatedEIP(4844)) {
const bundle = this.blobsBundles.get(payloadId) ?? { blobs: [], commitments: [], proofs: [] }
const blobGasLimit = vm.common.param('maxblobGasPerBlock')
const blobGasPerBlob = vm.common.param('blobGasPerBlob')
allowedBlobs = Number(blobGasLimit / blobGasPerBlob) - bundle.blobs.length
} else {
allowedBlobs = 0
}
// get current bundle
const currentBundle = this.blobsBundles.get(payloadId) ?? { blobs: [], commitments: [], proofs: [] }

// Add new txs that the pool received
const txs = (
await this.txPool.txsByPriceAndNonce(vm, {
baseFee: headerData.baseFeePerGas! as bigint,
allowedBlobs,
allowedBlobs:this.maxBlobsPerBlock!-currentBundle.blobs.length,
})
).filter(
(tx) =>
Expand Down
58 changes: 51 additions & 7 deletions packages/client/src/rpc/modules/engine/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
payloadAttributesFieldValidatorsV1,
payloadAttributesFieldValidatorsV2,
payloadAttributesFieldValidatorsV3,
payloadAttributesFieldValidatorsV4,
} from './validators.js'

import type { Chain } from '../../../blockchain/index.js'
Expand All @@ -63,6 +64,8 @@ import type {
PayloadAttributesV1,
PayloadAttributesV2,
PayloadAttributesV3,
PayloadAttributesV4,
Uint64,
} from './types.js'
import type { Block, ExecutionPayload } from '@ethereumjs/block'
import type { PrefixedHexString } from '@ethereumjs/util'
Expand Down Expand Up @@ -210,8 +213,9 @@ export class Engine {
[validators.array(validators.bytes32)],
[validators.bytes32],
[validators.array(validators.hex)],
[validators.uint64],
],
['executionPayload', 'blobVersionedHashes', 'parentBeaconBlockRoot', 'executionRequests'],
['executionPayload', 'blobVersionedHashes', 'parentBeaconBlockRoot', 'executionRequests', 'targetBlobsPerBlock'],
),
([payload], response) => this.connectionManager.lastNewPayload({ payload, response }),
)
Expand Down Expand Up @@ -255,6 +259,13 @@ export class Engine {
]),
forkchoiceUpdatedResponseCMHandler,
)
this.forkchoiceUpdatedV4 = cmMiddleware(
middleware(callWithStackTrace(this.forkchoiceUpdatedV4.bind(this), this._rpcDebug), 1, [
[validators.object(forkchoiceFieldValidators)],
[validators.optional(validators.object(payloadAttributesFieldValidatorsV4))],
]),
forkchoiceUpdatedResponseCMHandler,
)

/**
* getPayload
Expand Down Expand Up @@ -346,10 +357,11 @@ export class Engine {
ExecutionPayload,
(Bytes32[] | null)?,
(Bytes32 | null)?,
(PrefixedHexString[] | null)?,
(Bytes32[] | null)?,
(Uint64 | null)?,
],
): Promise<PayloadStatusV1> {
const [payload, blobVersionedHashes, parentBeaconBlockRoot, executionRequests] = params
const [payload, blobVersionedHashes, parentBeaconBlockRoot, executionRequests, targetBlobsPerBlock] = params
if (this.config.synchronized) {
this.connectionManager.newPayloadLog()
}
Expand All @@ -372,6 +384,7 @@ export class Engine {
parentBeaconBlockRoot: parentBeaconBlockRoot ?? undefined,
blobVersionedHashes: blobVersionedHashes ?? undefined,
executionRequests: executionRequests ?? undefined,
targetBlobsPerBlock: targetBlobsPerBlock ?? undefined,
},
this.chain,
this.chainCache,
Expand Down Expand Up @@ -822,7 +835,7 @@ export class Engine {
}

async newPayloadV4(
params: [ExecutionPayloadV3, Bytes32[], Bytes32, Bytes32[]],
params: [ExecutionPayloadV3, Bytes32[], Bytes32, Bytes32[], Uint64],
): Promise<PayloadStatusV1> {
const pragueTimestamp = this.chain.config.chainCommon.hardforkTimestamp(Hardfork.Prague)
const ts = parseInt(params[0].timestamp)
Expand Down Expand Up @@ -1107,7 +1120,7 @@ export class Engine {
let validResponse
// If payloadAttributes is present, start building block and return payloadId
if (payloadAttributes) {
const { timestamp, prevRandao, suggestedFeeRecipient, withdrawals, parentBeaconBlockRoot } =
const { timestamp, prevRandao, suggestedFeeRecipient, withdrawals, parentBeaconBlockRoot, targetBlobsPerBlock, maxBlobsPerBlock } =
payloadAttributes
const timestampBigInt = BigInt(timestamp)

Expand All @@ -1129,8 +1142,9 @@ export class Engine {
mixHash: prevRandao,
coinbase: suggestedFeeRecipient,
parentBeaconBlockRoot,
targetBlobsPerBlock,
},
withdrawals,
{withdrawals, maxBlobsPerBlock},
)
const latestValidHash = await validHash(headBlock.hash(), this.chain, this.chainCache)
const payloadStatus = { status: Status.VALID, latestValidHash, validationError: null }
Expand Down Expand Up @@ -1276,7 +1290,37 @@ export class Engine {
this.chain.config.chainCommon,
3,
Hardfork.Cancun,
// this could be valid post cancun as well, if not then update the valid till hf here
Hardfork.Prague,
BigInt(payloadAttributes.timestamp),
)
}

return this.forkchoiceUpdated(params)
}

private async forkchoiceUpdatedV4(
params: [
forkchoiceState: ForkchoiceStateV1,
payloadAttributes: PayloadAttributesV4 | undefined,
],
): Promise<ForkchoiceResponseV1 & { headBlock?: Block }> {
const payloadAttributes = params[1]
if (payloadAttributes !== undefined && payloadAttributes !== null) {
if (
Object.values(payloadAttributes).filter((attr) => attr !== null && attr !== undefined)
.length > 7
) {
throw {
code: INVALID_PARAMS,
message: 'PayloadAttributesV4 MUST be used for forkchoiceUpdatedV4',
}
}

validateHardforkRange(
this.chain.config.chainCommon,
4,
Hardfork.Prague,
// this could be valid post prague as well, if not then update the valid till hf here
null,
BigInt(payloadAttributes.timestamp),
)
Expand Down
3 changes: 3 additions & 0 deletions packages/client/src/rpc/modules/engine/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ export type PayloadAttributes = {
// add higher version fields as optionals to make it easy for typescript
withdrawals?: WithdrawalV1[]
parentBeaconBlockRoot?: Bytes32
targetBlobsPerBlock?: Uint64
maxBlobsPerBlock?: Uint64
}

export type PayloadAttributesV1 = Omit<PayloadAttributes, 'withdrawals' | 'parentBeaconBlockRoot'>
export type PayloadAttributesV2 = PayloadAttributesV1 & { withdrawals: WithdrawalV1[] }
export type PayloadAttributesV3 = PayloadAttributesV2 & { parentBeaconBlockRoot: Bytes32 }
export type PayloadAttributesV4 = PayloadAttributesV2 & { targetBlobsPerBlock: Uint64, maxBlobsPerBlock: Uint64}

export type PayloadStatusV1 = {
status: Status
Expand Down
Loading

0 comments on commit 3bd746f

Please sign in to comment.