Skip to content

Commit

Permalink
Fix forking a shanghai block with a pre-shanghai local hardfork
Browse files Browse the repository at this point in the history
  • Loading branch information
fvictorio committed Apr 15, 2023
1 parent 231f7c4 commit 15c07c2
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2387,6 +2387,30 @@ Hardhat Network's forking functionality only works with blocks from at least spu
// know anything about the txs in the current block
}

// If this VM is running without EIP4895, but the block has withdrawals,
// we remove them and the withdrawal root from the block
if (
!this.isEip4895Active(blockNumberOrPending) &&
blockContext.withdrawals !== undefined
) {
blockContext = Block.fromBlockData(
{
...blockContext,
withdrawals: undefined,
header: {
...blockContext.header,
withdrawalsRoot: 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 @@ -2550,6 +2574,19 @@ Hardhat Network's forking functionality only works with blocks from at least spu
return this._vm._common.gteHardfork("london");
}

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

public isPostMergeHardfork(): boolean {
return hardforkGte(this.hardfork, HardforkName.MERGE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ if (INFURA_URL !== undefined) {
useProvider({
useJsonRpc: false,
loggerEnabled: true,
forkConfig: { jsonRpcUrl: url },
forkConfig: { jsonRpcUrl: url, blockNumber: options.forkBlockNumber },
...options,
});
},
Expand All @@ -153,7 +153,7 @@ if (INFURA_URL !== undefined) {
useProvider({
useJsonRpc: false,
loggerEnabled: true,
forkConfig: { jsonRpcUrl: url },
forkConfig: { jsonRpcUrl: url, blockNumber: options.forkBlockNumber },
mining: {
auto: false,
interval: 10000,
Expand All @@ -171,7 +171,7 @@ if (INFURA_URL !== undefined) {
useProvider({
useJsonRpc: false,
loggerEnabled: true,
forkConfig: { jsonRpcUrl: url },
forkConfig: { jsonRpcUrl: url, blockNumber: options.forkBlockNumber },
...options,
});
},
Expand All @@ -188,7 +188,7 @@ if (ALCHEMY_URL !== undefined) {
useProvider({
useJsonRpc: false,
loggerEnabled: true,
forkConfig: { jsonRpcUrl: url },
forkConfig: { jsonRpcUrl: url, blockNumber: options.forkBlockNumber },
...options,
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface UseProviderOptions {
mempool?: HardhatNetworkMempoolConfig;
coinbase?: string;
chains?: HardhatNetworkChainsConfig;
forkBlockNumber?: number;
}

export function useProvider({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import { assert } from "chai";

import {
numberToRpcQuantity,
rpcDataToBigInt,
} from "../../../../src/internal/core/jsonrpc/types/base-types";
import { workaroundWindowsCiFailures } from "../../../utils/workaround-windows-ci-failures";
import { DAI_ADDRESS } from "../helpers/constants";
import { setCWD } from "../helpers/cwd";
import {
DEFAULT_ACCOUNTS_ADDRESSES,
FORKED_PROVIDERS,
} from "../helpers/providers";

const TOTAL_SUPPLY_SELECTOR = "0x18160ddd";

const SHANGHAI_HARDFORK_BLOCK_NUMBER = 17_034_870;
const MERGE_HARDFORK_BLOCK_NUMBER = 15_537_394;

describe("Forking a block with a different hardfork", function () {
FORKED_PROVIDERS.forEach(({ rpcProvider, useProvider }) => {
workaroundWindowsCiFailures.call(this, { isFork: true });

describe(`Using ${rpcProvider}`, function () {
setCWD();

describe("shanghai hardfork", function () {
const hardfork = "shanghai";

describe("forking a 'shanghai' block", function () {
const forkBlockNumber = SHANGHAI_HARDFORK_BLOCK_NUMBER + 100;

useProvider({
hardfork,
forkBlockNumber,
});

it("should mine transactions", async function () {
await this.provider.send("eth_sendTransaction", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DEFAULT_ACCOUNTS_ADDRESSES[1],
},
]);
});

it("should make calls in the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
5022305384218217259061852351n
);
});

it("should make calls in blocks before the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber - 1),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
5022305384218217259061852351n
);
});
});

describe("forking a 'merge' block", function () {
const forkBlockNumber = MERGE_HARDFORK_BLOCK_NUMBER + 100;

useProvider({
forkBlockNumber,
});

it("should mine transactions", async function () {
await this.provider.send("eth_sendTransaction", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DEFAULT_ACCOUNTS_ADDRESSES[1],
},
]);
});

it("should make calls in the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
6378560137543824474512862351n
);
});

it("should make calls in blocks before the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber - 1),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
6378560137543824474512862351n
);
});
});
});

describe("merge hardfork", function () {
const hardfork = "merge";

describe("forking a 'shanghai' block", function () {
const forkBlockNumber = SHANGHAI_HARDFORK_BLOCK_NUMBER + 100;

useProvider({
forkBlockNumber,
hardfork,
});

it("should mine transactions", async function () {
await this.provider.send("eth_sendTransaction", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DEFAULT_ACCOUNTS_ADDRESSES[1],
},
]);
});

it("should make calls in the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
5022305384218217259061852351n
);
});

it("should make calls in blocks before the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber - 1),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
5022305384218217259061852351n
);
});
});

describe("forking a 'merge' block", function () {
const forkBlockNumber = MERGE_HARDFORK_BLOCK_NUMBER + 100;

useProvider({
forkBlockNumber,
});

it("should mine transactions", async function () {
await this.provider.send("eth_sendTransaction", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DEFAULT_ACCOUNTS_ADDRESSES[1],
},
]);
});

it("should make calls in the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
6378560137543824474512862351n
);
});

it("should make calls in blocks before the forked block", async function () {
const daiSupply = await this.provider.send("eth_call", [
{
from: DEFAULT_ACCOUNTS_ADDRESSES[0],
to: DAI_ADDRESS.toString(),
data: TOTAL_SUPPLY_SELECTOR,
},
numberToRpcQuantity(forkBlockNumber - 1),
]);

assert.equal(
rpcDataToBigInt(daiSupply),
6378560137543824474512862351n
);
});
});
});
});
});
});

0 comments on commit 15c07c2

Please sign in to comment.