Skip to content

Commit

Permalink
Reject blob txs with a custom error
Browse files Browse the repository at this point in the history
  • Loading branch information
fvictorio committed Feb 13, 2024
1 parent 45b8290 commit a3abedc
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { rpcAccessList } from "../access-list";
import {
rpcAddress,
rpcData,
rpcHash,
rpcQuantity,
rpcStorageSlot,
rpcStorageSlotHexString,
Expand All @@ -23,6 +24,8 @@ export const rpcCallRequest = t.type(
accessList: optionalOrNullable(rpcAccessList),
maxFeePerGas: optionalOrNullable(rpcQuantity),
maxPriorityFeePerGas: optionalOrNullable(rpcQuantity),
blobs: optionalOrNullable(t.array(rpcData)),
blobVersionedHashes: optionalOrNullable(t.array(rpcHash)),
},
"RpcCallRequest"
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as t from "io-ts";

import { optionalOrNullable } from "../../../../util/io-ts";
import { rpcAccessList } from "../access-list";
import { rpcAddress, rpcData, rpcQuantity } from "../base-types";
import { rpcAddress, rpcData, rpcHash, rpcQuantity } from "../base-types";

// Type used by eth_sendTransaction
export const rpcTransactionRequest = t.type(
Expand All @@ -18,6 +18,8 @@ export const rpcTransactionRequest = t.type(
chainId: optionalOrNullable(rpcQuantity),
maxFeePerGas: optionalOrNullable(rpcQuantity),
maxPriorityFeePerGas: optionalOrNullable(rpcQuantity),
blobs: optionalOrNullable(t.array(rpcData)),
blobVersionedHashes: optionalOrNullable(t.array(rpcHash)),
},
"RpcTransactionRequest"
);
Expand All @@ -38,6 +40,8 @@ export interface RpcTransactionRequestInput {
}>;
maxFeePerGas?: string;
maxPriorityFeePerGas?: string;
blobs?: string[];
blobVersionedHashes?: string[];
}

