Skip to content
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
1 change: 0 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ Unless you need to set a non-default value, it is recommended to only populate o
| `CLIENT_TRANSPORT_SECURITY` | "false" | Flag to enable or disable TLS for both networks. |
| `CONSENSUS_MAX_EXECUTION_TIME` | "15000" | Maximum time in ms the SDK will wait when submitting a transaction/query before throwing a TIMEOUT error. |
| `CONTRACT_CALL_GAS_LIMIT` | "50_000_000" | Maximum gas limit applied to eth_call endpoint networks, the Relay will accept up to 50M but keep it capped at 15M for the actual call. |
| `CONTRACT_CODE_SIZE_LIMIT` | 24576 | Maximum contract code size in bytes (24KB by default) allowed for contract deployment transactions. This limit is enforced during transaction validation to prevent excessive gas consumption from oversized contracts. |
| `CONTRACT_QUERY_TIMEOUT_RETRIES` | "3" | Maximum retries for failed contract call query with timeout exceeded error |
| `DEBUG_API_ENABLED` | "false" | Enables all debug related methods: `debug_traceTransaction` |
| `DEFAULT_RATE_LIMIT` | "200" | default fallback rate limit, if no other is configured. |
Expand Down
6 changes: 0 additions & 6 deletions packages/config-service/src/services/globalConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,6 @@ const _CONFIG = {
required: false,
defaultValue: 50_000_000,
},
CONTRACT_CODE_SIZE_LIMIT: {
envName: 'CONTRACT_CODE_SIZE_LIMIT',
type: 'number',
required: false,
defaultValue: 24576, // 24KB
},
CONTRACT_QUERY_TIMEOUT_RETRIES: {
envName: 'CONTRACT_QUERY_TIMEOUT_RETRIES',
type: 'number',
Expand Down
1 change: 0 additions & 1 deletion packages/relay/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,6 @@ export default {

MAX_TRANSACTION_FEE_THRESHOLD: ConfigService.get('MAX_TRANSACTION_FEE_THRESHOLD'),
SEND_RAW_TRANSACTION_SIZE_LIMIT: ConfigService.get('SEND_RAW_TRANSACTION_SIZE_LIMIT'),
CONTRACT_CODE_SIZE_LIMIT: ConfigService.get('CONTRACT_CODE_SIZE_LIMIT'),
CALL_DATA_SIZE_LIMIT: ConfigService.get('CALL_DATA_SIZE_LIMIT'),

INVALID_EVM_INSTRUCTION: '0xfe',
Expand Down
5 changes: 0 additions & 5 deletions packages/relay/src/lib/errors/JsonRpcError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,6 @@ export const predefined = {
code: -32201,
message: `Oversized data: call data size ${actualSize}, call data size limit ${expectedSize}`,
}),
CONTRACT_CODE_SIZE_LIMIT_EXCEEDED: (actualSize: number, expectedSize: number) =>
new JsonRpcError({
code: -32201,
message: `Oversized data: contract code size ${actualSize}, contract code size limit ${expectedSize}`,
}),
BATCH_REQUESTS_DISABLED: new JsonRpcError({
code: -32202,
message: 'Batch requests are disabled',
Expand Down
18 changes: 0 additions & 18 deletions packages/relay/src/lib/precheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export class Precheck {
networkGasPriceInWeiBars: number,
requestDetails: RequestDetails,
): Promise<void> {
this.contractCodeSize(parsedTx);
this.callDataSize(parsedTx);
this.transactionSize(parsedTx);
this.transactionType(parsedTx, requestDetails);
Expand Down Expand Up @@ -383,21 +382,4 @@ export class Precheck {
}
}
}

/**
* Validates that the contract code size is within the allowed limit.
* This check is only performed for contract creation transactions (where tx.to is null).
* This limits contract code size to prevent excessive gas consumption.
*
* @param {Transaction} tx - The transaction to validate.
* @throws {JsonRpcError} If the contract code size exceeds the configured limit.
*/
contractCodeSize(tx: Transaction): void {
if (!tx.to) {
const contractCodeSize = tx.data.replace('0x', '').length / 2;
if (contractCodeSize > constants.CONTRACT_CODE_SIZE_LIMIT) {
throw predefined.CONTRACT_CODE_SIZE_LIMIT_EXCEEDED(contractCodeSize, constants.CONTRACT_CODE_SIZE_LIMIT);
}
}
}
}
84 changes: 0 additions & 84 deletions packages/relay/tests/lib/precheck.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,90 +769,6 @@ describe('Precheck', async function () {
});
});

