Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement pectra devnet-5 spec #3807

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/block/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ export function genRequestsRoot(

let flatRequests = new Uint8Array()
for (const req of requests) {
flatRequests = concatBytes(flatRequests, sha256Function(req.bytes))
if (req.bytes.length > 1) {
// Only append requests if they have content
flatRequests = concatBytes(flatRequests, sha256Function(req.bytes))
}
}

return sha256Function(flatRequests)
Expand Down
9 changes: 9 additions & 0 deletions packages/block/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,13 @@ export const paramsBlock: ParamsDict = {
// pow
difficultyBombDelay: 11400000, // the amount of blocks to delay the difficulty bomb with
},
/**
. * Blob throughput increase
. */
7691: {
// gasConfig
targetBlobGasPerBlock: 786432, // The target blob gas consumed per block
maxblobGasPerBlock: 1179648, // The max blob gas allowable per block
blobGasPriceUpdateFraction: 5007716, // The denominator used in the exponential when calculating a blob gas price
},
}
13 changes: 12 additions & 1 deletion packages/client/src/rpc/modules/engine/util/getPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,20 @@ export const blockToExecutionPayload = (
// ethereumjs does not provide any transaction censoring detection (yet) to suggest
// overriding builder/mev-boost blocks
const shouldOverrideBuilder = false

let executionRequests = undefined
if (requests !== undefined) {
executionRequests = []
for (const request of requests) {
if (request.bytes.length > 1) {
executionRequests.push(bytesToHex(request.bytes))
}
}
}

return {
executionPayload,
executionRequests: requests?.map((req) => bytesToHex(req.data)),
executionRequests,
blockValue: bigIntToHex(value),
blobsBundle,
shouldOverrideBuilder,
Expand Down
49 changes: 27 additions & 22 deletions packages/client/src/rpc/modules/engine/util/newPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,30 +57,35 @@ export const validateAndGen7685RequestsHash = (
common: Common,
executionRequests: PrefixedHexString[],
): PrefixedHexString => {
let validationError: string | null = null

const requests: CLRequest<CLRequestType>[] = []
let requestIndex = 0
if (common.isActivatedEIP(6110)) {
requests.push(new CLRequest(CLRequestType.Deposit, hexToBytes(executionRequests[requestIndex])))
requestIndex++
}
if (common.isActivatedEIP(7002)) {
requests.push(
new CLRequest(CLRequestType.Withdrawal, hexToBytes(executionRequests[requestIndex])),
)
requestIndex++
}
if (common.isActivatedEIP(7251)) {
requests.push(
new CLRequest(CLRequestType.Consolidation, hexToBytes(executionRequests[requestIndex])),
)
requestIndex++
}

if (requestIndex !== executionRequests.length) {
validationError = `Invalid executionRequests=${executionRequests.length} expected=${requestIndex}`
throw validationError
for (const request of executionRequests) {
const bytes = hexToBytes(request)
if (bytes.length === 0) {
throw new Error('Got a request without a request-identifier')
}
switch (bytes[0]) {
case 0:
if (!common.isActivatedEIP(6110)) {
throw new Error(`Deposit requests are not active`)
}
requests.push(new CLRequest(CLRequestType.Deposit, bytes.slice(1)))
break
case 1:
if (!common.isActivatedEIP(7002)) {
throw new Error(`Withdrawal requests are not active`)
}
requests.push(new CLRequest(CLRequestType.Withdrawal, bytes.slice(1)))
break
case 2:
if (!common.isActivatedEIP(7251)) {
throw new Error(`Consolidation requests are not active`)
}
requests.push(new CLRequest(CLRequestType.Consolidation, bytes.slice(1)))
break
default:
throw new Error(`Unknown request identifier: got ${bytes[0]}`)
}
}

const sha256Function = common.customCrypto.sha256 ?? sha256
Expand Down
19 changes: 15 additions & 4 deletions packages/client/test/rpc/engine/newPayloadV4.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
excessBlobGas: '0x0',
parentHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2',
stateRoot: '0x7aa6e46df1f78988a3141b5e7da8abee78d1daca175f43fe8866b2d1bf8d8ef8',
blockHash: '0x9a5903d803e6e7c3631cd76cb7279f93d7facc995c53eaffadf4e225504b18eb',
blockHash: '0xece8d273a76238c4c9e4e28cbd301d40b04cb21242e83960ca688324e0483fd8',
}

const oldMethods = ['engine_newPayloadV1', 'engine_newPayloadV2', 'engine_newPayloadV3']
Expand All @@ -180,8 +180,8 @@
assert.ok(res.error.message.includes(expectedError))
}

res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, ['0x', '0x', '0x']])
res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, []])
assert.equal(res.result.status, 'VALID')

Check failure on line 184 in packages/client/test/rpc/engine/newPayloadV4.spec.ts

View workflow job for this annotation

GitHub Actions / client / test-client

test/rpc/engine/newPayloadV4.spec.ts > engine_newPayloadV4: call with executionPayloadV4 > valid data

AssertionError: expected 'INVALID' to equal 'VALID' Expected: "VALID" Received: "INVALID" ❯ test/rpc/engine/newPayloadV4.spec.ts:184:12

