Skip to content

Commit de03c6c

Browse files
authored
feat: Add precheck for blob transactions (#2540)
* feat: add precheck for tx type 3 Signed-off-by: Ivo Yankov <[email protected]> * test: add unit tests Signed-off-by: Ivo Yankov <[email protected]> --------- Signed-off-by: Ivo Yankov <[email protected]>
1 parent e585337 commit de03c6c

File tree

6 files changed

+120
-1
lines changed

6 files changed

+120
-1
lines changed

packages/relay/src/lib/errors/JsonRpcError.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ export const predefined = {
169169
code: -32601,
170170
message: 'Unsupported JSON-RPC method',
171171
}),
172+
UNSUPPORTED_TRANSACTION_TYPE: new JsonRpcError({
173+
name: 'Unsupported transaction type',
174+
code: -32611,
175+
message: 'Unsupported transaction type',
176+
}),
172177
VALUE_TOO_LOW: new JsonRpcError({
173178
name: 'Value too low',
174179
code: -32602,

packages/relay/src/lib/precheck.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export class Precheck {
5252
* @param gasPrice
5353
*/
5454
async sendRawTransactionCheck(parsedTx: ethers.Transaction, gasPrice: number, requestId?: string) {
55+
this.transactionType(parsedTx, requestId);
5556
this.gasLimit(parsedTx, requestId);
5657
const mirrorAccountInfo = await this.verifyAccount(parsedTx, requestId);
5758
await this.nonce(parsedTx, mirrorAccountInfo.ethereum_nonce, requestId);
@@ -301,4 +302,17 @@ export class Precheck {
301302
throw predefined.TRANSACTION_SIZE_TOO_BIG(String(transactionSize), String(transactionSizeLimit));
302303
}
303304
}
305+
306+
transactionType(tx: Transaction, requestId?: string) {
307+
// Blob transactions are not supported as per HIP 866
308+
if (tx.type === 3) {
309+
const requestIdPrefix = formatRequestIdMessage(requestId);
310+
this.logger.trace(
311+
`${requestIdPrefix} Transaction with type=${
312+
tx.type
313+
} is unsupported for sendRawTransaction(transaction=${JSON.stringify(tx)})`,
314+
);
315+
throw predefined.UNSUPPORTED_TRANSACTION_TYPE;
316+
}
317+
}
304318
}

packages/relay/tests/helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ export { expectUnsupportedMethod, expectedError, signTransaction, mockData, rand
341341
export const bytecode =
342342
'0x608060405234801561001057600080fd5b5060405161078938038061078983398181016040528101906100329190';
343343
export const blockHashTrimmed = '0x3c08bbbee74d287b1dcd3f0ca6d1d2cb92c90883c4acf9747de9f3f3162ad25b';
344+
export const blobVersionedHash = '0x3c08bbbee74d287b1dcd3f0ca6d1d2cb92c90883c4acf9747de9f3f3162ad25b';
344345
export const blockHash = `${blockHashTrimmed}999fc7e86699f60f2a3fb3ed9a646c6b`;
345346
export const blockHash2 = `${blockHashTrimmed}999fc7e86699f60f2a3fb3ed9a646c6c`;
346347
export const blockHash3 = `${blockHashTrimmed}999fc7e86699f60f2a3fb3ed9a646c6d`;

packages/relay/tests/lib/eth/eth_sendRawTransaction.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,5 +218,23 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function ()
218218
expect(`Error invoking RPC: ${response.message}`).to.equal(predefined.INTERNAL_ERROR(response.message).message);
219219
sinon.assert.calledOnce(sdkClientStub.submitEthereumTransaction);
220220
});
221+
222+
it('should throw precheck error for type=3 transactions', async function () {
223+
const type3tx = {
224+
...transaction,
225+
type: 3,
226+
maxFeePerBlobGas: transaction.gasPrice,
227+
blobVersionedHashes: [ethereumHash],
228+
};
229+
const signed = await signTransaction(type3tx);
230+
231+
await RelayAssertions.assertRejection(
232+
predefined.UNSUPPORTED_TRANSACTION_TYPE,
233+
ethImpl.sendRawTransaction,
234+
false,
235+
ethImpl,
236+
[signed, getRequestId()],
237+
);
238+
});
221239
});
222240
});

packages/relay/tests/lib/precheck.spec.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const registry = new Registry();
2525