describe('contractCodeSize', function () {
const defaultTx = {
value: ONE_TINYBAR_IN_WEI_HEX,
gasPrice: defaultGasPrice,
gasLimit: defaultGasLimit,
chainId: defaultChainId,
nonce: 5,
};

// Helper function to create a transaction with specified data and to address
const createTransaction = async (data: string, to?: string) => {
const wallet = ethers.Wallet.createRandom();
const txParams = {
...defaultTx,
from: wallet.address,
data: data,
};

if (to) {
txParams['to'] = to;
}

const signed = await wallet.signTransaction(txParams);
return ethers.Transaction.from(signed);
};

it('should accept contract creation with code size within limit', async () => {
// Create data that is within the limit
const dataSize = constants.CONTRACT_CODE_SIZE_LIMIT - 1;
const data = '0x' + '00'.repeat(dataSize);

const tx = await createTransaction(data);

expect(() => precheck.contractCodeSize(tx)).not.to.throw();
});

it('should accept contract creation with code size at the limit', async () => {
// Create data that is exactly at the limit
const dataSize = constants.CONTRACT_CODE_SIZE_LIMIT;
const data = '0x' + '00'.repeat(dataSize);

const tx = await createTransaction(data);

expect(() => precheck.contractCodeSize(tx)).not.to.throw();
});

it('should reject contract creation with code size exceeding limit', async () => {
// Create data that exceeds the limit
const dataSize = constants.CONTRACT_CODE_SIZE_LIMIT + 1;
const data = '0x' + '00'.repeat(dataSize);

const tx = await createTransaction(data);

try {
precheck.contractCodeSize(tx);
expect('Transaction should have been rejected');
} catch (error) {
expect(error).to.be.an.instanceOf(JsonRpcError);
const expectedError = predefined.CONTRACT_CODE_SIZE_LIMIT_EXCEEDED(
dataSize,
constants.CONTRACT_CODE_SIZE_LIMIT,
);
expect(error).to.deep.equal(expectedError);
}
});

it('should not check code size for regular transactions (with to address)', async () => {
// Create data that exceeds the limit but has a to address
const dataSize = constants.CONTRACT_CODE_SIZE_LIMIT + 1;
const data = '0x' + '00'.repeat(dataSize);

const tx = await createTransaction(data, contractAddress1);

// Should not throw even though data size exceeds limit
expect(() => precheck.contractCodeSize(tx)).not.to.throw();
});

it('should handle empty data for contract creation', async () => {
const tx = await createTransaction('0x');

expect(() => precheck.contractCodeSize(tx)).not.to.throw();
});
});

describe('transactionType', async function () {
const defaultTx = {
value: ONE_TINYBAR_IN_WEI_HEX,
Expand Down
20 changes: 1 addition & 19 deletions packages/server/tests/acceptance/rpc_batch1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1479,24 +1479,6 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () {
expect(fileInfo.size.toNumber()).to.eq(0);
});

it('should execute "eth_sendRawTransaction" and fail when deploying too large contract', async function () {
const gasPrice = await relay.gasPrice(requestId);
const transaction = {
type: 2,
chainId: Number(CHAIN_ID),
nonce: await relay.getAccountNonce(accounts[1].address, requestId),
maxPriorityFeePerGas: gasPrice,
maxFeePerGas: gasPrice,
gasLimit: defaultGasLimit,
data: '0x' + '00'.repeat(132221),
};

const signedTx = await accounts[1].wallet.signTransaction(transaction);
const error = predefined.CONTRACT_CODE_SIZE_LIMIT_EXCEEDED(132221, Constants.CONTRACT_CODE_SIZE_LIMIT);

await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]);
});