res = await rpc.request('engine_forkchoiceUpdatedV3', validPayload)
const payloadId = res.result.payloadId
Expand All @@ -201,9 +201,20 @@

res = await rpc.request('engine_getPayloadV4', [payloadId])
const { executionPayload, executionRequests } = res.result

assert.ok(
executionRequests?.length === 1,
'executionRequests should have the deposit request, and should exclude the other requests (these are empty)',
)

const depositRequestBytes = hexToBytes(executionRequests[0])
assert.ok(
depositRequestBytes[0] === 0x00,
'deposit request byte 0 is the deposit request identifier byte (0x00)',
)
assert.ok(
executionRequests?.length === 3,
'executionRequests should have 3 entries for each request type',
depositRequestBytes.length > 1,
'deposit request includes data (and is thus not empty)',
)

res = await rpc.request(method, [
Expand Down
18 changes: 18 additions & 0 deletions packages/common/src/eips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,15 @@ export const eipsDict: EIPsDict = {
*/
requiredEIPs: [3540, 3541, 3670],
},
/**
* Description : Increase calldata cost to reduce maximum block size
* URL : https://github.com/ethereum/EIPs/blob/da2a86bf15044416e8eb0301c9bdb8d561feeb32/EIPS/eip-7623.md
* Status : Review
*/
7623: {
minimumHardfork: Hardfork.Chainstart,
requiredEIPs: [],
},
/**
* Description : General purpose execution layer requests
* URL : https://eips.ethereum.org/EIPS/eip-7685
Expand All @@ -415,6 +424,15 @@ export const eipsDict: EIPsDict = {
minimumHardfork: Hardfork.Cancun,
requiredEIPs: [3675],
},
/**
* Description : Blob throughput increase
* URL : https://eips.ethereum.org/EIPS/eip-7691
* Status : Review
*/
7691: {
minimumHardfork: Hardfork.Paris,
requiredEIPs: [4844],
},
/**
* Description : EVM Object Format (EOFv1) Meta
* URL : https://github.com/ethereum/EIPs/blob/4153e95befd0264082de3c4c2fe3a85cc74d3152/EIPS/eip-7692.md
Expand Down
4 changes: 3 additions & 1 deletion packages/common/src/hardforks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ export const hardforksDict: HardforksDict = {
* Status : Final
*/
prague: {
eips: [2537, 2935, 6110, 7002, 7251, 7685, 7702], // This is current prague without EOF
// TODO update this accordingly to the right devnet setup
//eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698], // This is EOF-only
eips: [2537, 2935, 6110, 7002, 7251, 7623, 7685, 7691, 7702], // This is current prague without EOF
},
osaka: {
eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698], // These are the EOF EIPs
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export class EVM implements EVMInterface {
const supportedEIPs = [
663, 1153, 1559, 2537, 2565, 2718, 2929, 2930, 2935, 3198, 3529, 3540, 3541, 3607, 3651, 3670,
3855, 3860, 4200, 4399, 4750, 4788, 4844, 4895, 5133, 5450, 5656, 6110, 6206, 6780, 6800,
7002, 7069, 7251, 7480, 7516, 7620, 7685, 7692, 7698, 7702, 7709,
7002, 7069, 7251, 7480, 7516, 7620, 7685, 7691, 7692, 7698, 7702, 7709,
]

for (const eip of this.common.eips()) {
Expand Down
42 changes: 31 additions & 11 deletions packages/evm/src/opcodes/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
concatBytes,
equalsBytes,
getVerkleTreeIndicesForStorageSlot,
hexToBytes,
setLengthLeft,
} from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak.js'
Expand Down Expand Up @@ -61,16 +62,40 @@ export interface AsyncOpHandler {

export type OpHandler = SyncOpHandler | AsyncOpHandler

// TODO: verify that this is the correct designator
// The PR https://github.com/ethereum/EIPs/pull/8969 has two definitions of the
// designator: the original (0xef0100) and the designator added in the changes (0xef01)
const eip7702Designator = hexToBytes('0xef01')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just confirming my thinking. These only get computed once at run time, right? They aren't recomputed every single time we access this code are they?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only computed once! Can also move it somewhere else like types.ts.

const eip7702HashBigInt = bytesToBigInt(keccak256(eip7702Designator))

function getEIP7702DelegatedAddress(code: Uint8Array) {
if (equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) {
return new Address(code.slice(3, 24))
}
}

async function eip7702CodeCheck(runState: RunState, code: Uint8Array) {
/**
* This method performs checks to transform the code which the EVM observes regarding EIP-7702.
* If the code is 7702-delegated code, it will retrieve the code of the designated address
* in case of an executable operation (`isReadOperation` == false), or the 7702 designator
* code in case of a read operation
* @param runState
* @param code
* @param isReadOperation Boolean to determine if the target code is meant to be read or executed (default: `false`)
* @returns
*/
async function eip7702CodeCheck(
runState: RunState,
code: Uint8Array,
isReadOperation: boolean = false,
) {
const address = getEIP7702DelegatedAddress(code)
if (address !== undefined) {
return runState.stateManager.getCode(address)
if (isReadOperation) {
return eip7702Designator
} else {
return runState.stateManager.getCode(address)
}
}

return code
Expand Down Expand Up @@ -538,7 +563,7 @@ export const handlers: Map<number, OpHandler> = new Map([
runState.stack.push(BigInt(EOFBYTES.length))
return
} else if (common.isActivatedEIP(7702)) {
code = await eip7702CodeCheck(runState, code)
code = await eip7702CodeCheck(runState, code, true)
}

const size = BigInt(code.length)
Expand All @@ -560,7 +585,7 @@ export const handlers: Map<number, OpHandler> = new Map([
// In legacy code, the target code is treated as to be "EOFBYTES" code
code = EOFBYTES
} else if (common.isActivatedEIP(7702)) {
code = await eip7702CodeCheck(runState, code)
code = await eip7702CodeCheck(runState, code, true)
}

const data = getDataSlice(code, codeOffset, dataLength)
Expand All @@ -587,13 +612,8 @@ export const handlers: Map<number, OpHandler> = new Map([
} else if (common.isActivatedEIP(7702)) {
const possibleDelegatedAddress = getEIP7702DelegatedAddress(code)
if (possibleDelegatedAddress !== undefined) {
const account = await runState.stateManager.getAccount(possibleDelegatedAddress)
if (!account || account.isEmpty()) {
runState.stack.push(BIGINT_0)
return
}

runState.stack.push(BigInt(bytesToHex(account.codeHash)))
// The account is delegated by an EIP-7702 tx. Push the EIP-7702 designator hash to the stack
runState.stack.push(eip7702HashBigInt)
return
}
}
Expand Down
12 changes: 6 additions & 6 deletions packages/evm/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,14 @@ export const paramsEVM: ParamsDict = {
*/
2537: {
// gasPrices
bls12381G1AddGas: 500, // Gas cost of a single BLS12-381 G1 addition precompile-call
bls12381G1AddGas: 375, // Gas cost of a single BLS12-381 G1 addition precompile-call
bls12381G1MulGas: 12000, // Gas cost of a single BLS12-381 G1 multiplication precompile-call
bls12381G2AddGas: 800, // Gas cost of a single BLS12-381 G2 addition precompile-call
bls12381G2MulGas: 45000, // Gas cost of a single BLS12-381 G2 multiplication precompile-call
bls12381PairingBaseGas: 65000, // Base gas cost of BLS12-381 pairing check
bls12381PairingPerPairGas: 43000, // Per-pair gas cost of BLS12-381 pairing check
bls12381G2AddGas: 600, // Gas cost of a single BLS12-381 G2 addition precompile-call
bls12381G2MulGas: 22500, // Gas cost of a single BLS12-381 G2 multiplication precompile-call
bls12381PairingBaseGas: 37700, // Base gas cost of BLS12-381 pairing check
bls12381PairingPerPairGas: 32600, // Per-pair gas cost of BLS12-381 pairing check
bls12381MapG1Gas: 5500, // Gas cost of BLS12-381 map field element to G1
bls12381MapG2Gas: 75000, // Gas cost of BLS12-381 map field element to G2
bls12381MapG2Gas: 23800, // Gas cost of BLS12-381 map field element to G2
},
/**
. * Gas cost increases for state access opcodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import { bytesToHex } from '@ethereumjs/util'
import { EvmErrorResult, OOGResult } from '../evm.js'
import { ERROR, EvmError } from '../exceptions.js'

import { leading16ZeroBytesCheck, msmGasUsed } from './bls12_381/index.js'
import {
BLS_GAS_DISCOUNT_PAIRS_G1,
leading16ZeroBytesCheck,
msmGasUsed,
} from './bls12_381/index.js'
import { gasLimitCheck, moduloLengthCheck } from './util.js'

import { getPrecompileName } from './index.js'

import type { EVMBLSInterface, ExecResult } from '../types.js'
import type { PrecompileInput } from './types.js'

export async function precompile0d(opts: PrecompileInput): Promise<ExecResult> {
export async function precompile0c(opts: PrecompileInput): Promise<ExecResult> {
const pName = getPrecompileName('0d')
const bls = (<any>opts._EVM)._bls! as EVMBLSInterface

Expand All @@ -29,7 +33,7 @@ export async function precompile0d(opts: PrecompileInput): Promise<ExecResult> {
// validation (same for g2msm)
const numPairs = Math.floor(inputData.length / 160)
const gasUsedPerPair = opts.common.paramByEIP('bls12381G1MulGas', 2537) ?? BigInt(0)
const gasUsed = msmGasUsed(numPairs, gasUsedPerPair)
const gasUsed = msmGasUsed(numPairs, gasUsedPerPair, BLS_GAS_DISCOUNT_PAIRS_G1)

if (!gasLimitCheck(opts, gasUsed, pName)) {
return OOGResult(opts.gasLimit)
Expand Down
55 changes: 0 additions & 55 deletions packages/evm/src/precompiles/0c-bls12-g1mul.ts

This file was deleted.

Loading
Loading