2626
import pino from 'pino';
2727
import { Precheck } from '../../src/lib/precheck';
28-
import { expectedError, mockData, signTransaction } from '../helpers';
28+
import { blobVersionedHash, contractAddress1, expectedError, mockData, signTransaction } from '../helpers';
2929
import { MirrorNodeClient } from '../../src/lib/clients';
3030
import axios from 'axios';
3131
import MockAdapter from 'axios-mock-adapter';
@@ -61,6 +61,7 @@ describe('Precheck', async function () {
6161
);
6262
const oneTinyBar = ethers.parseUnits('1', 10);
6363
const defaultGasPrice = 720_000_000_000;
64+
const defaultGasLimit = 1_000_000;
6465
const defaultChainId = Number('0x12a');
6566
let precheck: Precheck;
6667
let mock: MockAdapter;
@@ -579,4 +580,48 @@ describe('Precheck', async function () {
579580
expect(error.code).to.equal(-32603);
580581
});
581582
});
583+
584+
describe('transactionType', async function () {
585+
const defaultTx = {
586+
value: oneTinyBar,
587+
gasPrice: defaultGasPrice,
588+
gasLimit: defaultGasLimit,
589+
chainId: defaultChainId,
590+
nonce: 5,
591+
to: contractAddress1,
592+
};
593+
594+
it('should accept legacy transactions', async () => {
595+
const signedLegacy = await signTransaction(defaultTx);
596+
expect(precheck.transactionType(ethers.Transaction.from(signedLegacy))).not.to.throw;
597+
});
598+
599+
it('should accept London transactions', async () => {
600+
const signedLondon = await signTransaction({
601+
...defaultTx,
602+
type: 2,
603+
maxPriorityFeePerGas: defaultGasPrice,
604+
maxFeePerGas: defaultGasPrice,
605+
});
606+
expect(precheck.transactionType(ethers.Transaction.from(signedLondon))).not.to.throw;
607+
});
608+
609+
it('should reject Cancun transactions', async () => {
610+
let error;
611+
try {
612+
const signedCancun = await signTransaction({
613+
...defaultTx,
614+
type: 3,
615+
maxFeePerBlobGas: defaultGasPrice,
616+
blobVersionedHashes: [blobVersionedHash],
617+
});
618+
precheck.transactionType(ethers.Transaction.from(signedCancun));
619+
} catch (e) {
620+
error = e;
621+
}
622+
expect(error).to.be.an.instanceOf(JsonRpcError);
623+
expect(error.message).to.equal(predefined.UNSUPPORTED_TRANSACTION_TYPE.message);
624+
expect(error.code).to.equal(predefined.UNSUPPORTED_TRANSACTION_TYPE.code);
625+
});
626+
});
582627
});

packages/server/tests/acceptance/rpc_batch3.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,4 +2021,40 @@ describe('@api-batch-3 RPC Server Acceptance Tests', function () {
20212021
}
20222022
});
20232023
});
2024+
2025+
describe('Shard Blob Transactions', async function () {
2026+
let defaultLondonTransactionData, defaultGasPrice, defaultGasLimit;
2027+
let defaultBlobVersionedHashes = ['0x6265617665726275696c642e6f7267476265617665726275696c642e6f726747'];
2028+
2029+
before(() => {
2030+
defaultGasPrice = numberTo0x(Assertions.defaultGasPrice);
2031+
defaultGasLimit = numberTo0x(3_000_000);
2032+
2033+
defaultLondonTransactionData = {
2034+
value: ONE_TINYBAR,
2035+
chainId: Number(CHAIN_ID),
2036+
maxPriorityFeePerGas: defaultGasPrice,
2037+
maxFeePerGas: defaultGasPrice,
2038+
gasLimit: defaultGasLimit,
2039+
};
2040+
});
2041+
2042+
it('Type 3 transactions are not supported for eth_sendRawTransaction', async () => {
2043+
const transaction = {
2044+
...defaultLondonTransactionData,
2045+
type: 3,
2046+
maxFeePerBlobGas: defaultGasPrice,
2047+
blobVersionedHashes: defaultBlobVersionedHashes,
2048+
};
2049+
2050+
const signedTx = await accounts[0].wallet.signTransaction(transaction);
2051+
await Assertions.assertPredefinedRpcError(
2052+
predefined.UNSUPPORTED_TRANSACTION_TYPE,
2053+
relay.sendRawTransaction,
2054+
false,
2055+
relay,
2056+
[signedTx, requestId],
2057+
);
2058+
});
2059+
});
20242060
});

0 commit comments

Comments
 (0)