export type RpcTransactionRequest = t.TypeOf<typeof rpcTransactionRequest>;
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,15 @@ export class EthModule extends Base {
private _validateTransactionAndCallRequest(
rpcRequest: RpcCallRequest | RpcTransactionRequest
) {
if (
rpcRequest.blobs !== undefined ||
rpcRequest.blobVersionedHashes !== undefined
) {
throw new InvalidInputError(
`An EIP-4844 (shard blob) transaction was received, but Hardhat doesn't have support for them yet.`
);
}

if (
(rpcRequest.maxFeePerGas !== undefined ||
rpcRequest.maxPriorityFeePerGas !== undefined) &&
Expand Down Expand Up @@ -1741,6 +1750,12 @@ Enable the 'allowUnlimitedContractSize' option to allow init codes of any length
}

private _validateRawTransactionHardforkRequirements(rawTx: Buffer) {
if (rawTx[0] <= 0x7f && rawTx[0] === 3) {
throw new InvalidInputError(
`An EIP-4844 (shard blob) transaction was received, but Hardhat doesn't have support for them yet.`
);
}

if (rawTx[0] <= 0x7f && rawTx[0] !== 1 && rawTx[0] !== 2) {
throw new InvalidArgumentsError(`Invalid transaction type ${rawTx[0]}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ export interface CallParams {
export type TransactionParams =
| LegacyTransactionParams
| AccessListTransactionParams
| EIP1559TransactionParams;
| EIP1559TransactionParams
| BlobTransactionParams;

interface BaseTransactionParams {
// `to` should be undefined for contract creation
Expand Down Expand Up @@ -112,6 +113,11 @@ export interface EIP1559TransactionParams extends BaseTransactionParams {
maxPriorityFeePerGas: bigint;
}

export interface BlobTransactionParams extends EIP1559TransactionParams {
blobs: Uint8Array[];
blobVersionedHashes: Uint8Array[];
}

export interface FilterParams {
fromBlock: bigint;
toBlock: bigint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
FeeMarketEIP1559Transaction,
LegacyTransaction,
TypedTransaction,
BlobEIP4844Transaction,
} from "@nomicfoundation/ethereumjs-tx";
import {
Address,
Expand Down Expand Up @@ -490,7 +491,12 @@ Hardhat Network's forking functionality only works with blocks from at least spu
if (pk !== undefined) {
let tx: TypedTransaction;

if ("maxFeePerGas" in txParams) {
if ("blobs" in txParams) {
tx = BlobEIP4844Transaction.fromTxData(txParams, {
common: this._vm.common,
allowUnlimitedInitCodeSize: true,
});
} else if ("maxFeePerGas" in txParams) {
tx = FeeMarketEIP1559Transaction.fromTxData(txParams, {
common: this._vm.common,
allowUnlimitedInitCodeSize: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1569,6 +1569,24 @@ contract C {
assert.equal(await getChainIdFromContract(this.provider), chainId);
});

it("should reject blob transactions", async function () {
await assertInvalidInputError(
this.provider,
"eth_call",
[
{
from: DEFAULT_ACCOUNTS_ADDRESSES[1],
to: DEFAULT_ACCOUNTS_ADDRESSES[2],
blobs: ["0x1234"],
blobVersionedHashes: [
"0x1234567812345678123456781234567812345678123456781234567812345678",
],
},
],
"An EIP-4844 (shard blob) transaction was received, but Hardhat doesn't have support for them yet."
);
});

describe("http JSON-RPC response", function () {
let client: Client;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,24 @@ describe("Eth module", function () {
assert.isTrue(BigInt(estimation) <= 100_000n);
});

it("should reject blob transactions", async function () {
await assertInvalidInputError(
this.provider,
"eth_estimateGas",
[
{
from: DEFAULT_ACCOUNTS_ADDRESSES[1],
to: DEFAULT_ACCOUNTS_ADDRESSES[2],
blobs: ["0x1234"],
blobVersionedHashes: [
"0x1234567812345678123456781234567812345678123456781234567812345678",
],
},
],
"An EIP-4844 (shard blob) transaction was received, but Hardhat doesn't have support for them yet."
);
});

describe("Fee price fields", function () {
describe("Running a hardfork with EIP-1559", function () {
it("Should validate that gasPrice and maxFeePerGas & maxPriorityFeePerGas are not mixed", async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ describe("Eth module", function () {
assertReceiptMatchesGethOne(receipt, receiptFromGeth, 1);
});

it("should reject blob transactions", async function () {
// blob tx signed with the private key of the first default account
const rawBlobTx =
"0x03f88380808080809400000000000000000000000000000000000000108080c080e1a0000000000000000000000000000000000000001012345678901234567890123401a0f3f7e5408804e3a0e3c4ac30a4f14b2995656a02d8b0279d7d48044d3cdf05e6a004e7606fef78d5221916053b3ec8a5fefddaa8a62ac6440f24a7c860ca25aa9f";

await assertInvalidInputError(
this.provider,
"eth_sendRawTransaction",
[rawBlobTx],
"An EIP-4844 (shard blob) transaction was received, but Hardhat doesn't have support for them yet."
);
});

describe("Transaction hash returned within the error data", function () {
describe("Set lower baseFeePerGas", function () {
// setting a lower baseFeePerGas here to avoid having to re-create the raw tx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,24 @@ describe("Eth module", function () {
// assert:
assert.equal(await getChainIdFromContract(this.provider), chainId);
});

it("should reject blob transactions", async function () {
await assertInvalidInputError(
this.provider,
"eth_sendTransaction",
[
{
from: DEFAULT_ACCOUNTS_ADDRESSES[1],
to: DEFAULT_ACCOUNTS_ADDRESSES[2],
blobs: ["0x1234"],
blobVersionedHashes: [
"0x1234567812345678123456781234567812345678123456781234567812345678",
],
},
],
"An EIP-4844 (shard blob) transaction was received, but Hardhat doesn't have support for them yet."
);
});
});

describe("eth_sendTransaction with minGasPrice", function () {
Expand Down

0 comments on commit a3abedc

Please sign in to comment.