Skip to content

Commit

Permalink
we now include contract in the check as well + removed duplicate call…
Browse files Browse the repository at this point in the history
… to mirror node

Signed-off-by: Simeon Nakov <simeon.nakov@limechain.tech>
  • Loading branch information
simzzz committed Feb 5, 2025
1 parent 18c79ec commit 8a52152
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 41 deletions.
9 changes: 2 additions & 7 deletions packages/relay/src/lib/clients/mirrorNodeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1155,14 +1155,9 @@ export class MirrorNodeClient {
);
}

public async getTokenById(tokenId: string, requestDetails: RequestDetails, retries?: number, timestamp?: string) {
const queryParamObject = {};
if (timestamp) {
this.setQueryParam(queryParamObject, 'timestamp', timestamp);
}
const queryParams = this.getQueryParams(queryParamObject);
public async getTokenById(tokenId: string, requestDetails: RequestDetails, retries?: number) {
return this.get(
`${MirrorNodeClient.GET_TOKENS_ENDPOINT}/${tokenId}${queryParams}`,
`${MirrorNodeClient.GET_TOKENS_ENDPOINT}/${tokenId}`,
MirrorNodeClient.GET_TOKENS_ENDPOINT,
requestDetails,
retries,
Expand Down
45 changes: 22 additions & 23 deletions packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
*/

import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
import { FileId, Hbar, PrecheckStatusError } from '@hashgraph/sdk';
import { ContractId, FileId, Hbar, PrecheckStatusError } from '@hashgraph/sdk';
import crypto from 'crypto';
import { Transaction as EthersTransaction } from 'ethers';
import { Logger } from 'pino';
Expand Down Expand Up @@ -1209,30 +1209,11 @@ export class EthImpl implements Eth {
constants.TYPE_CONTRACT,
constants.TYPE_TOKEN,
]);

if (result) {
if (result?.type === constants.TYPE_TOKEN) {
if (blockNumber && !this.common.blockTagIsLatestOrPending(blockNumber)) {
let blockNumberInt;
if (blockNumber === EthImpl.blockEarliest) {
blockNumberInt = 0;
} else {
blockNumberInt = parseInt(blockNumber, 16);
}
const blockInfo = await this.mirrorNodeClient.getBlock(blockNumberInt, requestDetails);

if (!blockInfo) {
throw predefined.UNKNOWN_BLOCK(`Block number ${blockNumber} does not exist`);
}

const tokenId = Utils.addressToTokenId(address);
const tokenInfo = await this.mirrorNodeClient.getTokenById(
tokenId,
requestDetails,
undefined,
blockInfo.timestamp.to,
);
if (!tokenInfo) {
const blockInfo = await this.getBlockInfo(blockNumber, requestDetails);
if (blockInfo) {
if (parseFloat(result.entity?.created_timestamp) > parseFloat(blockInfo.timestamp.to)) {
return EthImpl.emptyHex;

Check warning on line 1217 in packages/relay/src/lib/eth.ts

View check run for this annotation

Codecov / codecov/patch

packages/relay/src/lib/eth.ts#L1217

Added line #L1217 was not covered by tests
}
}
Expand All @@ -1241,6 +1222,12 @@ export class EthImpl implements Eth {
}
return EthImpl.redirectBytecodeAddressReplace(address);
} else if (result?.type === constants.TYPE_CONTRACT) {
const blockInfo = await this.getBlockInfo(blockNumber, requestDetails);
if (blockInfo) {
if (parseFloat(result.entity?.created_timestamp) > parseFloat(blockInfo.timestamp.to)) {
return EthImpl.emptyHex;

Check warning on line 1228 in packages/relay/src/lib/eth.ts

View check run for this annotation

Codecov / codecov/patch

packages/relay/src/lib/eth.ts#L1228

Added line #L1228 was not covered by tests
}
}
if (result?.entity.runtime_bytecode !== EthImpl.emptyHex) {
const prohibitedOpcodes = ['CALLCODE', 'DELEGATECALL', 'SELFDESTRUCT', 'SUICIDE'];
const opcodes = asm.disassemble(result?.entity.runtime_bytecode);
Expand Down Expand Up @@ -2858,4 +2845,16 @@ export class EthImpl implements Eth {
const exchangeRateInCents = currentNetworkExchangeRate.cent_equivalent / currentNetworkExchangeRate.hbar_equivalent;
return exchangeRateInCents;
}

private async getBlockInfo(blockNumber: string | null, requestDetails: RequestDetails): Promise<any> {
if (blockNumber && !this.common.blockTagIsLatestOrPending(blockNumber)) {
const blockNumberInt = blockNumber === EthImpl.blockEarliest ? 0 : parseInt(blockNumber, 16);
const blockInfo = await this.mirrorNodeClient.getBlock(blockNumberInt, requestDetails);
if (!blockInfo) {
throw predefined.UNKNOWN_BLOCK(`Block number ${blockNumber} does not exist`);
}
return blockInfo;
}
return null;
}
}
60 changes: 50 additions & 10 deletions packages/relay/tests/lib/eth/eth_getCode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*
*/

import { ContractId } from '@hashgraph/sdk';
import { expect, use } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import sinon from 'sinon';
Expand Down Expand Up @@ -48,8 +49,9 @@ let getSdkClientStub: sinon.SinonStub;
describe('@ethGetCode using MirrorNode', async function () {
this.timeout(10000);
const { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv();
const validBlockParam = [null, 'earliest', 'latest', 'pending', 'finalized', 'safe', '0x0', '0x369ABF'];
const invalidBlockParam = ['hedera', 'ethereum', '0xhbar', '0x369ABF369ABF369ABF369ABF'];
const earlyBlockParams = ['0x0', '0x369ABF', 'earliest'];
const otherValidBlockParams = [null, 'latest', 'pending', 'finalized', 'safe'];
const invalidBlockParam = ['hedera', 'ethereum', '0xhbar', '0x369ABF369ABF369ABF'];

const requestDetails = new RequestDetails({ requestId: 'eth_getCodeTest', ipAddress: '0.0.0.0' });

Expand Down Expand Up @@ -131,8 +133,19 @@ describe('@ethGetCode using MirrorNode', async function () {
expect(res).to.equal(EthImpl.invalidEVMInstruction);
});

validBlockParam.forEach((blockParam) => {
it(`should pass the validate param check with blockParam=${blockParam} and return the bytecode`, async () => {
earlyBlockParams.forEach((blockParam) => {
it(`should return empty bytecode for early block param ${blockParam}`, async () => {
const paramAsInt = blockParam === 'earliest' ? 0 : parseInt(blockParam, 16);
restMock.onGet(`blocks/${paramAsInt}`).reply(200, {
timestamp: { to: '1532175203.847228000' },
});
const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, blockParam, requestDetails);
expect(res).to.equal(EthImpl.emptyHex);
});
});

otherValidBlockParams.forEach((blockParam) => {
it(`should return deployed bytecode for block param ${blockParam}`, async () => {
const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, blockParam, requestDetails);
expect(res).to.equal(MIRROR_NODE_DEPLOYED_BYTECODE);
});
Expand Down Expand Up @@ -168,11 +181,27 @@ describe('@ethGetCode using MirrorNode', async function () {
timestamp: { to: blockToTimestamp },
});

restMock.onGet(`tokens/0.0.${parseInt(HTS_TOKEN_ADDRESS, 16)}?timestamp=${blockToTimestamp}`).reply(404, null);
const res = await ethImpl.getCode(HTS_TOKEN_ADDRESS, blockNumberBeforeCreation, requestDetails);
expect(res).to.equal(EthImpl.emptyHex);
});

it('should return empty bytecode for contract before creation block', async () => {
const blockNumberBeforeCreation = '0x152a4aa';
const blockToTimestamp = '1632175203.847228000';
const contractId = ContractId.fromEvmAddress(0, 0, CONTRACT_ADDRESS_1);

restMock.onGet(`contracts/${contractId.toString()}`).reply(200, {
...DEFAULT_CONTRACT,
created_timestamp: '1632175205.855270000',
});
restMock.onGet(`blocks/${parseInt(blockNumberBeforeCreation, 16)}`).reply(200, {
timestamp: { to: blockToTimestamp },
});

const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, blockNumberBeforeCreation, requestDetails);
expect(res).to.equal(EthImpl.emptyHex);
});

it('should return redirect bytecode for HTS token after creation block', async () => {
const blockNumberAfterCreation = '0x152a4ab';
const blockToTimestamp = '1632175206.000000000';
Expand All @@ -186,11 +215,6 @@ describe('@ethGetCode using MirrorNode', async function () {
timestamp: { to: blockToTimestamp },
});

restMock.onGet(`tokens/0.0.${parseInt(HTS_TOKEN_ADDRESS, 16)}?timestamp=${blockToTimestamp}`).reply(200, {
...DEFAULT_HTS_TOKEN,
created_timestamp: '1632175205.855270000',
});

const res = await ethImpl.getCode(HTS_TOKEN_ADDRESS, blockNumberAfterCreation, requestDetails);
const expectedRedirectBytecode = `6080604052348015600f57600080fd5b506000610167905077618dc65e${HTS_TOKEN_ADDRESS.slice(
2,
Expand Down Expand Up @@ -219,5 +243,21 @@ describe('@ethGetCode using MirrorNode', async function () {
`Block number ${futureBlockNumber} does not exist`,
);
});

it('should return empty bytecode for contract when earliest block is queried', async () => {
const blockToTimestamp = '1632175203.847228000';
const contractId = ContractId.fromEvmAddress(0, 0, CONTRACT_ADDRESS_1);

restMock.onGet(`contracts/${contractId.toString()}`).reply(200, {
...DEFAULT_CONTRACT,
created_timestamp: '1632175205.855270000',
});
restMock.onGet('blocks/0').reply(200, {
timestamp: { to: blockToTimestamp },
});

const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, 'earliest', requestDetails);
expect(res).to.equal(EthImpl.emptyHex);
});
});
});
11 changes: 10 additions & 1 deletion packages/server/tests/acceptance/rpc_batch2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,16 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () {
it('should return empty bytecode for HTS token when a block earlier than the token creation is passed', async function () {
const res = await relay.call(
RelayCalls.ETH_ENDPOINTS.ETH_GET_CODE,
[NftHTSTokenContractAddress, '0x123'],
[NftHTSTokenContractAddress, '0x123'], // a very early block number
requestId,
);
expect(res).to.equal(EthImpl.emptyHex);
});

it('should return empty bytecode for contract when a block earlier than the contract creation is passed', async function () {
const res = await relay.call(
RelayCalls.ETH_ENDPOINTS.ETH_GET_CODE,
[mainContractAddress, '0x123'], // a very early block number
requestId,
);
expect(res).to.equal(EthImpl.emptyHex);
Expand Down

0 comments on commit 8a52152

Please sign in to comment.