Skip to content

Commit

Permalink
Merge pull request #3767 from NomicFoundation/shanghai
Browse files Browse the repository at this point in the history
Add support for the shanghai hardfork
  • Loading branch information
fvictorio committed Apr 10, 2023
2 parents 5426f58 + fcb182d commit 7645946
Show file tree
Hide file tree
Showing 29 changed files with 918 additions and 201 deletions.
5 changes: 5 additions & 0 deletions .changeset/orange-boxes-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hardhat": patch
---

Added support for the shanghai hardfork
5 changes: 4 additions & 1 deletion docs/src/content/hardhat-network/docs/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
- petersburg
- istanbul
- muirGlacier
- berlin
- london
- arrowGlacier
- grayGlacier
- merge
- shanghai

## Config

Expand Down Expand Up @@ -62,7 +65,7 @@ The block gas limit to use in Hardhat Network's blockchain. Default value: `30_0

#### `hardfork`

This setting changes how Hardhat Network works, to mimic Ethereum's mainnet at a given hardfork. It must be one of `"byzantium"`, `"constantinople"`, `"petersburg"`, `"istanbul"`, `"muirGlacier"`, `"berlin"`, `"london"`, `"arrowGlacier"`, `"grayGlacier"` and `"merge"`. Default value: `"merge"`
This setting changes how Hardhat Network works, to mimic Ethereum's mainnet at a given hardfork. It must be one of `"byzantium"`, `"constantinople"`, `"petersburg"`, `"istanbul"`, `"muirGlacier"`, `"berlin"`, `"london"`, `"arrowGlacier"`, `"grayGlacier"`, `"merge"` and `"shanghai"`. Default value: `"merge"`

#### `throwOnTransactionFailures`

