Skip to content

Commit 0736e01

Browse files
authored
feat: remove transactions with status INVALID_ACCOUNT_ID from block-specific routes (#3177)
* chore: add excluded transaction statuses array Signed-off-by: nikolay <[email protected]> * chore: resolve comments Signed-off-by: nikolay <[email protected]> * chore: resolve comments Signed-off-by: nikolay <[email protected]> * chore: resolve comments Signed-off-by: nikolay <[email protected]> * chore: pull main Signed-off-by: nikolay <[email protected]> * chore: alphabet order Signed-off-by: nikolay <[email protected]> --------- Signed-off-by: nikolay <[email protected]>
1 parent ca044f1 commit 0736e01

File tree

6 files changed

+162
-101
lines changed

6 files changed

+162
-101
lines changed

docs/configuration.md

Lines changed: 62 additions & 61 deletions
Large diffs are not rendered by default.

packages/config-service/src/services/globalConfig.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ export class GlobalConfig {
183183
required: false,
184184
defaultValue: null,
185185
},
186+
HEDERA_SPECIFIC_REVERT_STATUSES: {
187+
envName: 'HEDERA_SPECIFIC_REVERT_STATUSES',
188+
type: 'string',
189+
required: false,
190+
defaultValue: '["WRONG_NONCE", "INVALID_ACCOUNT_ID"]',
191+
},
186192
FEE_HISTORY_MAX_RESULTS: {
187193
envName: 'FEE_HISTORY_MAX_RESULTS',
188194
type: 'number',

packages/relay/src/lib/eth.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,13 @@ import {
4848
formatContractResult,
4949
formatTransactionIdWithoutQueryParams,
5050
getFunctionSelector,
51-
hexToASCII,
5251
isHex,
5352
isValidEthereumAddress,
5453
nanOrNumberTo0x,
5554
nullableNumberTo0x,
5655
numberTo0x,
5756
parseNumericEnvVar,
5857
prepend0x,
59-
strip0x,
6058
toHash32,
6159
trimPrecedingZeros,
6260
weibarHexToTinyBarInt,
@@ -2298,18 +2296,15 @@ export class EthImpl implements Eth {
22982296
throw predefined.MAX_BLOCK_SIZE(blockResponse.count);
22992297
}
23002298

2301-
const isWrongNonce = (contractResult: { result: string; error_message: any }) => {
2302-
return (
2303-
contractResult.result === constants.TRANSACTION_RESULT_STATUS.WRONG_NONCE ||
2304-
hexToASCII(strip0x(contractResult.error_message ?? '')) === constants.TRANSACTION_RESULT_STATUS.WRONG_NONCE
2305-
);
2306-
};
2307-
23082299
// prepare transactionArray
23092300
let transactionArray: any[] = [];
23102301
for (const contractResult of contractResults) {
2311-
if (isWrongNonce(contractResult)) {
2312-
// skip wrong nonce transactions as they are not valid transactions which should be included in the block
2302+
// there are several hedera-specific validations that occur right before entering the evm
2303+
// if a transaction has reverted there, we should not include that tx in the block response
2304+
if (Utils.isRevertedDueToHederaSpecificValidation(contractResult)) {
2305+
this.logger.debug(
2306+
`${requestDetails.formattedRequestId} Transaction with hash ${contractResult.hash} is skipped due to hedera-specific validation failure (${contractResult.result})`,
2307+
);
23132308
continue;
23142309
}
23152310
contractResult.from = await this.resolveEvmAddress(contractResult.from, requestDetails, [constants.TYPE_ACCOUNT]);

packages/relay/src/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { PrivateKey } from '@hashgraph/sdk';
2222
import constants from './lib/constants';
2323
import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
2424
import crypto from 'crypto';
25+
import { hexToASCII, strip0x } from './formatters';
2526

2627
export class Utils {
2728
public static readonly IP_ADDRESS_REGEX = /\b((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}\b/g;
@@ -107,4 +108,21 @@ export class Utils {
107108

108109
return estimatedTxFee;
109110
}
111+
112+
/**
113+
* Check whether the transaction has reverted by a hedera-specific validation before the actual evm execution
114+
* @param contractResult
115+
* @returns {boolean}
116+
*/
117+
public static isRevertedDueToHederaSpecificValidation(contractResult: {
118+
result: string;
119+
error_message: any;
120+
}): boolean {
121+
// @ts-ignore
122+
const statuses = JSON.parse(ConfigService.get('HEDERA_SPECIFIC_REVERT_STATUSES'));
123+
return (
124+
statuses.includes(contractResult.result) ||
125+
statuses.includes(hexToASCII(strip0x(contractResult.error_message ?? '')))
126+
);
127+
}
110128
}

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

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -568,34 +568,45 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () {
568568
});
569569

570570
[false, true].forEach((showDetails) => {
571-
it(`eth_getBlockByNumber should skip wrong nonce transactions when showDetails = ${showDetails}`, async () => {
572-
// mirror node request mocks
573-
restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, DEFAULT_BLOCK);
574-
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOST_RECENT_BLOCK);
575-
restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, {
576-
results: [
577-
...defaultContractResults.results,
578-
{ ...defaultContractResults.results[0], result: 'WRONG_NONCE' },
579-
{ ...defaultContractResults.results[0], error_message: prepend0x(ASCIIToHex('WRONG_NONCE')) },
580-
],
571+
['WRONG_NONCE', 'INVALID_ACCOUNT_ID'].forEach((status) => {
572+
it(`eth_getBlockByNumber should skip ${status} transactions when showDetails = ${showDetails}`, async () => {
573+
// mirror node request mocks
574+
restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, DEFAULT_BLOCK);
575+
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOST_RECENT_BLOCK);
576+
restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, {
577+
results: [
578+
...defaultContractResults.results,
579+
{
580+
...defaultContractResults.results[0],
581+
result: status,
582+
hash: '0xf84b9a38205131431901ca6a945046369f5be81bb579167458d4992427d03bb1',
583+
},
584+
{
585+
...defaultContractResults.results[0],
586+
error_message: prepend0x(ASCIIToHex(status)),
587+
hash: '0x9c8d9d99e033c56bec1669a0ea68887b7df69ec1bac55899150b6ed5bc3f4b79',
588+
},
589+
],
590+
});
591+
restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS);
592+
593+
const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), showDetails, requestDetails);
594+
595+
RelayAssertions.assertBlock(
596+
result,
597+
{
598+
hash: BLOCK_HASH_TRIMMED,
599+
gasUsed: TOTAL_GAS_USED,
600+
number: BLOCK_NUMBER_HEX,
601+
parentHash: BLOCK_HASH_PREV_TRIMMED,
602+
timestamp: BLOCK_TIMESTAMP_HEX,
603+
// should not include the transaction with wrong nonce or invalid account id
604+
transactions: [CONTRACT_HASH_1, CONTRACT_HASH_2],
605+
receiptsRoot: DEFAULT_BLOCK_RECEIPTS_ROOT_HASH,
606+
},
607+
showDetails,
608+
);
581609
});
582-
restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS);
583-
584-
const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), showDetails, requestDetails);
585-
586-
RelayAssertions.assertBlock(
587-
result,
588-
{
589-
hash: BLOCK_HASH_TRIMMED,
590-
gasUsed: TOTAL_GAS_USED,
591-
number: BLOCK_NUMBER_HEX,
592-
parentHash: BLOCK_HASH_PREV_TRIMMED,
593-
timestamp: BLOCK_TIMESTAMP_HEX,
594-
transactions: [CONTRACT_HASH_1, CONTRACT_HASH_2], // should not include the transaction with wrong nonce
595-
receiptsRoot: DEFAULT_BLOCK_RECEIPTS_ROOT_HASH,
596-
},
597-
showDetails,
598-
);
599610
});
600611
});
601612
});

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
import { expect } from 'chai';
2222
import { Utils } from '../../src/utils';
2323
import constants from '../../src/lib/constants';
24-
import { overrideEnvsInMochaDescribe } from '../helpers';
25-
import { estimateFileTransactionsFee } from '../helpers';
24+
import { estimateFileTransactionsFee, overrideEnvsInMochaDescribe } from '../helpers';
2625
import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
26+
import { ASCIIToHex, prepend0x } from '../../src/formatters';
2727

