Skip to content

Commit

Permalink
Merge pull request #4843 from NomicFoundation/add-parentBeaconBlockRo…
Browse files Browse the repository at this point in the history
…ot-and-blob-properties

Add parent beacon block root and blob properties
  • Loading branch information
fvictorio authored Feb 13, 2024
2 parents 88238c6 + cf22ac8 commit 5ba1b45
Show file tree
Hide file tree
Showing 15 changed files with 850 additions and 204 deletions.
11 changes: 11 additions & 0 deletions packages/hardhat-core/src/internal/core/config/default-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,17 @@ export const defaultHardhatNetworkParams: Omit<
]),
},
],
[
11155111, // sepolia
{
hardforkHistory: new Map([
[HardforkName.GRAY_GLACIER, 0],
[HardforkName.MERGE, 1_450_409],
[HardforkName.SHANGHAI, 2_990_908],
[HardforkName.CANCUN, 5_187_023],
]),
},
],
]),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const baseBlockResponse = {
baseFeePerGas: optional(rpcQuantity),
withdrawals: optional(t.array(rpcWithdrawalItem)),
withdrawalsRoot: optional(rpcHash),
parentBeaconBlockRoot: optional(rpcHash),
blobGasUsed: optional(rpcQuantity),
excessBlobGas: optional(rpcQuantity),
};

export type RpcBlock = t.TypeOf<typeof rpcBlock>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,12 @@ export class ForkBlockchain
if (rpcBlock.baseFeePerGas === undefined) {
common.setHardfork("berlin");
} else if (rpcBlock.withdrawals === undefined) {
common.setHardfork("merge");
} else {
// ethereumjs uses this name for the merge hardfork
common.setHardfork("mergeForkIdTransition");
} else if (rpcBlock.parentBeaconBlockRoot === undefined) {
common.setHardfork("shanghai");
} else {
common.setHardfork("cancun");
}