Expand Down
20 changes: 10 additions & 10 deletions packages/hardhat-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,16 @@
"dependencies": {
"@ethersproject/abi": "^5.1.2",
"@metamask/eth-sig-util": "^4.0.0",
"@nomicfoundation/ethereumjs-block": "^4.0.0",
"@nomicfoundation/ethereumjs-blockchain": "^6.0.0",
"@nomicfoundation/ethereumjs-common": "^3.0.0",
"@nomicfoundation/ethereumjs-evm": "^1.0.0",
"@nomicfoundation/ethereumjs-rlp": "^4.0.0",
"@nomicfoundation/ethereumjs-statemanager": "^1.0.0",
"@nomicfoundation/ethereumjs-trie": "^5.0.0",
"@nomicfoundation/ethereumjs-tx": "^4.0.0",
"@nomicfoundation/ethereumjs-util": "^8.0.0",
"@nomicfoundation/ethereumjs-vm": "^6.0.0",
"@nomicfoundation/ethereumjs-block": "5.0.0",
"@nomicfoundation/ethereumjs-blockchain": "7.0.0",
"@nomicfoundation/ethereumjs-common": "4.0.0",
"@nomicfoundation/ethereumjs-evm": "2.0.0",
"@nomicfoundation/ethereumjs-rlp": "5.0.0",
"@nomicfoundation/ethereumjs-statemanager": "2.0.0",
"@nomicfoundation/ethereumjs-trie": "6.0.0",
"@nomicfoundation/ethereumjs-tx": "5.0.0",
"@nomicfoundation/ethereumjs-util": "9.0.0",
"@nomicfoundation/ethereumjs-vm": "7.0.0",
"@nomicfoundation/solidity-analyzer": "^0.1.0",
"@sentry/node": "^5.18.1",
"@types/bn.js": "^5.1.0",
Expand Down
1 change: 1 addition & 0 deletions packages/hardhat-core/src/internal/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const HARDHAT_NETWORK_SUPPORTED_HARDFORKS = [
"arrowGlacier",
"grayGlacier",
"merge",
"shanghai",
];

export const HARDHAT_MEMPOOL_SUPPORTED_ORDERS = ["fifo", "priority"] as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import { rpcAddress, rpcData, rpcHash, rpcQuantity } from "../base-types";

import { rpcTransaction } from "./transaction";

const rpcWithdrawalItem = t.type(
{
index: rpcQuantity,
validatorIndex: rpcQuantity,
address: rpcAddress,
amount: rpcQuantity,
},
"RpcBlockWithdrawalItem"
);

const baseBlockResponse = {
number: nullable(rpcQuantity),
hash: nullable(rpcHash),
Expand All @@ -26,6 +36,8 @@ const baseBlockResponse = {
uncles: t.array(rpcHash, "HASH Array"),
mixHash: optional(rpcHash),
baseFeePerGas: optional(rpcQuantity),
withdrawals: optional(t.array(rpcWithdrawalItem)),
withdrawalsRoot: optional(rpcHash),
};

export type RpcBlock = t.TypeOf<typeof rpcBlock>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export abstract class BlockchainBase {

public async getBlock(
blockHashOrNumber: Buffer | bigint | number
): Promise<Block | null> {
): Promise<Block> {
if (
(typeof blockHashOrNumber === "number" ||
BigIntUtils.isBigInt(blockHashOrNumber)) &&
Expand All @@ -73,22 +73,32 @@ export abstract class BlockchainBase {
}

if (typeof blockHashOrNumber === "number") {
return this._data.getBlockByNumber(BigInt(blockHashOrNumber)) ?? null;
const blockByNumber = this._data.getBlockByNumber(
BigInt(blockHashOrNumber)
);
if (blockByNumber === undefined) {
throw new Error("Block not found");
}
return blockByNumber;
}
if (BigIntUtils.isBigInt(blockHashOrNumber)) {
return this._data.getBlockByNumber(blockHashOrNumber) ?? null;
const blockByNumber = this._data.getBlockByNumber(blockHashOrNumber);
if (blockByNumber === undefined) {
throw new Error("Block not found");
}
return blockByNumber;
}
const blockByHash = this._data.getBlockByHash(blockHashOrNumber);
if (blockByHash === undefined) {
throw new Error("Block not found");
}
return this._data.getBlockByHash(blockHashOrNumber) ?? null;
return blockByHash;
}

public abstract getLatestBlockNumber(): bigint;

public async getLatestBlock(): Promise<Block> {
const block = await this.getBlock(this.getLatestBlockNumber());
if (block === null) {
throw new Error("Block not found");
}
return block;
return this.getBlock(this.getLatestBlockNumber());
}

public getLocalTransaction(
Expand Down Expand Up @@ -162,7 +172,6 @@ export abstract class BlockchainBase {
}

const parentBlock = await this.getBlock(blockNumber - 1n);
assertHardhatInvariant(parentBlock !== null, "Parent block should exist");

const parentHash = parentBlock.hash();
const parentTD = this._data.getTotalDifficulty(parentHash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export function deserializeTransaction(
} else {
data = TransactionFactory.fromSerializedData(toBuffer(rlpSerialization), {
common,
disableMaxInitCodeSizeCheck: true,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ export class ForkBlockchain
return this._latestBlockNumber;
}

public async getBlock(
blockHashOrNumber: Buffer | bigint
): Promise<Block | null> {
public async getBlock(blockHashOrNumber: Buffer | bigint): Promise<Block> {
if (
typeof blockHashOrNumber === "bigint" &&
this._data.isReservedBlock(blockHashOrNumber)
Expand All @@ -61,11 +59,17 @@ export class ForkBlockchain
let block: Block | undefined | null;
if (Buffer.isBuffer(blockHashOrNumber)) {
block = await this._getBlockByHash(blockHashOrNumber);
return block ?? null;
if (block === undefined) {
throw new Error("Block not found");
}
return block;
}

block = await this._getBlockByNumber(BigInt(blockHashOrNumber));
return block ?? null;
if (block === undefined) {
throw new Error("Block not found");
}
return block;
}

public async addBlock(block: Block): Promise<Block> {
Expand Down Expand Up @@ -131,10 +135,10 @@ export class ForkBlockchain
if (td !== undefined) {
return td;
}
const block = await this.getBlock(blockHash);
if (block === null) {
throw new Error("Block not found");
}

// fetch block to check if it exists
await this.getBlock(blockHash);

td = this._data.getTotalDifficulty(blockHash);
if (td === undefined) {
throw new Error("This should never happen");
Expand Down Expand Up @@ -232,10 +236,12 @@ export class ForkBlockchain
if (blockNumber > this._latestBlockNumber) {
return undefined;
}
const block = await super.getBlock(blockNumber);
if (block !== null) {

try {
const block = await super.getBlock(blockNumber);
return block;
}
} catch {}

const rpcBlock = await this._jsonRpcClient.getBlockByNumber(
blockNumber,
true
Expand All @@ -254,13 +260,17 @@ export class ForkBlockchain
}

const common = this._common.copy();
// We set the common's hardfork to Berlin if the remote block doesn't have
// EIP-1559 activated. The reason for this is that ethereumjs throws if we
// have a base fee for an older hardfork, and set a default one for London.
if (rpcBlock.baseFeePerGas !== undefined) {
common.setHardfork("london"); // TODO: consider changing this to "latest hardfork"
} else {
// We set the common's hardfork depending on the remote block fields, to
// prevent ethereumjs from throwing if unsupported fields are passed.
// We use "berlin" for pre-EIP-1559 blocks (blocks without baseFeePerGas),
// "merge" for blocks that have baseFeePerGas but not withdrawals,
// and "shanghai" for blocks with withdrawals
if (rpcBlock.baseFeePerGas === undefined) {
common.setHardfork("berlin");
} else if (rpcBlock.withdrawals === undefined) {
common.setHardfork("merge");
} else {
common.setHardfork("shanghai");
}

// 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 @@ -23,8 +23,10 @@ export function rpcToBlockData(rpcBlock: RpcBlockWithTransactions): BlockData {
mixHash: rpcBlock.mixHash,
nonce: rpcBlock.nonce,
baseFeePerGas: rpcBlock.baseFeePerGas,
withdrawalsRoot: rpcBlock.withdrawalsRoot,
},
transactions: rpcBlock.transactions.map(rpcToTxData),
withdrawals: rpcBlock.withdrawals,
// uncleHeaders are not fetched and set here as provider methods for getting them are not supported
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,13 @@ import {
import { assertHardhatNetworkInvariant } from "../utils/assertions";
import { optional } from "../../../util/io-ts";
import * as BigIntUtils from "../../../util/bigint";
import { HardforkName } from "../../../util/hardforks";
import { ModulesLogger } from "./logger";

const EIP1559_MIN_HARDFORK = "london";
const ACCESS_LIST_MIN_HARDFORK = "berlin";
const EIP155_MIN_HARDFORK = "spuriousDragon";
const EIP1559_MIN_HARDFORK = HardforkName.LONDON;
const ACCESS_LIST_MIN_HARDFORK = HardforkName.BERLIN;
const EIP155_MIN_HARDFORK = HardforkName.SPURIOUS_DRAGON;
const EIP3860_MIN_HARDFORK = HardforkName.SHANGHAI;

/* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */
export class EthModule {
Expand Down Expand Up @@ -960,7 +962,10 @@ export class EthModule {
try {
tx = TransactionFactory.fromSerializedData(rawTx, {
common: this._common,
disableMaxInitCodeSizeCheck: true,
});

this._validateEip3860MaxInitCodeSize(tx.to?.toBuffer(), tx.data);
} catch (error) {
// This section of the code is incredibly dependant of TransactionFactory.fromSerializedData
// AccessListEIP2930Transaction.fromSerializedTx and Transaction.fromSerializedTx
Expand Down Expand Up @@ -1017,6 +1022,11 @@ export class EthModule {
private async _sendTransactionAction(
transactionRequest: RpcTransactionRequest
): Promise<string> {
this._validateEip3860MaxInitCodeSize(
transactionRequest.to,
transactionRequest.data ?? Buffer.from([])
);

const expectedChainId = this._common.chainId();
if (
transactionRequest.chainId !== undefined &&
Expand Down Expand Up @@ -1742,6 +1752,33 @@ You can use them by running Hardhat Network with 'hardfork' ${EIP155_MIN_HARDFOR
}
}

private _validateEip3860MaxInitCodeSize(
to: Buffer | undefined,
data: Buffer
) {
if (!this._common.gteHardfork(EIP3860_MIN_HARDFORK)) {
// this check is only relevant after shanghai
return;
}

if (to !== undefined) {
// this check is only relevant for deployments
return;
}

if (this._node.allowUnlimitedContractSize) {
// this check is not performed if allowUnlimitedContractSize is enabled
return;
}

const maxInitCodeSize = this._common.param("vm", "maxInitCodeSize");
if (data.length > maxInitCodeSize) {
throw new InvalidArgumentsError(`Trying to send a deployment transaction whose init code length is ${data.length}. The max length allowed by EIP-3860 is ${maxInitCodeSize}.
Enable the 'allowUnlimitedContractSize' option to allow init codes of any length.`);
}
}

private _validateRawTransactionHardforkRequirements(rawTx: Buffer) {
if (rawTx[0] <= 0x7f && rawTx[0] !== 1 && rawTx[0] !== 2) {
throw new InvalidArgumentsError(`Invalid transaction type ${rawTx[0]}.
Expand Down
Loading

0 comments on commit 7645946

Please sign in to comment.