2828
describe('Utils', () => {
2929
describe('addPercentageBufferToGasPrice', () => {
@@ -63,4 +63,34 @@ describe('Utils', () => {
6363
expect(result).to.eq(expectedResult);
6464
});
6565
});
66+
67+
describe('isRevertedDueToHederaSpecificValidation', () => {
68+
it('should not exclude transaction with status SUCCESS', () => {
69+
expect(Utils.isRevertedDueToHederaSpecificValidation({ result: 'SUCCESS', error_message: null })).to.be.false;
70+
});
71+
72+
it('should not exclude evm reverted transaction', () => {
73+
expect(
74+
Utils.isRevertedDueToHederaSpecificValidation({
75+
result: 'CONTRACT_REVERT_EXECUTED',
76+
error_message: 'Error',
77+
}),
78+
).to.be.false;
79+
});
80+
81+
// @ts-ignore
82+
JSON.parse(ConfigService.get('HEDERA_SPECIFIC_REVERT_STATUSES')).forEach((status) => {
83+
it(`should exclude transaction with result ${status}`, () => {
84+
expect(Utils.isRevertedDueToHederaSpecificValidation({ result: status, error_message: null })).to.be.true;
85+
});
86+
it(`should exclude transaction with error_message ${status}`, () => {
87+
expect(
88+
Utils.isRevertedDueToHederaSpecificValidation({
89+
result: '',
90+
error_message: prepend0x(ASCIIToHex(status)),
91+
}),
92+
).to.be.true;
93+
});
94+
});
95+
});
6696
});

0 commit comments

Comments
 (0)