Skip to content

Commit 06ab6e3

Browse files
authored
Check for Valid To and From Addresses on eth_call (#994)
* Adding validation for checking if From is a valid account address and To is a valid contract or HTS address --------- Signed-off-by: Alfredo Gutierrez <[email protected]>
1 parent 62bb731 commit 06ab6e3

File tree

5 files changed

+114
-44
lines changed

5 files changed

+114
-44
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,31 @@ export const predefined = {
171171
name: 'Invalid Contract Address',
172172
code: -32012,
173173
message: message
174-
})
174+
});
175+
},
176+
'NON_EXISTING_CONTRACT': (address) => {
177+
let message = `Non Existing Contract Address: ${address}.`;
178+
if (address && address.length) {
179+
message = `${message} Expected a Contract or Token Address.`;
180+
}
181+
182+
return new JsonRpcError({
183+
name: 'Non Existing Contract Address',
184+
code: -32013,
185+
message: message
186+
});
187+
},
188+
'NON_EXISTING_ACCOUNT': (address) => {
189+
let message = `Non Existing Account Address: ${address}.`;
190+
if (address && address.length) {
191+
message = `${message} Expected an Account Address.`;
192+
}
193+
194+
return new JsonRpcError({
195+
name: 'Non Existing Account Address',
196+
code: -32014,
197+
message: message
198+
});
175199
},
176200
'COULD_NOT_ESTIMATE_GAS_PRICE': new JsonRpcError({
177201
name: 'Could not estimate gas price',

packages/relay/src/lib/eth.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,19 @@ export class EthImpl implements Eth {
979979
throw predefined.INVALID_CONTRACT_ADDRESS(call.to);
980980
}
981981

982+
// If "From" is distinct from blank, we check is a valid account
983+
if(call.from) {
984+
const fromEntityType = await this.mirrorNodeClient.resolveEntityType(call.from, requestId, [constants.TYPE_ACCOUNT]);
985+
if (fromEntityType?.type !== constants.TYPE_ACCOUNT) {
986+
throw predefined.NON_EXISTING_ACCOUNT(call.from);
987+
}
988+
}
989+
// Check "To" is a valid Contract or HTS Address
990+
const toEntityType = await this.mirrorNodeClient.resolveEntityType(call.to, requestId, [constants.TYPE_TOKEN, constants.TYPE_CONTRACT]);
991+
if(!(toEntityType?.type === constants.TYPE_CONTRACT || toEntityType?.type === constants.TYPE_TOKEN)) {
992+
throw predefined.NON_EXISTING_CONTRACT(call.to);
993+
}
994+
982995
try {
983996
// Get a reasonable value for "gas" if it is not specified.
984997
let gas = Number(call.gas) || 400_000;
@@ -1002,7 +1015,7 @@ export class EthImpl implements Eth {
10021015
if (process.env.ETH_CALL_CONSENSUS == 'false') {
10031016
//temporary workaround until precompiles are implemented in Mirror node evm module
10041017
const isHts = await this.mirrorNodeClient.resolveEntityType(call.to, requestId, [constants.TYPE_TOKEN]);
1005-
if (!isHts) {
1018+
if (!(isHts?.type === constants.TYPE_TOKEN)) {
10061019
const callData = {
10071020
...call,
10081021
gas,

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

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ describe('Eth calls using MirrorNode', async function () {
145145
const gasUsedRatio = 0.5;
146146
const deployedBytecode = '0x608060405234801561001057600080fd5b5060405161078938038061078983398181016040528101906100329190';
147147
const mirrorNodeDeployedBytecode = '0x608060405234801561001057600080fd5b5060405161078938038061078983398181016040528101906100321234';
148+
const accountAddress1 = '0x13212A14deaf2775a5b3bEcC857806D5c719d3f2';
148149

149150
const defaultBlock = {
150151
'count': blockTransactionCount,
@@ -402,7 +403,7 @@ describe('Eth calls using MirrorNode', async function () {
402403
'value': '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925'
403404
}
404405
]
405-
}
406+
};
406407

407408
const defaultDetailedContractResults2 = {
408409
...defaultDetailedContractResults, ...{
@@ -494,7 +495,7 @@ describe('Eth calls using MirrorNode', async function () {
494495
...defaultContract,
495496
"address": contractAddress2,
496497
"contract_id": contractId2,
497-
}
498+
};
498499

499500
const defaultHTSToken =
500501
{
@@ -561,7 +562,7 @@ describe('Eth calls using MirrorNode', async function () {
561562
}
562563
]
563564
}
564-
}
565+
};
565566

566567

567568
this.afterEach(() => {
@@ -1252,7 +1253,7 @@ describe('Eth calls using MirrorNode', async function () {
12521253
});
12531254

12541255
it('should return balance from mirror node with block number passed as param the same as latest', async () => {
1255-
const blockNumber = "0x2710"
1256+
const blockNumber = "0x2710";
12561257
restMock.onGet(`blocks?limit=1&order=desc`).reply(200, {
12571258
blocks: [{
12581259
number: 10000
@@ -1270,7 +1271,7 @@ describe('Eth calls using MirrorNode', async function () {
12701271
});
12711272

12721273
it('should return balance from mirror node with block number passed as param, one behind latest', async () => {
1273-
const blockNumber = "0x270F"
1274+
const blockNumber = "0x270F";
12741275
restMock.onGet(`blocks?limit=1&order=desc`).reply(200, {
12751276
blocks: [{
12761277
number: 10000
@@ -1452,7 +1453,7 @@ describe('Eth calls using MirrorNode', async function () {
14521453
});
14531454

14541455
const resBalance = await ethImpl.getBalance(contractId1, '2');
1455-
const historicalBalance = EthImpl.numberTo0x((balance3) * constants.TINYBAR_TO_WEIBAR_COEF)
1456+
const historicalBalance = EthImpl.numberTo0x((balance3) * constants.TINYBAR_TO_WEIBAR_COEF);
14561457
expect(resBalance).to.equal(historicalBalance);
14571458
});
14581459

@@ -1471,7 +1472,7 @@ describe('Eth calls using MirrorNode', async function () {
14711472
});
14721473

14731474
const resBalance = await ethImpl.getBalance(contractId1, '2');
1474-
const historicalBalance = EthImpl.numberTo0x((balance3 - 175) * constants.TINYBAR_TO_WEIBAR_COEF)
1475+
const historicalBalance = EthImpl.numberTo0x((balance3 - 175) * constants.TINYBAR_TO_WEIBAR_COEF);
14751476
expect(resBalance).to.equal(historicalBalance);
14761477
});
14771478

@@ -1485,7 +1486,7 @@ describe('Eth calls using MirrorNode', async function () {
14851486
});
14861487

14871488
const resBalance = await ethImpl.getBalance(contractId1, '2');
1488-
const historicalBalance = EthImpl.numberTo0x((balance3 + 175) * constants.TINYBAR_TO_WEIBAR_COEF)
1489+
const historicalBalance = EthImpl.numberTo0x((balance3 + 175) * constants.TINYBAR_TO_WEIBAR_COEF);
14891490
expect(resBalance).to.equal(historicalBalance);
14901491
});
14911492

@@ -1500,7 +1501,7 @@ describe('Eth calls using MirrorNode', async function () {
15001501
});
15011502

15021503
const resBalance = await ethImpl.getBalance(contractId1, '2');
1503-
const historicalBalance = EthImpl.numberTo0x((balance3 + 65) * constants.TINYBAR_TO_WEIBAR_COEF)
1504+
const historicalBalance = EthImpl.numberTo0x((balance3 + 65) * constants.TINYBAR_TO_WEIBAR_COEF);
15041505
expect(resBalance).to.equal(historicalBalance);
15051506
});
15061507

@@ -1526,7 +1527,7 @@ describe('Eth calls using MirrorNode', async function () {
15261527
});
15271528

15281529
const resBalance = await ethImpl.getBalance(contractId1, '2');
1529-
const historicalBalance = EthImpl.numberTo0x((balance3 + 65) * constants.TINYBAR_TO_WEIBAR_COEF)
1530+
const historicalBalance = EthImpl.numberTo0x((balance3 + 65) * constants.TINYBAR_TO_WEIBAR_COEF);
15301531
expect(resBalance).to.equal(historicalBalance);
15311532
});
15321533

@@ -1715,7 +1716,7 @@ describe('Eth calls using MirrorNode', async function () {
17151716
{...defaultLogs.logs[2], address: "0x0000000000000000000000000000000002131953"},
17161717
{...defaultLogs.logs[3], address: "0x0000000000000000000000000000000002131954"}
17171718
]
1718-
}
1719+
};
17191720
const filteredLogs = {
17201721
logs: [
17211722
{...defaultLogs.logs[0], address: "0x67D8d32E9Bf1a9968a5ff53B87d777Aa8EBBEe69"},
@@ -2006,7 +2007,7 @@ describe('Eth calls using MirrorNode', async function () {
20062007
const result = await ethImpl.getLogs(null, null, null, null, defaultNullLogTopics);
20072008

20082009
expect(result).to.exist;
2009-
expect(result[0].topics.length).to.eq(defaultLogs4[0].topics.length)
2010+
expect(result[0].topics.length).to.eq(defaultLogs4[0].topics.length);
20102011
for (let index = 0; index < result[0].topics.length; index++) {
20112012
expect(result[0].topics[index]).to.eq(defaultLogs4[0].topics[index]);
20122013
}
@@ -2158,7 +2159,7 @@ describe('Eth calls using MirrorNode', async function () {
21582159

21592160
restMock.onGet('blocks?limit=1&order=desc').reply(200, {blocks: [{...defaultBlock, number: 10}]});
21602161
restMock.onGet(`network/fees?timestamp=lte:${defaultBlock.timestamp.to}`).reply(200, defaultNetworkFees);
2161-
Array.from(Array(11).keys()).map(blockNumber => restMock.onGet(`blocks/${blockNumber}`).reply(200, {...defaultBlock, number: blockNumber}))
2162+
Array.from(Array(11).keys()).map(blockNumber => restMock.onGet(`blocks/${blockNumber}`).reply(200, {...defaultBlock, number: blockNumber}));
21622163

21632164
const feeHistory = await ethImpl.feeHistory(200, '0x9', [0]);
21642165

@@ -2376,9 +2377,16 @@ describe('Eth calls using MirrorNode', async function () {
23762377

23772378
after(() => {
23782379
process.env.ETH_CALL_CONSENSUS = initialEthCallConesneusFF;
2379-
})
2380+
});
23802381

23812382
it('eth_call with no gas', async function () {
2383+
restMock.onGet(`contracts/${accountAddress1}`).reply(404);
2384+
restMock.onGet(`accounts/${accountAddress1}`).reply(200, {
2385+
account: "0.0.1723",
2386+
evm_address: accountAddress1
2387+
});
2388+
restMock.onGet(`contracts/${contractAddress2}`).reply(200, defaultContract2);
2389+
23822390
sdkClientStub.submitContractCallQuery.returns({
23832391
asBytes: function () {
23842392
return Uint8Array.of(0);
@@ -2387,12 +2395,12 @@ describe('Eth calls using MirrorNode', async function () {
23872395
);
23882396

23892397
const result = await ethImpl.call({
2390-
"from": contractAddress1,
2398+
"from": accountAddress1,
23912399
"to": contractAddress2,
23922400
"data": contractCallData,
23932401
}, 'latest');
23942402

2395-
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, 400_000, contractAddress1, 'eth_call');
2403+
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, 400_000, accountAddress1, 'eth_call');
23962404
expect(result).to.equal("0x00");
23972405
});
23982406

@@ -2405,12 +2413,12 @@ describe('Eth calls using MirrorNode', async function () {
24052413
);
24062414

24072415
const result = await ethImpl.call({
2408-
"from": contractAddress1,
2416+
"from": accountAddress1,
24092417
"to": contractAddress2,
24102418
"gas": maxGasLimitHex
24112419
}, 'latest');
24122420

2413-
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, undefined, maxGasLimit, contractAddress1, 'eth_call');
2421+
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, undefined, maxGasLimit, accountAddress1, 'eth_call');
24142422
expect(result).to.equal("0x00");
24152423
});
24162424

@@ -2441,13 +2449,13 @@ describe('Eth calls using MirrorNode', async function () {
24412449
);
24422450

24432451
const result = await ethImpl.call({
2444-
"from": contractAddress1,
2452+
"from": accountAddress1,
24452453
"to": contractAddress2,
24462454
"data": contractCallData,
24472455
"gas": maxGasLimitHex
24482456
}, 'latest');
24492457

2450-
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, maxGasLimit, contractAddress1, 'eth_call');
2458+
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, maxGasLimit, accountAddress1, 'eth_call');
24512459
expect(result).to.equal("0x00");
24522460
});
24532461

@@ -2462,7 +2470,7 @@ describe('Eth calls using MirrorNode', async function () {
24622470

24632471
for (let index = 0; index < 3; index++) {
24642472
const result = await ethImpl.call({
2465-
"from": contractAddress1,
2473+
"from": accountAddress1,
24662474
"to": contractAddress2,
24672475
"data": contractCallData,
24682476
"gas": maxGasLimitHex
@@ -2475,7 +2483,7 @@ describe('Eth calls using MirrorNode', async function () {
24752483
await new Promise(r => setTimeout(r, 200));
24762484
try {
24772485
await ethImpl.call({
2478-
"from": contractAddress1,
2486+
"from": accountAddress1,
24792487
"to": contractAddress2,
24802488
"data": contractCallData,
24812489
"gas": maxGasLimitHex
@@ -2497,13 +2505,13 @@ describe('Eth calls using MirrorNode', async function () {
24972505
);
24982506

24992507
const result = await ethImpl.call({
2500-
"from": contractAddress1,
2508+
"from": accountAddress1,
25012509
"to": contractAddress2,
25022510
"data": contractCallData,
25032511
"gas": 50_000_000
25042512
}, 'latest');
25052513

2506-
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, 15_000_000, contractAddress1, 'eth_call');
2514+
sinon.assert.calledWith(sdkClientStub.submitContractCallQuery, contractAddress2, contractCallData, 15_000_000, accountAddress1, 'eth_call');
25072515
expect(result).to.equal("0x00");
25082516
});
25092517
});
@@ -2512,7 +2520,7 @@ describe('Eth calls using MirrorNode', async function () {
25122520
sdkClientStub.submitContractCallQuery.throws(predefined.CONTRACT_REVERT(defaultErrorMessage));
25132521

25142522
const result = await ethImpl.call({
2515-
"from": contractAddress1,
2523+
"from": accountAddress1,
25162524
"to": contractAddress2,
25172525
"data": contractCallData,
25182526
"gas": maxGasLimitHex
@@ -2555,7 +2563,7 @@ describe('Eth calls using MirrorNode', async function () {
25552563
const defaultCallData = {
25562564
"gas": 400000,
25572565
"value": null
2558-
}
2566+
};
25592567
let initialEthCallConesneusFF;
25602568

25612569
before(() => {
@@ -2565,17 +2573,17 @@ describe('Eth calls using MirrorNode', async function () {
25652573

25662574
after(() => {
25672575
process.env.ETH_CALL_CONSENSUS = initialEthCallConesneusFF;
2568-
})
2576+
});
25692577

25702578
//temporary workaround until precompiles are implemented in Mirror node evm module
25712579
beforeEach(() => {
25722580
restMock.onGet(`tokens/${defaultContractResults.results[1].contract_id}`).reply(404, null);
2573-
})
2581+
});
25742582

25752583
it('eth_call with no gas', async function () {
25762584
const callData = {
25772585
...defaultCallData,
2578-
"from": contractAddress1,
2586+
"from": accountAddress1,
25792587
"to": contractAddress2,
25802588
"data": contractCallData
25812589
};
@@ -2588,7 +2596,7 @@ describe('Eth calls using MirrorNode', async function () {
25882596
it('eth_call with no data', async function () {
25892597
const callData = {
25902598
...defaultCallData,
2591-
"from": contractAddress1,
2599+
"from": accountAddress1,
25922600
"to": contractAddress2,
25932601
"gas": maxGasLimit
25942602
};
@@ -2613,7 +2621,7 @@ describe('Eth calls using MirrorNode', async function () {
26132621
it('eth_call with all fields', async function () {
26142622
const callData = {
26152623
...defaultCallData,
2616-
"from": contractAddress1,
2624+
"from": accountAddress1,
26172625
"to": contractAddress2,
26182626
"data": contractCallData,
26192627
"gas": maxGasLimit
@@ -2626,7 +2634,7 @@ describe('Eth calls using MirrorNode', async function () {
26262634
it('caps gas at 15_000_000', async function () {
26272635
const callData = {
26282636
...defaultCallData,
2629-
"from": contractAddress1,
2637+
"from": accountAddress1,
26302638
"to": contractAddress2,
26312639
"data": contractCallData,
26322640
"gas": 50_000_000
@@ -2639,7 +2647,7 @@ describe('Eth calls using MirrorNode', async function () {
26392647
it('SDK returns a precheck error', async function () {
26402648
const callData = {
26412649
...defaultCallData,
2642-
"from": contractAddress1,
2650+
"from": accountAddress1,
26432651
"to": contractAddress2,
26442652
"data": contractCallData,
26452653
"gas": maxGasLimit
@@ -2786,7 +2794,7 @@ describe('Eth calls using MirrorNode', async function () {
27862794
});
27872795

27882796
it('eth_getStorageAt should return EthImpl.zeroHex32Byte when state_changes is null', async function () {
2789-
defaultDetailedContractResultsNullStateChange
2797+
defaultDetailedContractResultsNullStateChange;
27902798
restMock.onGet(`blocks/${blockNumber}`).reply(200, defaultBlock);
27912799
restMock.onGet(`contracts/${contractAddress1}/results?timestamp=lte:${defaultBlock.timestamp.to}&limit=1&order=desc`).reply(200, defaultContractResults);
27922800
restMock.onGet(`contracts/${contractAddress1}/results/${contractTimestamp1}`).reply(200, defaultDetailedContractResultsNullStateChange);
@@ -2795,7 +2803,7 @@ describe('Eth calls using MirrorNode', async function () {
27952803
});
27962804

27972805
it('eth_getStorageAt should return EthImpl.zeroHex32Byte when state_changes is an empty array', async function () {
2798-
defaultDetailedContractResultsNullStateChange
2806+
defaultDetailedContractResultsNullStateChange;
27992807
restMock.onGet(`blocks/${blockNumber}`).reply(200, defaultBlock);
28002808
restMock.onGet(`contracts/${contractAddress1}/results?timestamp=lte:${defaultBlock.timestamp.to}&limit=1&order=desc`).reply(200, defaultContractResults);
28012809
restMock.onGet(`contracts/${contractAddress1}/results/${contractTimestamp1}`).reply(200, defaultDetailedContractResultsEmptyArrayStateChange);

0 commit comments

Comments
 (0)