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

Process transactions with unknown tx types #3302

Merged
merged 3 commits into from
Oct 27, 2022
Merged
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: 5 additions & 0 deletions .changeset/nine-kangaroos-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hardhat": patch
---

Fixed an issue when forking networks like Arbitrum Nitro that use non-standard transaction types (#2995, #3194).
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ReadOnlyValidTransaction } from "../transactions/ReadOnlyValidTransacti
import { HardhatBlockchainInterface } from "../types/HardhatBlockchainInterface";

import { ReadOnlyValidEIP1559Transaction } from "../transactions/ReadOnlyValidEIP1559Transaction";
import { ReadOnlyValidUnknownTypeTransaction } from "../transactions/ReadOnlyValidUnknownTypeTransaction";
import { rpcToBlockData } from "./rpcToBlockData";
import { rpcToTxData } from "./rpcToTxData";

Expand Down Expand Up @@ -297,9 +298,20 @@ export class ForkBlockchain
rpcToTxData(transaction) as FeeMarketEIP1559TxData
);
} else {
throw new InternalError(
`Unknown transaction type ${transaction.type.toString()}`
);
// we try to interpret unknown txs as legacy transactions, to support
// networks like Arbitrum that have non-standards tx types
try {
tx = new ReadOnlyValidUnknownTypeTransaction(
new Address(transaction.from),
Number(transaction.type),
rpcToTxData(transaction)
);
} catch (e: any) {
throw new InternalError(
`Could not process transaction with type ${transaction.type.toString()}`,
e
);
}
}

block.transactions.push(tx);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { Common } from "@nomicfoundation/ethereumjs-common";
import { Transaction, TxData, TxOptions } from "@nomicfoundation/ethereumjs-tx";
import { Address } from "@nomicfoundation/ethereumjs-util";

import { InternalError } from "../../../core/providers/errors";

/* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */

/**
* This class is like `ReadOnlyValidTransaction` but for
* a transaction with an unknown tx type.
*/
export class ReadOnlyValidUnknownTypeTransaction extends Transaction {
public static fromTxData(_txData: TxData, _opts?: TxOptions): never {
throw new InternalError(
"`fromTxData` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public static fromSerializedTx(
_serialized: Buffer,
_opts?: TxOptions
): never {
throw new InternalError(
"`fromSerializedTx` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public static fromRlpSerializedTx(
_serialized: Buffer,
_opts?: TxOptions
): never {
throw new InternalError(
"`fromRlpSerializedTx` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public static fromValuesArray(_values: Buffer[], _opts?: TxOptions): never {
throw new InternalError(
"`fromRlpSerializedTx` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public readonly common: Common;

private readonly _sender: Address;
private readonly _actualType: number;

constructor(sender: Address, type: number, data: TxData = {}) {
super(data, { freeze: false });

this.common = this._getCommon();
this._sender = sender;
this._actualType = type;
}

public get type(): number {
return this._actualType;
}

public verifySignature(): boolean {
return true;
}

public getSenderAddress(): Address {
return this._sender;
}

public sign(): never {
throw new InternalError(
"`sign` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public getDataFee(): never {
throw new InternalError(
"`getDataFee` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public getBaseFee(): never {
throw new InternalError(
"`getBaseFee` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public getUpfrontCost(): never {
throw new InternalError(
"`getUpfrontCost` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public validate(_stringError?: false): never;
public validate(_stringError: true): never;
public validate(_stringError: boolean = false): never {
throw new InternalError(
"`validate` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public toCreationAddress(): never {
throw new InternalError(
"`toCreationAddress` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public getSenderPublicKey(): never {
throw new InternalError(
"`getSenderPublicKey` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public getMessageToVerifySignature(): never {
throw new InternalError(
"`getMessageToVerifySignature` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}

public getMessageToSign(): never {
throw new InternalError(
"`getMessageToSign` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
}
}

// Override private methods

const ReadOnlyValidUnknownTypeTransactionPrototype: any =
ReadOnlyValidUnknownTypeTransaction.prototype;

ReadOnlyValidUnknownTypeTransactionPrototype._validateTxV = function (
_v: any,
common: any
) {
return this._getCommon(common);
};

ReadOnlyValidUnknownTypeTransactionPrototype._signedTxImplementsEIP155 =
function () {
throw new InternalError(
"`_signedTxImplementsEIP155` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
};

ReadOnlyValidUnknownTypeTransactionPrototype._unsignedTxImplementsEIP155 =
function () {
throw new InternalError(
"`_unsignedTxImplementsEIP155` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
};

ReadOnlyValidUnknownTypeTransactionPrototype._getMessageToSign = function () {
throw new InternalError(
"`_getMessageToSign` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
};

ReadOnlyValidUnknownTypeTransactionPrototype._processSignature = function () {
throw new InternalError(
"`_processSignature` is not implemented in ReadOnlyValidUnknownTypeTransaction"
);
};