// we don't include the transactions to add our own custom tx objects,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ export class ForkStateManager implements EVMStateManagerInterface {
get(address: Address, key: Uint8Array): Promise<Uint8Array>;
clear(): void;
} = {
get: async (_address: Address, _key: Uint8Array): Promise<Uint8Array> => {
throw new Error("Method not implemented.");
get: async (address: Address, key: Uint8Array): Promise<Uint8Array> => {
return this.getOriginalContractStorage(address, key);
},
clear: (): void => {
throw new Error("Method not implemented.");
this.clearOriginalStorageCache();
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export function rpcToBlockData(rpcBlock: RpcBlockWithTransactions): BlockData {
nonce: rpcBlock.nonce,
baseFeePerGas: rpcBlock.baseFeePerGas,
withdrawalsRoot: rpcBlock.withdrawalsRoot,
parentBeaconBlockRoot: rpcBlock.parentBeaconBlockRoot,
blobGasUsed: rpcBlock.blobGasUsed,
excessBlobGas: rpcBlock.excessBlobGas,
},
transactions: rpcBlock.transactions.map(rpcToTxData),
withdrawals: rpcBlock.withdrawals,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export interface Snapshot {
userProvidedNextBlockBaseFeePerGas: bigint | undefined;
coinbase: string;
mixHashGenerator: RandomBufferGenerator;
parentBeaconBlockRootGenerator: RandomBufferGenerator;
}

export type SendTransactionResult =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ import { RandomBufferGenerator } from "./utils/random";

type ExecResult = EVMResult["execResult"];

const BEACON_ROOT_ADDRESS = "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02";
const BEACON_ROOT_BYTECODE =
"0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500";

const log = debug("hardhat:core:hardhat-network:node");

/* eslint-disable @nomicfoundation/hardhat-internal-rules/only-hardhat-error */
Expand Down Expand Up @@ -174,6 +178,10 @@ export class HardhatNode extends EventEmitter {

const hardfork = getHardforkName(config.hardfork);
const mixHashGenerator = RandomBufferGenerator.create("randomMixHashSeed");
const parentBeaconBlockRootGenerator = RandomBufferGenerator.create(
"randomParentBeaconBlockRootSeed"
);

let forkClient: JsonRpcClient | undefined;

const common = makeCommon(config);
Expand Down Expand Up @@ -202,6 +210,14 @@ export class HardhatNode extends EventEmitter {
forkBlockNumber
);
await forkStateManager.initializeGenesisAccounts(genesisAccounts);

if (hardforkGte(hardfork, HardforkName.CANCUN)) {
await forkStateManager.putContractCode(
Address.fromString(BEACON_ROOT_ADDRESS),
Buffer.from(toBytes(BEACON_ROOT_BYTECODE))
);
}

stateManager = forkStateManager;

blockchain = new ForkBlockchain(forkClient, forkBlockNumber, common);
Expand Down Expand Up @@ -238,6 +254,13 @@ export class HardhatNode extends EventEmitter {
trie: stateTrie,
});

if (hardforkGte(hardfork, HardforkName.CANCUN)) {
await stateManager.putContractCode(
Address.fromString(BEACON_ROOT_ADDRESS),
Buffer.from(toBytes(BEACON_ROOT_BYTECODE))
);
}

const hardhatBlockchain = new HardhatBlockchain(common);

const genesisBlockBaseFeePerGas = hardforkGte(
Expand All @@ -252,9 +275,10 @@ export class HardhatNode extends EventEmitter {
hardhatBlockchain,
common,
config,
stateTrie,
await stateManager.getStateRoot(),
hardfork,
mixHashGenerator.next(),
parentBeaconBlockRootGenerator.next(),
genesisBlockBaseFeePerGas
);

Expand Down Expand Up @@ -304,6 +328,7 @@ export class HardhatNode extends EventEmitter {
hardfork,
hardforkActivations,
mixHashGenerator,
parentBeaconBlockRootGenerator,
allowUnlimitedContractSize,
allowBlocksWithSameTimestamp,
tracingConfig,
Expand Down Expand Up @@ -392,6 +417,7 @@ Hardhat Network's forking functionality only works with blocks from at least spu
public readonly hardfork: HardforkName,
private readonly _hardforkActivations: HardforkHistoryConfig,
private _mixHashGenerator: RandomBufferGenerator,
private _parentBeaconBlockRootGenerator: RandomBufferGenerator,
public readonly allowUnlimitedContractSize: boolean,
private _allowBlocksWithSameTimestamp: boolean,
tracingConfig?: TracingConfig,
Expand Down Expand Up @@ -1068,6 +1094,8 @@ Hardhat Network's forking functionality only works with blocks from at least spu
this.getUserProvidedNextBlockBaseFeePerGas(),
coinbase: this.getCoinbaseAddress().toString(),
mixHashGenerator: this._mixHashGenerator.clone(),
parentBeaconBlockRootGenerator:
this._parentBeaconBlockRootGenerator.clone(),
};

this._irregularStatesByBlockNumber = new Map(
Expand Down Expand Up @@ -1124,6 +1152,8 @@ Hardhat Network's forking functionality only works with blocks from at least spu
this._coinbase = snapshot.coinbase;

this._mixHashGenerator = snapshot.mixHashGenerator;
this._parentBeaconBlockRootGenerator =
snapshot.parentBeaconBlockRootGenerator;

// We delete this and the following snapshots, as they can only be used
// once in Ganache
Expand Down Expand Up @@ -1852,6 +1882,10 @@ Hardhat Network's forking functionality only works with blocks from at least spu
headerData.mixHash = this._getNextMixHash();
}

if (this.isPostCancunHardfork()) {
headerData.parentBeaconBlockRoot = this._getNextParentBeaconBlockRoot();
}

headerData.baseFeePerGas = await this.getNextBlockBaseFeePerGas();

const blockBuilder = await this._vm.buildBlock({
Expand Down Expand Up @@ -2560,6 +2594,31 @@ Hardhat Network's forking functionality only works with blocks from at least spu
);
}

// If this VM is running without cancun, but the block has cancun fields,
// we remove them from the block
if (
!this.isCancunBlock(blockNumberOrPending) &&
blockContext.header.blobGasUsed !== undefined
) {
blockContext = Block.fromBlockData(
{
...blockContext,
header: {
...blockContext.header,
blobGasUsed: undefined,
excessBlobGas: undefined,
parentBeaconBlockRoot: undefined,
},
},
{
freeze: false,
common: this._vm.common,

skipConsensusFormatValidation: true,
}
);
}

// NOTE: This is a workaround of both an @nomicfoundation/ethereumjs-vm limitation, and
// a bug in Hardhat Network.
//
Expand Down Expand Up @@ -2720,10 +2779,27 @@ Hardhat Network's forking functionality only works with blocks from at least spu
return this._vm.common.gteHardfork("shanghai");
}

public isCancunBlock(blockNumberOrPending?: bigint | "pending"): boolean {
if (
blockNumberOrPending !== undefined &&
blockNumberOrPending !== "pending"
) {
return this._vm.common.hardforkGteHardfork(
this._selectHardfork(blockNumberOrPending),
"cancun"
);
}
return this._vm.common.gteHardfork("cancun");
}

public isPostMergeHardfork(): boolean {
return hardforkGte(this.hardfork, HardforkName.MERGE);
}

public isPostCancunHardfork(): boolean {
return hardforkGte(this.hardfork, HardforkName.CANCUN);
}

public setPrevRandao(prevRandao: Buffer): void {
this._mixHashGenerator.setNext(prevRandao);
}
Expand Down Expand Up @@ -2779,6 +2855,10 @@ Hardhat Network's forking functionality only works with blocks from at least spu
return this._mixHashGenerator.next();
}

private _getNextParentBeaconBlockRoot(): Uint8Array {
return this._parentBeaconBlockRootGenerator.next();
}

private async _getEstimateGasFeePriceFields(
callParams: CallParams,
blockNumberOrPending: bigint | "pending"
Expand Down Expand Up @@ -2863,7 +2943,7 @@ Hardhat Network's forking functionality only works with blocks from at least spu
);
}

return hardfork;
return hardfork === "merge" ? "mergeForkIdTransition" : hardfork;
}