it('should execute "eth_sendRawTransaction" of type 1 and deploy a real contract', async function () {
//omitting the "to" and "nonce" fields when creating a new contract
const transaction = {
Expand Down Expand Up @@ -1552,7 +1534,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () {
maxPriorityFeePerGas: gasPrice,
maxFeePerGas: gasPrice,
gasLimit: Constants.MAX_TRANSACTION_FEE_THRESHOLD,
data: '0x' + '00'.repeat(Constants.CONTRACT_CODE_SIZE_LIMIT),
data: '0x' + '00'.repeat(100),
};

const signedTx = await accounts[2].wallet.signTransaction(transaction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,76 +150,6 @@ describe('@sendRawTransactionExtension Acceptance Tests', function () {
await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestDetails]);
});
});

describe('contractCodeSize', function () {
it('@release should execute "eth_sendRawTransaction" and deploy a contract with code size within the CONTRACT_CODE_SIZE_LIMIT - 24kb limit', async function () {
const gasPrice = await relay.gasPrice(requestId);

// create a regular deployment transaction with contract code size within the CONTRACT_CODE_SIZE_LIMIT - 24kb limit
const transaction = {
type: 2,
chainId: Number(CHAIN_ID),
nonce: await relay.getAccountNonce(accounts[1].address, requestId),
maxPriorityFeePerGas: gasPrice,
maxFeePerGas: gasPrice,
gasLimit: defaultGasLimit,
data: '0x' + '00'.repeat(5120),
};

const signedTx = await accounts[1].wallet.signTransaction(transaction);
const transactionHash = await relay.sendRawTransaction(signedTx, requestId);
await relay.pollForValidTransactionReceipt(transactionHash);

const info = await mirrorNode.get(`/contracts/results/${transactionHash}`, requestId);
expect(info).to.have.property('contract_id');
expect(info.contract_id).to.not.be.null;
expect(info).to.have.property('created_contract_ids');
expect(info.created_contract_ids.length).to.be.equal(1);
});

it('@release should fail "eth_sendRawTransaction" for contract with code size exceeding the CONTRACT_CODE_SIZE_LIMIT - 24kb limit', async function () {
const gasPrice = await relay.gasPrice(requestId);
// Create a deployment transaction with contract code size exceeding CONTRACT_CODE_SIZE_LIMIT
const transaction = {
type: 2,
chainId: Number(CHAIN_ID),
nonce: await relay.getAccountNonce(accounts[1].address, requestId),
maxPriorityFeePerGas: gasPrice,
maxFeePerGas: gasPrice,
gasLimit: defaultGasLimit,
data: '0x' + '00'.repeat(Constants.CONTRACT_CODE_SIZE_LIMIT + 1024), // exceeds the limit by 1KB
};

const signedTx = await accounts[1].wallet.signTransaction(transaction);
const contractCodeSize = (transaction.data.length - 2) / 2;
const error = predefined.CONTRACT_CODE_SIZE_LIMIT_EXCEEDED(
contractCodeSize,
Constants.CONTRACT_CODE_SIZE_LIMIT,
);

await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestDetails]);
});

it('@release should pass precheck and execute "eth_sendRawTransaction" for a regular transaction i.e. non contract deployment transaction with data exceeding the CONTRACT_CODE_SIZE_LIMIT - 24kb limit', async function () {
const gasPrice = await relay.gasPrice(requestId);
// Create a transaction with large data but sent to an existing address (not contract creation)
const transaction = {
type: 2,
chainId: Number(CHAIN_ID),
nonce: await relay.getAccountNonce(accounts[1].address, requestId),
maxPriorityFeePerGas: gasPrice,
maxFeePerGas: gasPrice,
gasLimit: defaultGasLimit,
to: accounts[0].address, // Sending to existing address, so code size check doesn't apply
data: '0x' + '00'.repeat(Constants.CONTRACT_CODE_SIZE_LIMIT + 1024), // exceeds the limit by 1KB
};

const signedTx = await accounts[1].wallet.signTransaction(transaction);
const transactionHash = await relay.sendRawTransaction(signedTx, requestId);
const info = await mirrorNode.get(`/contracts/results/${transactionHash}`, requestId);
expect(info).to.exist;
});
});
});

describe('Jumbo Transaction', function () {
Expand Down
Loading