private _getCommonForTracing(networkId: number, blockNumber: bigint): Common {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export interface RpcBlockOutput {
baseFeePerGas?: string;
withdrawals?: RpcWithdrawalItem[];
withdrawalsRoot?: string;

// Only present after Cancun hard-fork
parentBeaconBlockRoot?: string | null;
blobGasUsed?: string | null;
excessBlobGas?: string | null;
}

export type RpcTransactionOutput =
Expand Down Expand Up @@ -211,9 +216,31 @@ export function getRpcBlock(
output.withdrawalsRoot = bufferToRpcData(block.header.withdrawalsRoot);
}

addCancunPropertiesIfPresent(output, block, pending);

return output;
}

function addCancunPropertiesIfPresent(
output: RpcBlockOutput,
block: Block,
pending: boolean
) {
if (block.header.parentBeaconBlockRoot !== undefined) {
output.parentBeaconBlockRoot = pending
? null
: bufferToRpcData(block.header.parentBeaconBlockRoot, 32);
}

if (block.header.blobGasUsed !== undefined) {
output.blobGasUsed = numberToRpcQuantity(block.header.blobGasUsed);
}

if (block.header.excessBlobGas !== undefined) {
output.excessBlobGas = numberToRpcQuantity(block.header.excessBlobGas);
}
}

export function getRpcTransaction(
tx: TypedTransaction,
showTransactionType: boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Block, HeaderData } from "@nomicfoundation/ethereumjs-block";
import { Common } from "@nomicfoundation/ethereumjs-common";
import { Trie } from "@nomicfoundation/ethereumjs-trie";
import { bytesToHex as bufferToHex } from "@nomicfoundation/ethereumjs-util";

import { dateToTimestampSeconds } from "../../../util/date";
Expand All @@ -13,9 +12,10 @@ export async function putGenesisBlock(
blockchain: HardhatBlockchain,
common: Common,
{ initialDate, blockGasLimit: initialBlockGasLimit }: LocalNodeConfig,
stateTrie: Trie,
stateRoot: Uint8Array,
hardfork: HardforkName,
initialMixHash: Uint8Array,
initialParentBeaconBlockRoot: Uint8Array,
initialBaseFee?: bigint
) {
const initialBlockTimestamp =
Expand All @@ -24,20 +24,25 @@ export async function putGenesisBlock(
: getCurrentTimestamp();

const isPostMerge = hardforkGte(hardfork, HardforkName.MERGE);
const isPostCancun = hardforkGte(hardfork, HardforkName.CANCUN);

const header: HeaderData = {
timestamp: `0x${initialBlockTimestamp.toString(16)}`,
gasLimit: initialBlockGasLimit,
difficulty: isPostMerge ? 0 : 1,
nonce: isPostMerge ? "0x0000000000000000" : "0x0000000000000042",
extraData: "0x1234",
stateRoot: bufferToHex(stateTrie.root()),
stateRoot: bufferToHex(stateRoot),
};

if (isPostMerge) {
header.mixHash = initialMixHash;
}

if (isPostCancun) {
header.parentBeaconBlockRoot = initialParentBeaconBlockRoot;
}

if (initialBaseFee !== undefined) {
header.baseFeePerGas = initialBaseFee;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ export class VMDebugTracer {

let gasCost = step.opcode.fee;

let op = step.opcode.name;
let op = step.opcode.name === "KECCAK256" ? "SHA3" : step.opcode.name;
let error: object | undefined;

const storage: Storage = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ describe("Default config", function () {
}

for (const hardfork of Object.values(HardforkName)) {
if (hardfork === HardforkName.CANCUN) {
// temporarily skipped until Cancun is enabled in mainnet
continue;
}

const hardforkHistoryEntry =
mainnetChainConfig.hardforkHistory.get(hardfork);
assert.isDefined(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
import {
addHexPrefix,
unpadBytes,
toBytes,
bytesToHex,
} from "@nomicfoundation/ethereumjs-util";

export function hexStripZeros(hexString: string) {
return addHexPrefix(bytesToHex(unpadBytes(toBytes(hexString))));
return hexString === "0x0" ? "0x0" : hexString.replace(/^0x0+/, "0x");
}
Loading

0 comments on commit 5ba1b45

Please sign in to comment.