From b188cac06e7a6312567e14065e29800a402e3a28 Mon Sep 17 00:00:00 2001 From: Aleksandrina Todorova Date: Fri, 16 Feb 2024 14:13:07 +0200 Subject: [PATCH 01/14] e2e(tests):Add tests for equivalence Signed-off-by: Aleksandrina Todorova --- .../tests/acceptance/equivalence.spec.ts | 165 ++++++++++++++++++ .../server/tests/acceptance/index.spec.ts | 2 +- .../server/tests/clients/servicesClient.ts | 19 +- packages/server/tests/helpers/constants.ts | 1 + 4 files changed, 178 insertions(+), 9 deletions(-) create mode 100644 packages/server/tests/acceptance/equivalence.spec.ts diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts new file mode 100644 index 0000000000..6a2b3bf06e --- /dev/null +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -0,0 +1,165 @@ +/*- + * + * Hedera JSON RPC Relay + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { expect } from 'chai'; +import { Utils } from '../helpers/utils'; +import { AliasAccount } from '../clients/servicesClient'; +import { ContractFunctionParameters } from '@hashgraph/sdk'; + +describe.only('Equivalence tests', function () { + const signers: AliasAccount[] = []; + const { servicesNode, mirrorNode, relay }: any = global; + + const SUCCESS = 'SUCCESS'; + const STATUS_SUCCESS = '0x1'; + const CONTRACT_EXECUTION_EXCEPTION = 'CONTRACT_EXECUTION_EXCEPTION'; + + const ETH_PRECOMPILE_0x1 = '0.0.1'; + const ETH_PRECOMPILE_0x361 = '0.0.361'; + const ETH_PRECOMPILE_0x751 = '0.0.751'; + const ETH_PRECOMPILE_0x1001 = '0.0.1001'; + const NON_EXISTING_CONTRACT_ID = '0.0.564400'; + const NON_EXISTING_FUNCTION = 'nxxixxkxxi'; + const EMPTY_FUNCTION_PARAMS = new ContractFunctionParameters(); + + before(async function () { + signers[0] = await servicesNode.createAliasAccount(15, relay.provider, Utils.generateRequestId()); + }); + + async function getResultByEntityIdAndTxTimestamp(entityId, txTimestamp) { + return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); + } + + it.only('should execute direct call to non-existing contract', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + NON_EXISTING_CONTRACT_ID, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(NON_EXISTING_CONTRACT_ID, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(NON_EXISTING_CONTRACT_ID); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + it('should execute direct call to ethereum precompile 0x1', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + ETH_PRECOMPILE_0x1, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + it('should execute direct call to ethereum precompile 361 without amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + ETH_PRECOMPILE_0x361, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x361); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); //Check if it should be CONTRACT_EXECUTION_EXCEPTION + }); + + it('should execute direct call to ethereum precompile 361 with amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + ETH_PRECOMPILE_0x361, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 100, //add the amount + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x361); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + }); + + it('should execute direct call to ethereum precompile 751 without amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + ETH_PRECOMPILE_0x751, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x751, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x751); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); //Check if it should be CONTRACT_EXECUTION_EXCEPTION + }); + + it('should execute direct call to ethereum precompile 751 with amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + ETH_PRECOMPILE_0x751, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 100, //add the amount + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x751, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x751); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + }); + + it('should execute direct call to ethereum precompile over 1000 without amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + ETH_PRECOMPILE_0x1001, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1001); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + }); + + it('should execute direct call to ethereum precompile over 1000 with amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + ETH_PRECOMPILE_0x1001, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 100, //add the amount + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1001); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + }); +}); diff --git a/packages/server/tests/acceptance/index.spec.ts b/packages/server/tests/acceptance/index.spec.ts index 020427342e..d520401bca 100644 --- a/packages/server/tests/acceptance/index.spec.ts +++ b/packages/server/tests/acceptance/index.spec.ts @@ -85,7 +85,7 @@ describe('RPC Server Acceptance Tests', function () { logger.info(`E2E_RELAY_HOST: ${process.env.E2E_RELAY_HOST}`); if (global.relayIsLocal) { - runLocalRelay(); + // runLocalRelay(); } // cache start balance diff --git a/packages/server/tests/clients/servicesClient.ts b/packages/server/tests/clients/servicesClient.ts index 853c6e122b..cdced972a0 100644 --- a/packages/server/tests/clients/servicesClient.ts +++ b/packages/server/tests/clients/servicesClient.ts @@ -234,14 +234,17 @@ export default class ServicesClient { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); // Call a method on a contract exists on Hedera, but is allowed to mutate the contract state this.logger.info(`${requestIdPrefix} Execute contracts ${contractId}'s createChild method`); - const contractExecTransactionResponse = await this.executeTransaction( - new ContractExecuteTransaction() - .setContractId(contractId) - .setGas(gasLimit) - .setFunction(functionName, params) - .setTransactionMemo('Relay test contract execution'), - requestId, - ); + const tx = new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(gasLimit) + .setFunction(functionName, params) + .setTransactionMemo('Relay test contract execution'); + + /* if (amount != 0) { + tx.setPayableAmount(Hbar.fromTinybars(amount)) + }*/ + + const contractExecTransactionResponse = await this.executeTransaction(tx, requestId); // @ts-ignore const resp = await this.getRecordResponseDetails(contractExecTransactionResponse, requestId); diff --git a/packages/server/tests/helpers/constants.ts b/packages/server/tests/helpers/constants.ts index cb22986c35..ecdc05fbdf 100644 --- a/packages/server/tests/helpers/constants.ts +++ b/packages/server/tests/helpers/constants.ts @@ -167,6 +167,7 @@ const NON_EXISTING_BLOCK_HASH = '0x555555555555555555555555555555555555555555555 const NON_EXISTING_BLOCK_NUMBER = '0x5F5E0FF'; //99999999 const NON_EXISTING_INDEX = '0xF423F'; //999999 const ZERO_HEX = '0x0000000000000000000000000000000000000000'; +const ZERO_ADDRESS = '0.0.0.0'; const CALL_EXCEPTION = 'CALL_EXCEPTION'; From fd2bba26e426f54d8d567f81007333c9a01d136e Mon Sep 17 00:00:00 2001 From: Aleksandrina Todorova Date: Thu, 22 Feb 2024 14:13:10 +0200 Subject: [PATCH 02/14] Added new method for contractCall with amount Signed-off-by: Aleksandrina Todorova --- .../tests/acceptance/equivalence.spec.ts | 40 ++++++++++++++----- .../server/tests/clients/servicesClient.ts | 31 ++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 6a2b3bf06e..e13701b639 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -22,6 +22,7 @@ import { expect } from 'chai'; import { Utils } from '../helpers/utils'; import { AliasAccount } from '../clients/servicesClient'; import { ContractFunctionParameters } from '@hashgraph/sdk'; +import RelayAssertions from '../../../relay/tests/assertions'; describe.only('Equivalence tests', function () { const signers: AliasAccount[] = []; @@ -30,6 +31,7 @@ describe.only('Equivalence tests', function () { const SUCCESS = 'SUCCESS'; const STATUS_SUCCESS = '0x1'; const CONTRACT_EXECUTION_EXCEPTION = 'CONTRACT_EXECUTION_EXCEPTION'; + const INVALID_FEE_SUBMITTED = 'INVALID_FEE_SUBMITTED'; const ETH_PRECOMPILE_0x1 = '0.0.1'; const ETH_PRECOMPILE_0x361 = '0.0.361'; @@ -47,7 +49,7 @@ describe.only('Equivalence tests', function () { return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); } - it.only('should execute direct call to non-existing contract', async function () { + it('should execute direct call to non-existing contract', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( NON_EXISTING_CONTRACT_ID, NON_EXISTING_FUNCTION, @@ -67,6 +69,7 @@ describe.only('Equivalence tests', function () { ETH_PRECOMPILE_0x1, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1, contractExecuteTimestamp); @@ -81,6 +84,7 @@ describe.only('Equivalence tests', function () { ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); @@ -90,19 +94,24 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); //Check if it should be CONTRACT_EXECUTION_EXCEPTION }); - it('should execute direct call to ethereum precompile 361 with amount', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - ETH_PRECOMPILE_0x361, - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - 100, //add the amount + it.only('should execute direct call to ethereum precompile 361 with amount', async function () { + const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; + + const responseCode = await extractResponseCode( + INVALID_FEE_SUBMITTED, + servicesNode.executeContractCallWithAmount, + true, + servicesNode, + args, ); - const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); + console.log(responseCode); - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x361); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + //const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); + + //expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x361); + // expect(record.result).to.equal(SUCCESS); + //expect(record.status).to.equal(INVALID_FEE_SUBMITTED); }); it('should execute direct call to ethereum precompile 751 without amount', async function () { @@ -162,4 +171,13 @@ describe.only('Equivalence tests', function () { expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); }); + + async function extractResponseCode(error, method, checkMessage, thisObj, args?) { + await expect(method.apply(thisObj, args), `${error.message}`).to.eventually.be.rejected.and.satisfy((err) => { + if (!checkMessage) { + return err.code === error.code && err.name === error.name; + } + return err.code === error.code && err.name === error.name && err.message === error.message; + }); + } }); diff --git a/packages/server/tests/clients/servicesClient.ts b/packages/server/tests/clients/servicesClient.ts index cdced972a0..b2f092be43 100644 --- a/packages/server/tests/clients/servicesClient.ts +++ b/packages/server/tests/clients/servicesClient.ts @@ -254,6 +254,37 @@ export default class ServicesClient { return { contractExecuteTimestamp, contractExecutedTransactionId }; } + async executeContractCallWithAmount( + contractId, + functionName: string, + params: ContractFunctionParameters, + gasLimit = 75000, + amount = 0, + requestId?: string, + ) { + const requestIdPrefix = Utils.formatRequestIdMessage(requestId); + // Call a method on a contract exists on Hedera, but is allowed to mutate the contract state + this.logger.info(`${requestIdPrefix} Execute contracts ${contractId}'s createChild method`); + const tx = new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(gasLimit) + .setFunction(functionName, params) + .setTransactionMemo('Relay test contract execution'); + + if (amount > 0) { + tx.setPayableAmount(Hbar.fromTinybars(amount)); + } + + const contractExecTransactionResponse = await this.executeTransaction(tx, requestId); + + // @ts-ignore + const resp = await this.getRecordResponseDetails(contractExecTransactionResponse, requestId); + const contractExecuteTimestamp = resp.executedTimestamp; + const contractExecutedTransactionId = resp.executedTransactionId; + + return { contractExecuteTimestamp, contractExecutedTransactionId }; + } + async getAliasAccountInfo( accountId, privateKey: PrivateKey, From 21b0c39e0739c50f2c74884523cd8aadf30f209a Mon Sep 17 00:00:00 2001 From: Konstantina Blazhukova Date: Thu, 22 Feb 2024 17:36:52 +0200 Subject: [PATCH 03/14] Propagate error from execute transaction upwards to other methods Signed-off-by: Konstantina Blazhukova --- .../tests/acceptance/equivalence.spec.ts | 19 ++++--------------- .../server/tests/clients/servicesClient.ts | 8 +++++++- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index e13701b639..104376b1ea 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -97,15 +97,7 @@ describe.only('Equivalence tests', function () { it.only('should execute direct call to ethereum precompile 361 with amount', async function () { const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; - const responseCode = await extractResponseCode( - INVALID_FEE_SUBMITTED, - servicesNode.executeContractCallWithAmount, - true, - servicesNode, - args, - ); - - console.log(responseCode); + await testRejection(INVALID_FEE_SUBMITTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); //const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); @@ -172,12 +164,9 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); }); - async function extractResponseCode(error, method, checkMessage, thisObj, args?) { - await expect(method.apply(thisObj, args), `${error.message}`).to.eventually.be.rejected.and.satisfy((err) => { - if (!checkMessage) { - return err.code === error.code && err.name === error.name; - } - return err.code === error.code && err.name === error.name && err.message === error.message; + async function testRejection(errorMessage, method, checkMessage, thisObj, args?) { + await expect(method.apply(thisObj, args), `${errorMessage}`).to.eventually.be.rejected.and.satisfy((err) => { + return err.message.includes(errorMessage); }); } }); diff --git a/packages/server/tests/clients/servicesClient.ts b/packages/server/tests/clients/servicesClient.ts index b2f092be43..cd4d07edbd 100644 --- a/packages/server/tests/clients/servicesClient.ts +++ b/packages/server/tests/clients/servicesClient.ts @@ -104,6 +104,7 @@ export default class ServicesClient { return resp; } catch (e) { this.logger.error(e, `${requestIdPrefix} Error executing ${transaction.constructor.name} transaction`); + throw e; } } @@ -274,8 +275,13 @@ export default class ServicesClient { if (amount > 0) { tx.setPayableAmount(Hbar.fromTinybars(amount)); } + let contractExecTransactionResponse; - const contractExecTransactionResponse = await this.executeTransaction(tx, requestId); + try { + contractExecTransactionResponse = await this.executeTransaction(tx, requestId); + } catch (e) { + throw e; + } // @ts-ignore const resp = await this.getRecordResponseDetails(contractExecTransactionResponse, requestId); From ecc70e80f1588c3f5d04232ef438f121c81268fc Mon Sep 17 00:00:00 2001 From: Aleksandrina Todorova Date: Mon, 26 Feb 2024 10:41:11 +0200 Subject: [PATCH 04/14] Update tests to use testRejection Signed-off-by: Aleksandrina Todorova --- .../tests/acceptance/equivalence.spec.ts | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 104376b1ea..03fa637564 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -80,30 +80,13 @@ describe.only('Equivalence tests', function () { }); it('should execute direct call to ethereum precompile 361 without amount', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - ETH_PRECOMPILE_0x361, - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); - - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x361); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); //Check if it should be CONTRACT_EXECUTION_EXCEPTION + const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000]; + await testRejection(CONTRACT_EXECUTION_EXCEPTION, servicesNode.executeContractCall, true, servicesNode, args); }); - it.only('should execute direct call to ethereum precompile 361 with amount', async function () { + it('should execute direct call to ethereum precompile 361 with amount', async function () { const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; - await testRejection(INVALID_FEE_SUBMITTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); - - //const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x361, contractExecuteTimestamp); - - //expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x361); - // expect(record.result).to.equal(SUCCESS); - //expect(record.status).to.equal(INVALID_FEE_SUBMITTED); }); it('should execute direct call to ethereum precompile 751 without amount', async function () { From c3014479c5df71b1f4c0a70658a748ed48bb1ef1 Mon Sep 17 00:00:00 2001 From: Nikolay Nikolov Date: Wed, 17 Apr 2024 15:31:38 +0300 Subject: [PATCH 05/14] Added additional tests for internal calls and fixed some of the previous tests for direct calls Signed-off-by: Nikolay Nikolov --- .../tests/acceptance/equivalence.spec.ts | 319 +++++++++-- .../server/tests/clients/servicesClient.ts | 2 +- .../tests/contracts/EquivalenceContract.json | 496 ++++++++++++++++++ .../tests/contracts/EquivalenceContract.sol | 200 +++++++ .../tests/contracts/EquivalenceDestruct.json | 33 ++ .../tests/contracts/EquivalenceDestruct.sol | 14 + 6 files changed, 1030 insertions(+), 34 deletions(-) create mode 100644 packages/server/tests/contracts/EquivalenceContract.json create mode 100644 packages/server/tests/contracts/EquivalenceContract.sol create mode 100644 packages/server/tests/contracts/EquivalenceDestruct.json create mode 100644 packages/server/tests/contracts/EquivalenceDestruct.sol diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 03fa637564..3450910072 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -23,6 +23,16 @@ import { Utils } from '../helpers/utils'; import { AliasAccount } from '../clients/servicesClient'; import { ContractFunctionParameters } from '@hashgraph/sdk'; import RelayAssertions from '../../../relay/tests/assertions'; +import EstimatePrecompileContractJson from '../contracts/EstimatePrecompileContract.json'; +import Constants from '../helpers/constants'; +import EquivalenceContractJson from '../contracts/EquivalenceContract.json'; +import { ethers } from 'ethers'; +import { Precheck } from '../../../relay/src/lib/precheck'; +import pino from 'pino'; +import { MirrorNodeClient } from '../../../relay/src/lib/clients'; +import { CacheService } from '../../../relay/src/lib/services/cacheService/cacheService'; +import EquivalenceDestructContractJson from '../contracts/EquivalenceDestruct.json'; +const logger = pino(); describe.only('Equivalence tests', function () { const signers: AliasAccount[] = []; @@ -32,38 +42,75 @@ describe.only('Equivalence tests', function () { const STATUS_SUCCESS = '0x1'; const CONTRACT_EXECUTION_EXCEPTION = 'CONTRACT_EXECUTION_EXCEPTION'; const INVALID_FEE_SUBMITTED = 'INVALID_FEE_SUBMITTED'; - + const INVALID_SOLIDITY_ADDRESS = 'INVALID_SOLIDITY_ADDRESS'; + const CONTRACT_REVERT_EXECUTED = 'CONTRACT_REVERT_EXECUTED'; + const prefix = '0x'; const ETH_PRECOMPILE_0x1 = '0.0.1'; const ETH_PRECOMPILE_0x361 = '0.0.361'; const ETH_PRECOMPILE_0x751 = '0.0.751'; + const ADDRESS_0x800 = '0.0.800'; const ETH_PRECOMPILE_0x1001 = '0.0.1001'; const NON_EXISTING_CONTRACT_ID = '0.0.564400'; const NON_EXISTING_FUNCTION = 'nxxixxkxxi'; const EMPTY_FUNCTION_PARAMS = new ContractFunctionParameters(); + const ADDRESS_0_0_0 = '0.0.0'; + const ADDRESS_0_0_1 = '0.0.1'; + const ADDRESS_0_0_2 = '0.0.2'; + const ADDRESS_0_0_3 = '0.0.3'; + const ADDRESS_0_0_4 = '0.0.4'; + const ADDRESS_0_0_100 = '0.0.100'; + const MAKE_CALL_WITHOUT_AMOUNT = 'makeCallWithoutAmount'; + const MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithoutAmountToIdentityPrecompile'; + let estimatePrecompileContractReceipt; + let estimatePrecompileContractAddress; + let HederaAddress; + let equivalenceContractReceipt; + let equivalenceContractId; + let equivalenceContract; + let equivalenceContractSolidityAddress; + let equivalenceDestructContractId; + let equivalenceDestructContractReceipt; before(async function () { - signers[0] = await servicesNode.createAliasAccount(15, relay.provider, Utils.generateRequestId()); - }); + signers[0] = await servicesNode.createAliasAccount(150, relay.provider, Utils.generateRequestId()); - async function getResultByEntityIdAndTxTimestamp(entityId, txTimestamp) { - return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); - } + //Deploying Estimate Precompile contract + estimatePrecompileContractReceipt = await servicesNode.deployContract( + EstimatePrecompileContractJson, + Constants.GAS_AS_NUMBER.LIMIT_5_000_000, + ); + estimatePrecompileContractAddress = estimatePrecompileContractReceipt.contractId.toString(); + console.log(estimatePrecompileContractAddress); - it('should execute direct call to non-existing contract', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - NON_EXISTING_CONTRACT_ID, - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - 500_000, + //Deploying Equivalence Destrcut contract + equivalenceDestructContractReceipt = await servicesNode.deployContract( + EquivalenceDestructContractJson, + Constants.GAS_AS_NUMBER.LIMIT_5_000_000, ); + equivalenceDestructContractId = equivalenceDestructContractReceipt.contractId.toString(); + console.log(equivalenceDestructContractId); - const record = await getResultByEntityIdAndTxTimestamp(NON_EXISTING_CONTRACT_ID, contractExecuteTimestamp); + //Deploying Equivalence contract + equivalenceContractReceipt = await servicesNode.deployContract( + EquivalenceContractJson, + Constants.GAS_AS_NUMBER.LIMIT_5_000_000, + ); + equivalenceContractId = equivalenceContractReceipt.contractId.toString(); + console.log(equivalenceContractId); + equivalenceContractSolidityAddress = equivalenceContractReceipt.contractId.toSolidityAddress(); - expect(record.contract_id).to.equal(NON_EXISTING_CONTRACT_ID); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + equivalenceContract = new ethers.Contract( + prefix + equivalenceContractSolidityAddress, + EquivalenceContractJson.abi, + signers[0].wallet, + ); + console.log(equivalenceContract); }); + async function getResultByEntityIdAndTxTimestamp(entityId, txTimestamp) { + return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); + } + it('should execute direct call to ethereum precompile 0x1', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( ETH_PRECOMPILE_0x1, @@ -79,46 +126,54 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - it('should execute direct call to ethereum precompile 361 without amount', async function () { + //EQV-003 - Should fail with INVALID_SOLIDIY_ADDRESS + // async function testRejection(errorMessage, method, checkMessage, thisObj, args?) + it('should execute direct call to address 361 to 750 without amount', async function () { const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000]; await testRejection(CONTRACT_EXECUTION_EXCEPTION, servicesNode.executeContractCall, true, servicesNode, args); }); - it('should execute direct call to ethereum precompile 361 with amount', async function () { + //EQV-004 - Should fail with INVALID_FEE_SUBMITTED + it('should execute direct call to ethereum precompile 361 to 750 with amount', async function () { const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; await testRejection(INVALID_FEE_SUBMITTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); }); - it('should execute direct call to ethereum precompile 751 without amount', async function () { + //EQV-005 - OK - ??? Should it be like that? - Should it be successfull + it('should execute direct call to address 751 without amount', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - ETH_PRECOMPILE_0x751, + ADDRESS_0x800, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, + 500_000, ); - const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x751, contractExecuteTimestamp); + const record = await getResultByEntityIdAndTxTimestamp(ADDRESS_0x800, contractExecuteTimestamp); - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x751); + expect(record.contract_id).to.equal(ADDRESS_0x800); expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); //Check if it should be CONTRACT_EXECUTION_EXCEPTION + expect(record.status).to.equal(STATUS_SUCCESS); //Check if it should be CONTRACT_EXECUTION_EXCEPTION }); - it('should execute direct call to ethereum precompile 751 with amount', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - ETH_PRECOMPILE_0x751, + //EQV-005 - OK - ??? Should it be like that? - Should it be successfull + it('should execute direct call to address 751 with amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + ADDRESS_0x800, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, + 500_000, 100, //add the amount ); - const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x751, contractExecuteTimestamp); + const record = await getResultByEntityIdAndTxTimestamp(ADDRESS_0x800, contractExecuteTimestamp); - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x751); + expect(record.contract_id).to.equal(ADDRESS_0x800); expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + expect(record.status).to.equal(STATUS_SUCCESS); }); - it('should execute direct call to ethereum precompile over 1000 without amount', async function () { + //EQV-006 - should be successfull - OK + it('should execute direct call to address over 1000 without amount', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( ETH_PRECOMPILE_0x1001, NON_EXISTING_FUNCTION, @@ -129,12 +184,64 @@ describe.only('Equivalence tests', function () { expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1001); expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + expect(record.status).to.equal(STATUS_SUCCESS); }); - it('should execute direct call to ethereum precompile over 1000 with amount', async function () { + //EQV-006 - should be successfull - OK + it('should execute direct call to address over 1000 without amount - case contract ', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - ETH_PRECOMPILE_0x1001, + estimatePrecompileContractAddress, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceDestructContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceDestructContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQV-007 - Should fail with INVALID_SOLIDIY_ADDRESS but it is SUCCESSFULL and creates Inactive EVM Address - ???????? + it('should execute direct call to non-existing contract', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + NON_EXISTING_CONTRACT_ID, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(NON_EXISTING_CONTRACT_ID, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(NON_EXISTING_CONTRACT_ID); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQV-008 - should be successfull - OK + it('should execute direct call to address over 1000 without amount - case payable contract ', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceDestructContractId, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceDestructContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceDestructContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQV-009 - OK - should be unsuccessfull + it('should execute direct call to address over 1000 with amount - case non-payable contract ', async function () { + const args = [estimatePrecompileContractAddress, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; + await testRejection(CONTRACT_REVERT_EXECUTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); + }); + + it('should execute direct call to address over 1000 with amount', async function () { + const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + 'ETH_PRECOMPILE_0x1001', NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 100, //add the amount @@ -147,6 +254,152 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); }); + //EQV-13 This should be successful due to hollow account creation + it('should execute direct call to address over 1000 with amount - case hollow acconut', async function () { + const hollowAccount = ethers.Wallet.createRandom(); + const hollowAcAddress = hollowAccount.address.toString(); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + hollowAcAddress, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 1_000_000, + 100, //add the amount + ); + + const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1001); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + }); + + //----------Internal calls---------------- + //EQUIVALENCE-040 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be something else + it('internal CALL to 0.0.0 without amount - should be successfull', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + console.log('PARAMS ARE '); + console.log(params); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-041 - ОК + it('internal CALL to 0.0.1 without amount - should be successfull', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-042 - + it.only('internal CALL to 0.0.2 without amount - should be successfull', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-043 - PRECOMPILE ERROR + it('internal CALL to 0.0.2 without amount - should be successfull', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-044 - ОК + it('internal CALL to 0.0.4 without amount - should be successfull', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); + const params = new ContractFunctionParameters().addAddress(evmAddress); + console.log('PARAMS ARE '); + console.log(params); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE, + EMPTY_FUNCTION_PARAMS, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-045 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS + it('internal CALL to 0.0.100 without amount - should be successfull', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_100); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + async function testRejection(errorMessage, method, checkMessage, thisObj, args?) { await expect(method.apply(thisObj, args), `${errorMessage}`).to.eventually.be.rejected.and.satisfy((err) => { return err.message.includes(errorMessage); diff --git a/packages/server/tests/clients/servicesClient.ts b/packages/server/tests/clients/servicesClient.ts index cd4d07edbd..ce978812bb 100644 --- a/packages/server/tests/clients/servicesClient.ts +++ b/packages/server/tests/clients/servicesClient.ts @@ -259,7 +259,7 @@ export default class ServicesClient { contractId, functionName: string, params: ContractFunctionParameters, - gasLimit = 75000, + gasLimit = 500_000, amount = 0, requestId?: string, ) { diff --git a/packages/server/tests/contracts/EquivalenceContract.json b/packages/server/tests/contracts/EquivalenceContract.json new file mode 100644 index 0000000000..d096f2282a --- /dev/null +++ b/packages/server/tests/contracts/EquivalenceContract.json @@ -0,0 +1,496 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "EquivalenceContract", + "sourceName": "contracts/EquivalenceContract.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_customData", + "type": "bytes" + } + ], + "name": "callCodeToContractWithAmount", + "outputs": [ + { + "internalType": "bytes32", + "name": "returnData", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_customData", + "type": "bytes" + } + ], + "name": "callCodeToContractWithoutAmount", + "outputs": [ + { + "internalType": "bytes32", + "name": "returnData", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "copyCode", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tinycents", + "type": "uint256" + } + ], + "name": "exchangeRateWithAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "tinybars", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tinycents", + "type": "uint256" + } + ], + "name": "exchangeRateWithoutAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "tinybars", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "getBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "getCodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "getCodeSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getPseudorandomSeed", + "outputs": [ + { + "internalType": "bytes32", + "name": "randomBytes", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getPseudorandomSeedWithAmount", + "outputs": [ + { + "internalType": "bytes32", + "name": "randomBytes", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "htsCallWithAmount", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isToken", + "type": "bool" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "htsCallWithoutAmount", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isToken", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "htsDelegateCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isToken", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "htsStaticCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isToken", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "makeCallWithAmount", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "makeCallWithAmountToIdentityPrecompile", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "makeCallWithoutAmount", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "makeCallWithoutAmountToIdentityPrecompile", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "makeDelegateCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "makeDelegateCallToIdentityPrecompile", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "makeStaticCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "makeStaticCallToIdentityPrecompile", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5061114c806100206000396000f3fe6080604052600436106101705760003560e01c80638771074f116100d6578063b51c4f961161007f578063e34c93b811610059578063e34c93b8146103b9578063e717ea61146103cc578063f8b2cb4f146103e757600080fd5b8063b51c4f9614610370578063d558bd8e1461038f578063d83bf9a1146103a457600080fd5b8063b0d869b1116100b0578063b0d869b11461032a578063b2539b2e1461034a578063b2a07c3e1461035d57600080fd5b80638771074f146102ef57806388b0764a1461030f578063915482281461031757600080fd5b806367edbd6d116101385780637e38d3a3116101125780637e38d3a31461029057806381ea4408146102b0578063845b27cd146102cf57600080fd5b806367edbd6d146102465780636f68a38e1461025b57806375ce65191461027b57600080fd5b806315ed14111461017557806319f88c7d146101ac5780631d620e2a146101da5780634e731583146102115780636144ef6d1461023e575b600080fd5b34801561018157600080fd5b50610195610190366004610e92565b61040f565b6040516101a3929190610fb0565b60405180910390f35b3480156101b857600080fd5b506101cc6101c7366004610fcb565b610477565b6040519081526020016101a3565b3480156101e657600080fd5b506101fa6101f536600461104e565b6104a8565b6040805192151583529015156020830152016101a3565b34801561021d57600080fd5b5061023161022c36600461104e565b610587565b6040516101a39190611070565b6101cc6105e3565b34801561025257600080fd5b5061019561068f565b34801561026757600080fd5b506101fa61027636600461104e565b61075d565b34801561028757600080fd5b506101956107ef565b34801561029c57600080fd5b506101956102ab366004610e92565b6108aa565b3480156102bc57600080fd5b506101cc6102cb36600461104e565b3f90565b3480156102db57600080fd5b506101fa6102ea36600461104e565b610903565b3480156102fb57600080fd5b5061019561030a366004610e92565b610997565b6101956109ee565b610195610325366004610e92565b610aac565b34801561033657600080fd5b506101cc610345366004611083565b610b06565b6101fa61035836600461104e565b610bcc565b6101cc61036b366004611083565b610c62565b34801561037c57600080fd5b506101cc61038b36600461104e565b3b90565b34801561039b57600080fd5b50610195610cf9565b3480156103b057600080fd5b506101cc610db4565b6101cc6103c7366004610fcb565b610e30565b3480156103d857600080fd5b506040513081526020016101a3565b3480156103f357600080fd5b506101cc61040236600461104e565b6001600160a01b03163190565b60006060836001600160a01b03168360405161042b919061109c565b600060405180830381855afa9150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b606091505b50909590945092505050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2505195945050505050565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516104ff919061109c565b600060405180830381855afa9150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b606091505b50915091508115610580576000808280602001905181019061056191906110b8565b915060030b91508160070b601614955085801561057b5750805b945050505b5050915091565b6060813b60008167ffffffffffffffff8111156105a6576105a6610e7c565b6040519080825280601f01601f1916602001820160405280156105d0576020820181803683370190505b50905081600060208301863c9392505050565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b1790529051600091829182916101699134916106239161109c565b60006040518083038185875af1925050503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b606091505b50915091508161067457600080fd5b8080602001905181019061068891906110fd565b9250505090565b6000606060006040516020016106c5906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016106f59190611070565b60408051601f198184030181529082905261070f9161109c565b6000604051808303816000865af19150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b606091505b50915091505050509091565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516107b4919061109c565b600060405180830381855af49150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b600060606000604051602001610825906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016108559190611070565b60408051601f198184030181529082905261086f9161109c565b600060405180830381855af49150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b0316836040516108c6919061109c565b6000604051808303816000865af19150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b1790525161095a919061109c565b6000604051808303816000865af19150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60006060836001600160a01b0316836040516109b3919061109c565b600060405180830381855af49150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b600060606000604051602001610a24906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b03163484604051602001610a559190611070565b60408051601f1981840301815290829052610a6f9161109c565b60006040518083038185875af1925050503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b03163484604051610ac9919061109c565b60006040518083038185875af1925050503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b60008060006101686001600160a01b031684604051602401610b2a91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610b5f919061109c565b6000604051808303816000865af19150503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b606091505b509150915081610bb057600080fd5b80806020019051810190610bc491906110fd565b949350505050565b6040516001600160a01b038216602482015260009081908190819061016790349060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610c25919061109c565b60006040518083038185875af1925050503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60008060006101686001600160a01b03163485604051602401610c8791815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610cbc919061109c565b60006040518083038185875af1925050503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b600060606000604051602001610d2f906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001610d5f9190611070565b60408051601f1981840301815290829052610d799161109c565b600060405180830381855afa9150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b17905290516000918291829161016991610df3919061109c565b6000604051808303816000865af19150503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2505195945050505050565b80356001600160a01b0381168114610e7757600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610ea557600080fd5b610eae83610e60565b9150602083013567ffffffffffffffff80821115610ecb57600080fd5b818501915085601f830112610edf57600080fd5b813581811115610ef157610ef1610e7c565b604051601f8201601f19908116603f01168101908382118183101715610f1957610f19610e7c565b81604052828152886020848701011115610f3257600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b83811015610f6f578181015183820152602001610f57565b83811115610f7e576000848401525b50505050565b60008151808452610f9c816020860160208601610f54565b601f01601f19169290920160200192915050565b8215158152604060208201526000610bc46040830184610f84565b600080600060408486031215610fe057600080fd5b610fe984610e60565b9250602084013567ffffffffffffffff8082111561100657600080fd5b818601915086601f83011261101a57600080fd5b81358181111561102957600080fd5b87602082850101111561103b57600080fd5b6020830194508093505050509250925092565b60006020828403121561106057600080fd5b61106982610e60565b9392505050565b6020815260006110696020830184610f84565b60006020828403121561109557600080fd5b5035919050565b600082516110ae818460208701610f54565b9190910192915050565b600080604083850312156110cb57600080fd5b82518060030b81146110dc57600080fd5b602084015190925080151581146110f257600080fd5b809150509250929050565b60006020828403121561110f57600080fd5b505191905056fea2646970667358221220b774fcfcede95d5d0a658065ae9aba74acf713de4417f59168cf907ed0bbe87764736f6c63430008090033", + "deployedBytecode": "0x6080604052600436106101705760003560e01c80638771074f116100d6578063b51c4f961161007f578063e34c93b811610059578063e34c93b8146103b9578063e717ea61146103cc578063f8b2cb4f146103e757600080fd5b8063b51c4f9614610370578063d558bd8e1461038f578063d83bf9a1146103a457600080fd5b8063b0d869b1116100b0578063b0d869b11461032a578063b2539b2e1461034a578063b2a07c3e1461035d57600080fd5b80638771074f146102ef57806388b0764a1461030f578063915482281461031757600080fd5b806367edbd6d116101385780637e38d3a3116101125780637e38d3a31461029057806381ea4408146102b0578063845b27cd146102cf57600080fd5b806367edbd6d146102465780636f68a38e1461025b57806375ce65191461027b57600080fd5b806315ed14111461017557806319f88c7d146101ac5780631d620e2a146101da5780634e731583146102115780636144ef6d1461023e575b600080fd5b34801561018157600080fd5b50610195610190366004610e92565b61040f565b6040516101a3929190610fb0565b60405180910390f35b3480156101b857600080fd5b506101cc6101c7366004610fcb565b610477565b6040519081526020016101a3565b3480156101e657600080fd5b506101fa6101f536600461104e565b6104a8565b6040805192151583529015156020830152016101a3565b34801561021d57600080fd5b5061023161022c36600461104e565b610587565b6040516101a39190611070565b6101cc6105e3565b34801561025257600080fd5b5061019561068f565b34801561026757600080fd5b506101fa61027636600461104e565b61075d565b34801561028757600080fd5b506101956107ef565b34801561029c57600080fd5b506101956102ab366004610e92565b6108aa565b3480156102bc57600080fd5b506101cc6102cb36600461104e565b3f90565b3480156102db57600080fd5b506101fa6102ea36600461104e565b610903565b3480156102fb57600080fd5b5061019561030a366004610e92565b610997565b6101956109ee565b610195610325366004610e92565b610aac565b34801561033657600080fd5b506101cc610345366004611083565b610b06565b6101fa61035836600461104e565b610bcc565b6101cc61036b366004611083565b610c62565b34801561037c57600080fd5b506101cc61038b36600461104e565b3b90565b34801561039b57600080fd5b50610195610cf9565b3480156103b057600080fd5b506101cc610db4565b6101cc6103c7366004610fcb565b610e30565b3480156103d857600080fd5b506040513081526020016101a3565b3480156103f357600080fd5b506101cc61040236600461104e565b6001600160a01b03163190565b60006060836001600160a01b03168360405161042b919061109c565b600060405180830381855afa9150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b606091505b50909590945092505050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2505195945050505050565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516104ff919061109c565b600060405180830381855afa9150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b606091505b50915091508115610580576000808280602001905181019061056191906110b8565b915060030b91508160070b601614955085801561057b5750805b945050505b5050915091565b6060813b60008167ffffffffffffffff8111156105a6576105a6610e7c565b6040519080825280601f01601f1916602001820160405280156105d0576020820181803683370190505b50905081600060208301863c9392505050565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b1790529051600091829182916101699134916106239161109c565b60006040518083038185875af1925050503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b606091505b50915091508161067457600080fd5b8080602001905181019061068891906110fd565b9250505090565b6000606060006040516020016106c5906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016106f59190611070565b60408051601f198184030181529082905261070f9161109c565b6000604051808303816000865af19150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b606091505b50915091505050509091565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516107b4919061109c565b600060405180830381855af49150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b600060606000604051602001610825906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016108559190611070565b60408051601f198184030181529082905261086f9161109c565b600060405180830381855af49150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b0316836040516108c6919061109c565b6000604051808303816000865af19150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b1790525161095a919061109c565b6000604051808303816000865af19150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60006060836001600160a01b0316836040516109b3919061109c565b600060405180830381855af49150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b600060606000604051602001610a24906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b03163484604051602001610a559190611070565b60408051601f1981840301815290829052610a6f9161109c565b60006040518083038185875af1925050503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b03163484604051610ac9919061109c565b60006040518083038185875af1925050503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b60008060006101686001600160a01b031684604051602401610b2a91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610b5f919061109c565b6000604051808303816000865af19150503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b606091505b509150915081610bb057600080fd5b80806020019051810190610bc491906110fd565b949350505050565b6040516001600160a01b038216602482015260009081908190819061016790349060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610c25919061109c565b60006040518083038185875af1925050503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60008060006101686001600160a01b03163485604051602401610c8791815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610cbc919061109c565b60006040518083038185875af1925050503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b600060606000604051602001610d2f906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001610d5f9190611070565b60408051601f1981840301815290829052610d799161109c565b600060405180830381855afa9150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b17905290516000918291829161016991610df3919061109c565b6000604051808303816000865af19150503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2505195945050505050565b80356001600160a01b0381168114610e7757600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610ea557600080fd5b610eae83610e60565b9150602083013567ffffffffffffffff80821115610ecb57600080fd5b818501915085601f830112610edf57600080fd5b813581811115610ef157610ef1610e7c565b604051601f8201601f19908116603f01168101908382118183101715610f1957610f19610e7c565b81604052828152886020848701011115610f3257600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b83811015610f6f578181015183820152602001610f57565b83811115610f7e576000848401525b50505050565b60008151808452610f9c816020860160208601610f54565b601f01601f19169290920160200192915050565b8215158152604060208201526000610bc46040830184610f84565b600080600060408486031215610fe057600080fd5b610fe984610e60565b9250602084013567ffffffffffffffff8082111561100657600080fd5b818601915086601f83011261101a57600080fd5b81358181111561102957600080fd5b87602082850101111561103b57600080fd5b6020830194508093505050509250925092565b60006020828403121561106057600080fd5b61106982610e60565b9392505050565b6020815260006110696020830184610f84565b60006020828403121561109557600080fd5b5035919050565b600082516110ae818460208701610f54565b9190910192915050565b600080604083850312156110cb57600080fd5b82518060030b81146110dc57600080fd5b602084015190925080151581146110f257600080fd5b809150509250929050565b60006020828403121561110f57600080fd5b505191905056fea2646970667358221220b774fcfcede95d5d0a658065ae9aba74acf713de4417f59168cf907ed0bbe87764736f6c63430008090033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/server/tests/contracts/EquivalenceContract.sol b/packages/server/tests/contracts/EquivalenceContract.sol new file mode 100644 index 0000000000..eabd7b685e --- /dev/null +++ b/packages/server/tests/contracts/EquivalenceContract.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +contract EquivalenceContract { + address constant HTS_PRECOMPILE_ADDRESS = address(0x167); + address constant EXCHANGE_RATE_PRECOMPILE_ADDRESS = address(0x168); + address constant PRNG_PRECOMPILE_ADDRESS = address(0x169); + + function makeCallWithoutAmount(address _to, bytes memory _data) external returns (bool success, bytes memory returnData) { + (success, returnData) = _to.call(_data); + } + + function makeCallWithAmount(address _to, bytes memory _data) external payable returns (bool success, bytes memory returnData) { + (success, returnData) = _to.call{value: msg.value}(_data); + } + + function makeStaticCall(address _to, bytes memory _data) external returns (bool success, bytes memory returnData) { + (success, returnData) = _to.staticcall(_data); + } + + function makeDelegateCall(address _to, bytes memory _data) external returns (bool success, bytes memory returnData) { + (success, returnData) = _to.delegatecall(_data); + } + + function callCodeToContractWithoutAmount(address _addr, bytes calldata _customData) external returns (bytes32 returnData) { + assembly { + let input := mload(0x40) + mstore(0x40, add(input, calldatasize())) + calldatacopy(input, _customData.offset, calldatasize()) + + let output := mload(0x40) + mstore(0x40, add(output, 0x20)) + + let success := callcode( + 900000, // gas + _addr, // target address + 0, // value + input, // input data location + calldatasize(), // input data size + output, // output data location + 0x20 // size of output data expected + ) + + returnData := mload(output) // assign output value to the var + } + } + + function callCodeToContractWithAmount(address _addr, bytes calldata _customData) external payable returns (bytes32 returnData) { + assembly { + let input := mload(0x40) + mstore(0x40, add(input, calldatasize())) + calldatacopy(input, _customData.offset, calldatasize()) + + let output := mload(0x40) + mstore(0x40, add(output, 0x20)) + + let success := callcode( + 900000, // gas + _addr, // target address + callvalue(), // value + input, // input data location + calldatasize(), // input data size + output, // output data location + 0x20 // size of output data expected + ) + + returnData := mload(output) // assign output value to the var + } + } + + function getBalance(address _address) external view returns (uint256) { + return _address.balance; + } + + function getCodeSize(address _address) external view returns (uint256) { + uint256 size; + assembly { + size := extcodesize(_address) + } + return size; + } + + function getCodeHash(address _address) external view returns (bytes32) { + bytes32 codehash; + assembly { + codehash := extcodehash(_address) + } + return codehash; + } + + function copyCode(address _address) external view returns (bytes memory) { + uint256 size; + assembly { + size := extcodesize(_address) + } + bytes memory code = new bytes(size); + + assembly { + extcodecopy(_address, add(code, 32), 0, size) + } + return code; + } + + function getPseudorandomSeed() external returns (bytes32 randomBytes) { + (bool success, bytes memory result) = PRNG_PRECOMPILE_ADDRESS.call( + abi.encodeWithSignature("getPseudorandomSeed()")); + require(success); + randomBytes = abi.decode(result, (bytes32)); + } + + function getPseudorandomSeedWithAmount() external payable returns (bytes32 randomBytes) { + (bool success, bytes memory result) = PRNG_PRECOMPILE_ADDRESS.call{value: msg.value}( + abi.encodeWithSignature("getPseudorandomSeed()")); + require(success); + randomBytes = abi.decode(result, (bytes32)); + } + + function exchangeRateWithoutAmount(uint256 tinycents) external returns (uint256 tinybars) { + (bool success, bytes memory result) = EXCHANGE_RATE_PRECOMPILE_ADDRESS.call( + abi.encodeWithSignature("tinycentsToTinybars(uint256)", tinycents)); + require(success); + tinybars = abi.decode(result, (uint256)); + } + + function exchangeRateWithAmount(uint256 tinycents) external payable returns (uint256 tinybars) { + (bool success, bytes memory result) = EXCHANGE_RATE_PRECOMPILE_ADDRESS.call{value: msg.value}( + abi.encodeWithSignature("tinycentsToTinybars(uint256)", tinycents)); + require(success); + tinybars = abi.decode(result, (uint256)); + } + + function htsCallWithoutAmount(address token) external returns (bool success, bool isToken) { + (bool callSuccess, bytes memory result) = HTS_PRECOMPILE_ADDRESS.call( + abi.encodeWithSignature("isToken(address)", token)); + if (callSuccess) { + (int64 responseCode, bool isTokenFlag) = abi.decode(result, (int32, bool)); + success = responseCode == 22; // success + isToken = success && isTokenFlag; + } + } + + function htsCallWithAmount(address token) external payable returns (bool success, bool isToken) { + (bool callSuccess, bytes memory result) = HTS_PRECOMPILE_ADDRESS.call{value: msg.value}( + abi.encodeWithSignature("isToken(address)", token)); + if (callSuccess) { + (int64 responseCode, bool isTokenFlag) = abi.decode(result, (int32, bool)); + success = responseCode == 22; // success + isToken = success && isTokenFlag; + } + } + + function htsStaticCall(address token) external returns (bool success, bool isToken) { + (bool callSuccess, bytes memory result) = HTS_PRECOMPILE_ADDRESS.staticcall( + abi.encodeWithSignature("isToken(address)", token)); + if (callSuccess) { + (int64 responseCode, bool isTokenFlag) = abi.decode(result, (int32, bool)); + success = responseCode == 22; // success + isToken = success && isTokenFlag; + } + } + + function htsDelegateCall(address token) external returns (bool success, bool isToken) { + (bool callSuccess, bytes memory result) = HTS_PRECOMPILE_ADDRESS.delegatecall( + abi.encodeWithSignature("isToken(address)", token)); + if (callSuccess) { + (int64 responseCode, bool isTokenFlag) = abi.decode(result, (int32, bool)); + success = responseCode == 22; // success + isToken = success && isTokenFlag; + } + } + + function getCurrentAddress() external returns(address){ + return address(this); + } + + function makeCallWithoutAmountToIdentityPrecompile() external returns (bool success, bytes memory returnData) { + bytes memory input = abi.encode("Hello, World"); + + (bool success, bytes memory returnData) = address(0x04).call(abi.encode(input)); + } + + function makeCallWithAmountToIdentityPrecompile() external payable returns (bool success, bytes memory returnData) { + bytes memory input = abi.encode("Hello, World"); + + (bool success, bytes memory returnData) = address(0x04).call{value: msg.value}(abi.encode(input)); + } + + function makeStaticCallToIdentityPrecompile() external returns (bool success, bytes memory returnData) { + bytes memory input = abi.encode("Hello, World"); + + (bool success, bytes memory returnData) = address(0x04).staticcall(abi.encode(input)); + } + + function makeDelegateCallToIdentityPrecompile() external returns (bool success, bytes memory returnData) { + bytes memory input = abi.encode("Hello, World"); + + (bool success, bytes memory returnData) = address(0x04).delegatecall(abi.encode(input)); + } + +} diff --git a/packages/server/tests/contracts/EquivalenceDestruct.json b/packages/server/tests/contracts/EquivalenceDestruct.json new file mode 100644 index 0000000000..aad58cf611 --- /dev/null +++ b/packages/server/tests/contracts/EquivalenceDestruct.json @@ -0,0 +1,33 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "EquivalenceDestruct", + "sourceName": "contracts/EquivalenceDestruct.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "beneficiary", + "type": "address" + } + ], + "name": "destroyContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405260bf806100126000396000f3fe608060405260043610601c5760003560e01c8063016a373814601e575b005b348015602957600080fd5b50601c6035366004604e565b8073ffffffffffffffffffffffffffffffffffffffff16ff5b600060208284031215605f57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114608257600080fd5b939250505056fea2646970667358221220935b250d46b6379f7eebb869774fcbebaf31da8d7f35b02fa78f400c767a68dd64736f6c63430008090033", + "deployedBytecode": "0x608060405260043610601c5760003560e01c8063016a373814601e575b005b348015602957600080fd5b50601c6035366004604e565b8073ffffffffffffffffffffffffffffffffffffffff16ff5b600060208284031215605f57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114608257600080fd5b939250505056fea2646970667358221220935b250d46b6379f7eebb869774fcbebaf31da8d7f35b02fa78f400c767a68dd64736f6c63430008090033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/server/tests/contracts/EquivalenceDestruct.sol b/packages/server/tests/contracts/EquivalenceDestruct.sol new file mode 100644 index 0000000000..2ba8835824 --- /dev/null +++ b/packages/server/tests/contracts/EquivalenceDestruct.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +contract EquivalenceDestruct { + constructor() payable {} + // Function to self-destruct the contract + function destroyContract(address payable beneficiary) public { + // Self-destruct the contract and send funds to the beneficiary + selfdestruct(beneficiary); + } + + fallback() external payable { + } +} From 5175e0df6d5f1ed66f193329dc91f85b44d63b91 Mon Sep 17 00:00:00 2001 From: Nikolay Nikolov Date: Fri, 19 Apr 2024 16:14:04 +0300 Subject: [PATCH 06/14] - added additional tests Signed-off-by: Nikolay Nikolov --- .../tests/acceptance/equivalence.spec.ts | 131 ++++++++++++++++-- 1 file changed, 122 insertions(+), 9 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 3450910072..a7235bba6c 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -59,8 +59,12 @@ describe.only('Equivalence tests', function () { const ADDRESS_0_0_3 = '0.0.3'; const ADDRESS_0_0_4 = '0.0.4'; const ADDRESS_0_0_100 = '0.0.100'; + const ADDRESS_0_0_358 = '0.0.358'; + const ADDRESS_0_0_359 = '0.0.359'; const MAKE_CALL_WITHOUT_AMOUNT = 'makeCallWithoutAmount'; + const MAKE_CALL_WITH_AMOUNT = 'makeCallWithAmount'; const MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithoutAmountToIdentityPrecompile'; + const MAKE_CALL_WITH_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithAmountToIdentityPrecompile'; let estimatePrecompileContractReceipt; let estimatePrecompileContractAddress; let HederaAddress; @@ -275,7 +279,7 @@ describe.only('Equivalence tests', function () { }); //----------Internal calls---------------- - //EQUIVALENCE-040 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be something else + //EQUIVALENCE-014 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be something else it('internal CALL to 0.0.0 without amount - should be successfull', async function () { const emptyByteArray = new Uint8Array(0); const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); @@ -298,7 +302,29 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQUIVALENCE-041 - ОК + //EQUIVALENCE-015 - PRECOMPILE ERROR - Same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED + it('internal CALL to 0.0.0 with amount', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + equivalenceContractId, + MAKE_CALL_WITH_AMOUNT, + params, + 1_000_000, + 100, //add the amount + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-016 it('internal CALL to 0.0.1 without amount - should be successfull', async function () { const emptyByteArray = new Uint8Array(0); const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); @@ -318,8 +344,8 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQUIVALENCE-042 - - it.only('internal CALL to 0.0.2 without amount - should be successfull', async function () { + //EQUIVALENCE-017 + it('internal CALL to 0.0.2 without amount - should be successfull', async function () { const emptyByteArray = new Uint8Array(0); const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); const params = new ContractFunctionParameters().addAddress(evmAddress); @@ -338,10 +364,10 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQUIVALENCE-043 - PRECOMPILE ERROR - it('internal CALL to 0.0.2 without amount - should be successfull', async function () { + //EQUIVALENCE-018 + it('internal CALL to 0.0.3 without amount - should be successfull', async function () { const emptyByteArray = new Uint8Array(0); - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); const params = new ContractFunctionParameters().addAddress(evmAddress); params.addBytes(emptyByteArray); const { contractExecuteTimestamp } = await servicesNode.executeContractCall( @@ -358,7 +384,7 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQUIVALENCE-044 - ОК + //EQUIVALENCE-019 - ОК it('internal CALL to 0.0.4 without amount - should be successfull', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); const params = new ContractFunctionParameters().addAddress(evmAddress); @@ -379,7 +405,30 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQUIVALENCE-045 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS + //EQUIVALENCE-020 - OK + it('internal CALL to 0.0.4 with amount', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); + const params = new ContractFunctionParameters().addAddress(evmAddress); + console.log('PARAMS ARE '); + console.log(params); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + equivalenceContractId, + MAKE_CALL_WITH_AMOUNT_TO_IDENTITY_PRECOMPILE, + EMPTY_FUNCTION_PARAMS, + 1_000_000, + 100, //add the amount + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + //TODO: It returns INVALID_FEE_SUBMITTED and we have to implement the check for that + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-021 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS it('internal CALL to 0.0.100 without amount - should be successfull', async function () { const emptyByteArray = new Uint8Array(0); const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_100); @@ -400,6 +449,70 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); + //EQUIVALENCE-022 --- The same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED + it('internal CALL to 0.0.100 with amount - should be successfull', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_100); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + equivalenceContractId, + MAKE_CALL_WITH_AMOUNT, + params, + 1_000_000, + 100, //add the amount + ); + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-023 - PRECOMPILE ERROR + it('internal CALL to 0.0.358 without amount', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-024 - PRECOMPILE ERROR + it.only('internal CALL to 0.0.358 without amount', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); + const params = new ContractFunctionParameters().addAddress(evmAddress); + params.addBytes(emptyByteArray); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITH_AMOUNT, + params, + 1_000_000, + 100, //add the amount + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + async function testRejection(errorMessage, method, checkMessage, thisObj, args?) { await expect(method.apply(thisObj, args), `${errorMessage}`).to.eventually.be.rejected.and.satisfy((err) => { return err.message.includes(errorMessage); From b629175e27c43cee354a267c9bbc651e3dcd67e8 Mon Sep 17 00:00:00 2001 From: Nikolay Nikolov Date: Thu, 25 Apr 2024 16:46:55 +0300 Subject: [PATCH 07/14] - added more tests, also added a function for creating a fungible token Signed-off-by: Nikolay Nikolov --- .../tests/acceptance/equivalence.spec.ts | 127 +++++++++++++++++- 1 file changed, 124 insertions(+), 3 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index a7235bba6c..ec395e7065 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -32,9 +32,12 @@ import pino from 'pino'; import { MirrorNodeClient } from '../../../relay/src/lib/clients'; import { CacheService } from '../../../relay/src/lib/services/cacheService/cacheService'; import EquivalenceDestructContractJson from '../contracts/EquivalenceDestruct.json'; +import ERC20MockJson from '../contracts/ERC20Mock.json'; +import { AccountId, Hbar, ContractId } from '@hashgraph/sdk'; +import TokenManagementContractJson from '../contracts/TokenManagementContract.json'; const logger = pino(); -describe.only('Equivalence tests', function () { +describe.only('Equivalence tests', async function () { const signers: AliasAccount[] = []; const { servicesNode, mirrorNode, relay }: any = global; @@ -65,8 +68,13 @@ describe.only('Equivalence tests', function () { const MAKE_CALL_WITH_AMOUNT = 'makeCallWithAmount'; const MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithoutAmountToIdentityPrecompile'; const MAKE_CALL_WITH_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithAmountToIdentityPrecompile'; + const MAKE_HTS_CALL_WITHOUT_AMOUNT = 'htsCallWithoutAmount'; + const MAKE_HTS_CALL_WITH_AMOUNT = 'htsCallWithAmount'; + const accounts: AliasAccount[] = []; + let tokenAddress; let estimatePrecompileContractReceipt; let estimatePrecompileContractAddress; + let estimatePrecompileSolidityAddress; let HederaAddress; let equivalenceContractReceipt; let equivalenceContractId; @@ -74,6 +82,9 @@ describe.only('Equivalence tests', function () { let equivalenceContractSolidityAddress; let equivalenceDestructContractId; let equivalenceDestructContractReceipt; + let estimateContract; + let TokenManager; + let requestId; before(async function () { signers[0] = await servicesNode.createAliasAccount(150, relay.provider, Utils.generateRequestId()); @@ -84,6 +95,7 @@ describe.only('Equivalence tests', function () { Constants.GAS_AS_NUMBER.LIMIT_5_000_000, ); estimatePrecompileContractAddress = estimatePrecompileContractReceipt.contractId.toString(); + estimatePrecompileSolidityAddress = estimatePrecompileContractReceipt.contractId.toSolidityAddress(); console.log(estimatePrecompileContractAddress); //Deploying Equivalence Destrcut contract @@ -109,12 +121,42 @@ describe.only('Equivalence tests', function () { signers[0].wallet, ); console.log(equivalenceContract); + + requestId = Utils.generateRequestId(); + const contractMirror = await mirrorNode.get(`/contracts/${estimatePrecompileSolidityAddress}`, requestId); + + accounts[0] = await servicesNode.createAccountWithContractIdKey( + contractMirror.contract_id, + 200, + relay.provider, + requestId, + ); + + tokenAddress = await createFungibleToken(); + console.log(tokenAddress); }); async function getResultByEntityIdAndTxTimestamp(entityId, txTimestamp) { return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); } + async function createFungibleToken() { + estimateContract = new ethers.Contract( + prefix + estimatePrecompileSolidityAddress, + EstimatePrecompileContractJson.abi, + accounts[0].wallet, + ); + const tx = await estimateContract.createFungibleTokenPublic(accounts[0].wallet.address, { + value: BigInt('10000000000000000000'), + gasLimit: 10_000_000, + }); + + const tokenAddress = (await tx.wait()).logs.filter( + (e) => e.fragment.name === Constants.HTS_CONTRACT_EVENTS.CreatedToken, + )[0].args[0]; + return tokenAddress; + } + it('should execute direct call to ethereum precompile 0x1', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( ETH_PRECOMPILE_0x1, @@ -481,7 +523,6 @@ describe.only('Equivalence tests', function () { equivalenceContractId, MAKE_CALL_WITHOUT_AMOUNT, params, - 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); @@ -492,7 +533,7 @@ describe.only('Equivalence tests', function () { }); //EQUIVALENCE-024 - PRECOMPILE ERROR - it.only('internal CALL to 0.0.358 without amount', async function () { + it('internal CALL to 0.0.358 without amount', async function () { const emptyByteArray = new Uint8Array(0); const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); const params = new ContractFunctionParameters().addAddress(evmAddress); @@ -513,6 +554,86 @@ describe.only('Equivalence tests', function () { expect(record.status).to.equal(STATUS_SUCCESS); }); + //EQUIVALENCE-025 - PRECOMPILE ERROR + it('internal CALL to 0.0.359 without amount', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); + const params = new ContractFunctionParameters().addAddress(tokenAddress); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_HTS_CALL_WITHOUT_AMOUNT, + params, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-027 + it('internal CALL to 0.0.359 with amount', async function () { + const emptyByteArray = new Uint8Array(0); + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); + const params = new ContractFunctionParameters().addAddress(tokenAddress); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + MAKE_HTS_CALL_WITH_AMOUNT, + params, + 1_000_000, + 100, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-027 + it('internal CALL to PRNG precompile (0.0.360) withоут amount', async function () { + // const emptyByteArray = new Uint8Array(0); + // const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); + // const params = new ContractFunctionParameters().addAddress(tokenAddress); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + 'getPseudorandomSeed', + EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + + //EQUIVALENCE-028 + it.only('internal CALL to PRNG precompile (0.0.360) with amount', async function () { + // const emptyByteArray = new Uint8Array(0); + // const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); + // const params = new ContractFunctionParameters().addAddress(tokenAddress); + + const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + equivalenceContractId, + 'getPseudorandomSeedWithAmount', + EMPTY_FUNCTION_PARAMS, + 1_000_000, + 100, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + }); + async function testRejection(errorMessage, method, checkMessage, thisObj, args?) { await expect(method.apply(thisObj, args), `${errorMessage}`).to.eventually.be.rejected.and.satisfy((err) => { return err.message.includes(errorMessage); From 55599ed8565956ef8091831c30975045f3817cc7 Mon Sep 17 00:00:00 2001 From: emilevgenievgeorgiev Date: Tue, 21 May 2024 11:44:45 +0300 Subject: [PATCH 08/14] Added equivalence tests Signed-off-by: emilevgenievgeorgiev --- .../tests/acceptance/equivalence.spec.ts | 1405 +++++++++++++++-- .../tests/contracts/EquivalenceContract.json | 30 +- .../tests/contracts/EquivalenceContract.sol | 14 + 3 files changed, 1297 insertions(+), 152 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index ec395e7065..846d9d8d0c 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -20,13 +20,13 @@ import { expect } from 'chai'; import { Utils } from '../helpers/utils'; -import { AliasAccount } from '../clients/servicesClient'; -import { ContractFunctionParameters } from '@hashgraph/sdk'; +import ServicesClient, { AliasAccount } from '../clients/servicesClient'; +import { ContractFunctionParameters, ContractFunctionSelector } from '@hashgraph/sdk'; import RelayAssertions from '../../../relay/tests/assertions'; import EstimatePrecompileContractJson from '../contracts/EstimatePrecompileContract.json'; import Constants from '../helpers/constants'; import EquivalenceContractJson from '../contracts/EquivalenceContract.json'; -import { ethers } from 'ethers'; +import { ethers, toUtf8Bytes } from 'ethers'; import { Precheck } from '../../../relay/src/lib/precheck'; import pino from 'pino'; import { MirrorNodeClient } from '../../../relay/src/lib/clients'; @@ -35,11 +35,38 @@ import EquivalenceDestructContractJson from '../contracts/EquivalenceDestruct.js import ERC20MockJson from '../contracts/ERC20Mock.json'; import { AccountId, Hbar, ContractId } from '@hashgraph/sdk'; import TokenManagementContractJson from '../contracts/TokenManagementContract.json'; +import RelayClient from '../clients/relayClient'; +import { + decodeErrorMessage, + hexToASCII, + prepend0x, + toHexString, + trimPrecedingZeros, +} from '../../../relay/src/formatters'; +import MirrorClient from '../clients/mirrorClient'; const logger = pino(); +const removeLeading0x = (input: string) => { + return input.startsWith('0x') ? input.replace('0x', '') : input; +}; + +const decodeResultData = (input: string) => { + return hexToASCII(removeLeading0x(input)); +}; + +async function testRejection(errorMessage, method, checkMessage, thisObj, args?) { + await expect(method.apply(thisObj, args), `${errorMessage}`).to.eventually.be.rejected.and.satisfy((err) => { + return err.message.includes(errorMessage); + }); +} + describe.only('Equivalence tests', async function () { const signers: AliasAccount[] = []; + // const { servicesNode, mirrorNode, relay }: {servicesNode: ServicesClient; mirrorNode: MirrorNodeClient; relay: RelayClient;} = global; const { servicesNode, mirrorNode, relay }: any = global; + const servicesClient = servicesNode as ServicesClient; + const mirrorClient = mirrorNode as MirrorClient; + const relayClient = relay as RelayClient; const SUCCESS = 'SUCCESS'; const STATUS_SUCCESS = '0x1'; @@ -64,12 +91,16 @@ describe.only('Equivalence tests', async function () { const ADDRESS_0_0_100 = '0.0.100'; const ADDRESS_0_0_358 = '0.0.358'; const ADDRESS_0_0_359 = '0.0.359'; + const ADDRESS_0_0_360 = '0.0.360'; const MAKE_CALL_WITHOUT_AMOUNT = 'makeCallWithoutAmount'; const MAKE_CALL_WITH_AMOUNT = 'makeCallWithAmount'; const MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithoutAmountToIdentityPrecompile'; const MAKE_CALL_WITH_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithAmountToIdentityPrecompile'; const MAKE_HTS_CALL_WITHOUT_AMOUNT = 'htsCallWithoutAmount'; const MAKE_HTS_CALL_WITH_AMOUNT = 'htsCallWithAmount'; + const MAKE_STATICCALL = 'makeStaticCall'; + const MAKE_HTS_STATICCALL = 'htsStaticCall'; + const MAKE_DELEGATECALL = 'makeDelegateCall'; const accounts: AliasAccount[] = []; let tokenAddress; let estimatePrecompileContractReceipt; @@ -90,29 +121,29 @@ describe.only('Equivalence tests', async function () { signers[0] = await servicesNode.createAliasAccount(150, relay.provider, Utils.generateRequestId()); //Deploying Estimate Precompile contract - estimatePrecompileContractReceipt = await servicesNode.deployContract( + estimatePrecompileContractReceipt = await servicesClient.deployContract( EstimatePrecompileContractJson, Constants.GAS_AS_NUMBER.LIMIT_5_000_000, ); estimatePrecompileContractAddress = estimatePrecompileContractReceipt.contractId.toString(); estimatePrecompileSolidityAddress = estimatePrecompileContractReceipt.contractId.toSolidityAddress(); - console.log(estimatePrecompileContractAddress); + console.log(`estimatePrecompileContractAddress: ${estimatePrecompileContractAddress}`); //Deploying Equivalence Destrcut contract - equivalenceDestructContractReceipt = await servicesNode.deployContract( + equivalenceDestructContractReceipt = await servicesClient.deployContract( EquivalenceDestructContractJson, Constants.GAS_AS_NUMBER.LIMIT_5_000_000, ); equivalenceDestructContractId = equivalenceDestructContractReceipt.contractId.toString(); - console.log(equivalenceDestructContractId); + console.log(`equivalenceDestructContractId: ${equivalenceDestructContractId}`); //Deploying Equivalence contract - equivalenceContractReceipt = await servicesNode.deployContract( + equivalenceContractReceipt = await servicesClient.deployContract( EquivalenceContractJson, Constants.GAS_AS_NUMBER.LIMIT_5_000_000, ); equivalenceContractId = equivalenceContractReceipt.contractId.toString(); - console.log(equivalenceContractId); + console.log(`equivalenceContractId: ${equivalenceContractId}`); equivalenceContractSolidityAddress = equivalenceContractReceipt.contractId.toSolidityAddress(); equivalenceContract = new ethers.Contract( @@ -120,12 +151,12 @@ describe.only('Equivalence tests', async function () { EquivalenceContractJson.abi, signers[0].wallet, ); - console.log(equivalenceContract); + console.log(`equivalenceContract:\n${JSON.stringify(equivalenceContract, null, 2)}`); requestId = Utils.generateRequestId(); - const contractMirror = await mirrorNode.get(`/contracts/${estimatePrecompileSolidityAddress}`, requestId); + const contractMirror = await mirrorClient.get(`/contracts/${estimatePrecompileSolidityAddress}`, requestId); - accounts[0] = await servicesNode.createAccountWithContractIdKey( + accounts[0] = await servicesClient.createAccountWithContractIdKey( contractMirror.contract_id, 200, relay.provider, @@ -133,13 +164,22 @@ describe.only('Equivalence tests', async function () { ); tokenAddress = await createFungibleToken(); - console.log(tokenAddress); + console.log(`tokenAddress: ${tokenAddress}`); }); async function getResultByEntityIdAndTxTimestamp(entityId, txTimestamp) { return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); } + /** + * Returns a list of ContractActions for a contract's function executions for a given transactionId or ethereum transaction hash. + * @param transactionIdOrHash Transaction Id or a 32 byte hash with optional 0x prefix + * @returns list of ContractActions + */ + async function getContractActions(transactionIdOrHash: string) { + return await mirrorClient.get(`/contracts/results/${transactionIdOrHash}/actions`); + } + async function createFungibleToken() { estimateContract = new ethers.Contract( prefix + estimatePrecompileSolidityAddress, @@ -158,7 +198,7 @@ describe.only('Equivalence tests', async function () { } it('should execute direct call to ethereum precompile 0x1', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( ETH_PRECOMPILE_0x1, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, @@ -172,20 +212,20 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQV-003 - Should fail with INVALID_SOLIDIY_ADDRESS + // EQV-003 - Should fail with INVALID_SOLIDIY_ADDRESS // async function testRejection(errorMessage, method, checkMessage, thisObj, args?) it('should execute direct call to address 361 to 750 without amount', async function () { const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000]; await testRejection(CONTRACT_EXECUTION_EXCEPTION, servicesNode.executeContractCall, true, servicesNode, args); }); - //EQV-004 - Should fail with INVALID_FEE_SUBMITTED + // EQV-004 - Should fail with INVALID_FEE_SUBMITTED it('should execute direct call to ethereum precompile 361 to 750 with amount', async function () { const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; await testRejection(INVALID_FEE_SUBMITTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); }); - //EQV-005 - OK - ??? Should it be like that? - Should it be successfull + // EQV-005 - OK - ??? Should it be like that? - Should it be successfull it('should execute direct call to address 751 without amount', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( ADDRESS_0x800, @@ -201,14 +241,14 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(STATUS_SUCCESS); //Check if it should be CONTRACT_EXECUTION_EXCEPTION }); - //EQV-005 - OK - ??? Should it be like that? - Should it be successfull + // EQV-005 - OK - ??? Should it be like that? - Should it be successfull it('should execute direct call to address 751 with amount', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( ADDRESS_0x800, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, - 100, //add the amount + 100, ); const record = await getResultByEntityIdAndTxTimestamp(ADDRESS_0x800, contractExecuteTimestamp); @@ -218,7 +258,7 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQV-006 - should be successfull - OK + // EQV-006 - should be successfull - OK it('should execute direct call to address over 1000 without amount', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( ETH_PRECOMPILE_0x1001, @@ -233,7 +273,7 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQV-006 - should be successfull - OK + // EQV-006 - should be successfull - OK it('should execute direct call to address over 1000 without amount - case contract ', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( estimatePrecompileContractAddress, @@ -248,7 +288,7 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQV-007 - Should fail with INVALID_SOLIDIY_ADDRESS but it is SUCCESSFULL and creates Inactive EVM Address - ???????? + // EQV-007 - Should fail with INVALID_SOLIDIY_ADDRESS but it is SUCCESSFULL and creates Inactive EVM Address - ???????? it('should execute direct call to non-existing contract', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( NON_EXISTING_CONTRACT_ID, @@ -264,7 +304,7 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQV-008 - should be successfull - OK + // EQV-008 - should be successfull - OK it('should execute direct call to address over 1000 without amount - case payable contract ', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( equivalenceDestructContractId, @@ -279,18 +319,19 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQV-009 - OK - should be unsuccessfull + // EQV-009 - OK - should be unsuccessfull it('should execute direct call to address over 1000 with amount - case non-payable contract ', async function () { const args = [estimatePrecompileContractAddress, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; await testRejection(CONTRACT_REVERT_EXECUTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); }); it('should execute direct call to address over 1000 with amount', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( 'ETH_PRECOMPILE_0x1001', NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 100, //add the amount + 1_000_000, + 100, ); const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); @@ -300,17 +341,17 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); }); - //EQV-13 This should be successful due to hollow account creation + // EQV-13 This should be successful due to hollow account creation it('should execute direct call to address over 1000 with amount - case hollow acconut', async function () { const hollowAccount = ethers.Wallet.createRandom(); const hollowAcAddress = hollowAccount.address.toString(); - const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( hollowAcAddress, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 1_000_000, - 100, //add the amount + 100, ); const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); @@ -321,16 +362,14 @@ describe.only('Equivalence tests', async function () { }); //----------Internal calls---------------- - //EQUIVALENCE-014 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be something else + // EQUIVALENCE-014 - Same as mirror node - fails with PRECOMPILE_ERROR it should be something else it('internal CALL to 0.0.0 without amount - should be successfull', async function () { const emptyByteArray = new Uint8Array(0); const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); const params = new ContractFunctionParameters().addAddress(evmAddress); params.addBytes(emptyByteArray); - console.log('PARAMS ARE '); - console.log(params); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, MAKE_CALL_WITHOUT_AMOUNT, params, @@ -338,102 +377,183 @@ describe.only('Equivalence tests', async function () { ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + /** + * '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"CALL" + "caller":"0.0.1045" + "caller_type":"CONTRACT" + "from":"0x0000000000000000000000000000000000000415" + "gas":466906 + "gas_used":466906 + "index":1 + "input":"0x" + "recipient":null + "recipient_type":null + "result_data":"0x505245434f4d50494c455f4552524f52" + "result_data_type":"ERROR" + "timestamp":"1715842544.840853219" + "to":"0x0000000000000000000000000000000000000000" + "value":0}' + */ expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); }); - //EQUIVALENCE-015 - PRECOMPILE ERROR - Same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED - it('internal CALL to 0.0.0 with amount', async function () { - const emptyByteArray = new Uint8Array(0); + // EQUIVALENCE-015 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED + it('internal CALL to 0.0.0 with amount should fail with INVALID_FEE_SUBMITTED, as the intermediary contract should not transfer funds to 0x0.', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); - - const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( equivalenceContractId, MAKE_CALL_WITH_AMOUNT, - params, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), 1_000_000, - 100, //add the amount + 100, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); }); - //EQUIVALENCE-016 - it('internal CALL to 0.0.1 without amount - should be successfull', async function () { - const emptyByteArray = new Uint8Array(0); + // EQUIVALENCE-016 + it('internal CALL to 0.0.1 without amount - should be successfull (Expected to execute the ecrecover precompile through the intermediary contract.)', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, MAKE_CALL_WITHOUT_AMOUNT, - params, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('OUTPUT'); + /* '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"PRECOMPILE" + "caller":"0.0.1122" + "caller_type":"CONTRACT" + "from":"0x0000000000000000000000000000000000000462" + "gas":469355 + "gas_used":3000 + "index":1 + "input":"0x" + "recipient":"0.0.1" + "recipient_type":"CONTRACT" + "result_data":"0x" + "result_data_type":"OUTPUT" + "timestamp":"1715845647.860197419" + "to":"0x0000000000000000000000000000000000000001" + "value":0}' + */ }); - //EQUIVALENCE-017 - it('internal CALL to 0.0.2 without amount - should be successfull', async function () { - const emptyByteArray = new Uint8Array(0); + // EQUIVALENCE-017 + it('internal CALL to 0.0.2 without amount should execute the SHA-256 precompile through the intermediary contract.', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, MAKE_CALL_WITHOUT_AMOUNT, - params, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('OUTPUT'); + /* + '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"PRECOMPILE" + "caller":"0.0.1131" + "caller_type":"CONTRACT" + "from":"0x000000000000000000000000000000000000046b" + "gas":469355 + "gas_used":60 + "index":1 + "input":"0x" + "recipient":"0.0.2" + "recipient_type":"CONTRACT" + "result_data":"0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + "result_data_type":"OUTPUT" + "timestamp":"1715846596.410665470" + "to":"0x0000000000000000000000000000000000000002" + "value":0}' + */ }); - //EQUIVALENCE-018 - it('internal CALL to 0.0.3 without amount - should be successfull', async function () { - const emptyByteArray = new Uint8Array(0); + // EQUIVALENCE-018 + it('internal CALL to 0.0.3 without amount should execute the RIPEMD-160 precompile through the intermediary contract.', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, MAKE_CALL_WITHOUT_AMOUNT, - params, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('OUTPUT'); + /* + '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"PRECOMPILE" + "caller":"0.0.1149" + "caller_type":"CONTRACT" + "from":"0x000000000000000000000000000000000000047d" + "gas":469355 + "gas_used":600 + "index":1 + "input":"0x" + "recipient":"0.0.3" + "recipient_type":"CONTRACT" + "result_data":"0x0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31" + "result_data_type":"OUTPUT" + "timestamp":"1715847501.800742931" + "to":"0x0000000000000000000000000000000000000003" + "value":0}' + */ }); - //EQUIVALENCE-019 - ОК - it('internal CALL to 0.0.4 without amount - should be successfull', async function () { + // EQUIVALENCE-019 + it('internal CALL to 0.0.4 without amount should execute the identity precompile through the intermediary contract.', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); - const params = new ContractFunctionParameters().addAddress(evmAddress); - console.log('PARAMS ARE '); - console.log(params); - - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE, EMPTY_FUNCTION_PARAMS, @@ -441,202 +561,1187 @@ describe.only('Equivalence tests', async function () { ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('OUTPUT'); + /* + '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"PRECOMPILE" + "caller":"0.0.1158" + "caller_type":"CONTRACT" + "from":"0x0000000000000000000000000000000000000486" + "gas":469422 + "gas_used":30 + "index":1 + "input":"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f2c20576f726c640000000000000000000000000000000000000000" + "recipient":"0.0.4" + "recipient_type":"CONTRACT" + "result_data":"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f2c20576f726c640000000000000000000000000000000000000000" + "result_data_type":"OUTPUT" + "timestamp":"1715847778.640519586" + "to":"0x0000000000000000000000000000000000000004" + "value":0}' + */ }); - //EQUIVALENCE-020 - OK - it('internal CALL to 0.0.4 with amount', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); - const params = new ContractFunctionParameters().addAddress(evmAddress); - console.log('PARAMS ARE '); - console.log(params); + // EQUIVALENCE-020 + // FROM https://www.notion.so/limechain/In-Equivalance-Test-plan-json-rpc-relay-10a7cd268daa44e9b414f3f5410bc08d says 'These should all fail with INVALID_FEE_SUBMITTED when called through an intermediary contract.' + ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7', '0.0.8', '0.0.9'].forEach((address) => { + it(`internal CALL to precompile address ${address} with amount should fail with INVALID_FEE_SUBMITTED when called through an intermediary contract.`, async function () { + const evmAddress = Utils.idToEvmAddress(address); + const message = 'Encode me!'; + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + MAKE_CALL_WITH_AMOUNT, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), + 2_000_000, + 100, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); + }); + }); + + // EQUIVALENCE-021 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS + // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 + ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { + it(`internal CALL to ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract.`, async function () { + const evmAddress = Utils.idToEvmAddress(address); + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.call_operation_type).to.equal('CALL'); + expect(childRecord.call_type).to.equal('CALL'); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_SOLIDITY_ADDRESS); + /* + '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"CALL" + "caller":"0.0.1224" + "caller_type":"CONTRACT" + "from":"0x00000000000000000000000000000000000004c8" + "gas":466894 + "gas_used":466894 + "index":1 + "input":"0x" + "recipient":"0.0.100" + "recipient_type":"ACCOUNT" + "result_data":"0x505245434f4d50494c455f4552524f52" + "result_data_type":"ERROR" + "timestamp":"1715855739.520497716" + "to":"0x0000000000000000000000000000000000000064" + "value":0}' + */ + }); + }); + + // EQUIVALENCE-022 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED + // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 + ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { + it(`internal CALL to adress '${address}' with amount should fail with 'INVALID_FEE_SUBMITTED' when attempted through an intermediary contract.`, async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_100); + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + MAKE_CALL_WITH_AMOUNT, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + 1_000_000, + 100, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.call_operation_type).to.equal('CALL'); + expect(childRecord.call_type).to.equal('CALL'); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); + /* + '{ + "call_depth": 1, + "call_operation_type": "CALL", + "call_type": "CALL", + "caller": "0.0.1038", + "caller_type": "CONTRACT", + "from": "0x000000000000000000000000000000000000040e", + "gas": 927957, + "gas_used": 927957, + "index": 1, + "input": "0x", + "recipient": "0.0.100", + "recipient_type": "ACCOUNT", + "result_data": "0x505245434f4d50494c455f4552524f52", + "result_data_type": "ERROR", + "timestamp": "1715935668.132923212", + "to": "0x0000000000000000000000000000000000000064", + "value": 100 + }' + */ + }); + }); + + // EQUIVALENCE-023 - Same as mirror node - PRECOMPILE ERROR + it('internal CALL to 0.0.358 without amount should execute the HTS precompiles through the intermediary contract.', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + /* + '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"CALL" + "caller":"0.0.1256" + "caller_type":"CONTRACT" + "from":"0x00000000000000000000000000000000000004e8" + "gas":466883 + "gas_used":466883 + "index":1 + "input":"0x" + "recipient":null + "recipient_type":null + "result_data":"0x505245434f4d50494c455f4552524f52" + "result_data_type":"ERROR" + "timestamp":"1715860160.719665055" + "to":"0x0000000000000000000000000000000000000166" + "value":0}' + */ + }); + + // EQUIVALENCE-024 - Same as mirror node - PRECOMPILE ERROR + it('internal CALL to address 0.0.358 with amount for other functions than TokenCreate i.e. Approve/Associate should result in INVALID_FEE_SUBMITTED through the intermediary contract.', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + MAKE_CALL_WITH_AMOUNT, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + 1_000_000, + 100, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); + /* + '{"call_depth":1 + "call_operation_type":"CALL" + "call_type":"CALL" + "caller":"0.0.1265" + "caller_type":"CONTRACT" + "from":"0x00000000000000000000000000000000000004f1" + "gas":959113 + "gas_used":959113 + "index":1 + "input":"0x" + "recipient":null + "recipient_type":null + "result_data":"0x505245434f4d50494c455f4552524f52" + "result_data_type":"ERROR" + "timestamp":"1715860279.970723513" + "to":"0x0000000000000000000000000000000000000166" + "value":0}' + */ + }); + + // EQUIVALENCE-025 - Same as mirror node + it.only('internal CALL to 0.0.360 without amount should execute the ExchangeRate precompile through the intermediary contract.', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_360); + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_HTS_CALL_WITHOUT_AMOUNT, + new ContractFunctionParameters().addAddress(tokenAddress), + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; - const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('CALL'); + expect(childRecord.call_type).to.equal('SYSTEM'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('OUTPUT'); + /* + '{ + "call_depth": 1, + "call_operation_type": "CALL", + "call_type": "SYSTEM", + "caller": "0.0.1297", + "caller_type": "CONTRACT", + "from": "0x0000000000000000000000000000000000000511", + "gas": 49094, + "gas_used": 100, + "index": 1, + "input": "0x19f373610000000000000000000000000000000000000000000000000000000000000513", + "recipient": "0.0.359", + "recipient_type": "CONTRACT", + "result_data": "0x00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000001", + "result_data_type": "OUTPUT", + "timestamp": "1715867826.679620882", + "to": "0x0000000000000000000000000000000000000167", + "value": 0 + }' + */ + }); + + // EQUIVALENCE-026 - Same as mirror node + it.only('internal CALL to addresses 0.0.361 without amount(PRNG precompile) should execute it through the intermediary contract.', async function () { + const evmAddress = Utils.idToEvmAddress(ETH_PRECOMPILE_0x361); + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITH_AMOUNT_TO_IDENTITY_PRECOMPILE, + 'getPseudorandomSeed', EMPTY_FUNCTION_PARAMS, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('CALL'); + expect(childRecord.call_type).to.equal('SYSTEM'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + /* + * '{ + "call_depth": 1, + "call_operation_type": "CALL", + "call_type": "SYSTEM", + "caller": "0.0.1306", + "caller_type": "CONTRACT", + "from": "0x000000000000000000000000000000000000051a", + "gas": 49808, + "gas_used": 0, + "index": 1, + "input": "0xd83bf9a1", + "recipient": "0.0.361", + "recipient_type": "CONTRACT", + "result_data": "0x62f0bc0bb45c2750c1afebdefb68e6d0a8ea70351a43ce98820d30571fdd4ad5", + "result_data_type": "OUTPUT", + "timestamp": "1715869135.530404307", + "to": "0x0000000000000000000000000000000000000169", + "value": 0 + }' + */ + }); + + // EQUIVALENCE-027 - Same as mirror node - succeeds but should result in INVALID_FEE_SUBMITTED + it('internal CALL to addresses 0.0.359 with amount(ExchangeRate precompile) should result in INVALID_FEE_SUBMITTED through the intermediary contract.', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + MAKE_HTS_CALL_WITH_AMOUNT, + new ContractFunctionParameters().addAddress(tokenAddress), 1_000_000, - 100, //add the amount + 100, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; - //TODO: It returns INVALID_FEE_SUBMITTED and we have to implement the check for that expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('CALL'); + expect(childRecord.call_type).to.equal('SYSTEM'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); + /* + '{ + "call_depth": 1, + "call_operation_type": "CALL", + "call_type": "SYSTEM", + "caller": "0.0.1065", + "caller_type": "CONTRACT", + "from": "0x0000000000000000000000000000000000000429", + "gas": 959554, + "gas_used": 100, + "index": 1, + "input": "0x19f37361000000000000000000000000000000000000000000000000000000000000042b", + "recipient": "0.0.359", + "recipient_type": "CONTRACT", + "result_data": "0x00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000001", + "result_data_type": "OUTPUT", + "timestamp": "1716205365.582565806", + "to": "0x0000000000000000000000000000000000000167", + "value": 0 + }' + */ }); - //EQUIVALENCE-021 - The same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS - it('internal CALL to 0.0.100 without amount - should be successfull', async function () { - const emptyByteArray = new Uint8Array(0); - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_100); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); + // EQUIVALENCE-028 - Same as mirror node - succeeds but should result in INVALID_FEE_SUBMITTED + it('internal CALL to addresses 0.0.361 with amount (PRNG precompile) should result in INVALID_FEE_SUBMITTED through the intermediary contract.', async function () { + const evmAddress = Utils.idToEvmAddress(ETH_PRECOMPILE_0x361); + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + 'getPseudorandomSeedWithAmount', + EMPTY_FUNCTION_PARAMS, + 1_000_000, + 100, + ); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); + /* + '{ + "call_depth": 1, + "call_operation_type": "CALL", + "call_type": "SYSTEM", + "caller": "0.0.1315", + "caller_type": "CONTRACT", + "from": "0x0000000000000000000000000000000000000523", + "gas": 960397, + "gas_used": 0, + "index": 1, + "input": "0xd83bf9a1", + "recipient": "0.0.361", + "recipient_type": "CONTRACT", + "result_data": "0x29cd8faf6c267af3e6a2aa76d0609dafdcc5fe766141df230ab892398a4db347", + "result_data_type": "OUTPUT", + "timestamp": "1715869282.420056139", + "to": "0x0000000000000000000000000000000000000169", + "value": 0 + }' + */ + }); + + // EQUIVALENCE-029 range 0.0.361..0.0.750 !!PRNG is 361!! + [ + // '0.0.362', + // '0.0.450', + // '0.0.599', + // '0.0.700', + // '0.0.751', + // '0.0.785', + // '0.0.1000', + ].forEach((address) => { + it(`internal CALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { + const evmAddress = Utils.idToEvmAddress(address); + const { contractExecuteTimestamp, contractExecutedTransactionId } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_CALL_WITHOUT_AMOUNT, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const actions = await getContractActions(record.hash); + const childRecordError = decodeResultData(actions[1].result_data); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecordError).to.equal(INVALID_SOLIDITY_ADDRESS); + }); + }); + + // EQUIVALENCE-030 range 0.0.361..0.0.750 !!PRNG is 361!! + [ + // '0.0.362', + // '0.0.450', + // '0.0.599', + // '0.0.750', + ].forEach((address) => { + it(`internal CALL to address ${address} with amount should fail with INVALID_FEE_SUBMITTED when called through an intermediary contract.`, async function () {}); + }); + + /* EQUIVALENCE-031 range 0.0.751..0.0.1000 + * This should be verified what is the behaviour + * Per other notes sheet: + * This should work for address 0.0.800 + * And if account exists and has receiverSigRequired = false + * And if (account exists) and (account has receiverSigRequired = true) and (account is sender) + */ + + [ + // '0.0.751', + // '0.0.800', + // '0.0.1000', + ].forEach((address) => { + it(`internal CALL to address ${address} with amount`, async function () {}); + }); + + // EQUIVALENCE-032 + // EQUIVALENCE-033 + // EQUIVALENCE-034 + // EQUIVALENCE-035 + // EQUIVALENCE-036 + // EQUIVALENCE-037 + // EQUIVALENCE-038 + // EQUIVALENCE-039 + + // EQUIVALENCE-040 - PRECOMPILE ERROR - Same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED + it('internal STATICCALL to address(0.0.0) without amount should succeed with noop through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, + MAKE_STATICCALL, params, 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.call_type).to.equal('CALL'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); }); - //EQUIVALENCE-022 --- The same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED - it('internal CALL to 0.0.100 with amount - should be successfull', async function () { - const emptyByteArray = new Uint8Array(0); - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_100); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); + // EQUIVALENCE-041; + it('internal STATICCALL to address 0.0.1 with known hash and signature should execute the ecrecover precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - const { contractExecuteTimestamp } = await servicesNode.executeContractCallWithAmount( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITH_AMOUNT, + MAKE_STATICCALL, params, - 1_000_000, - 100, //add the amount + 500_000, ); + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); }); - //EQUIVALENCE-023 - PRECOMPILE ERROR - it('internal CALL to 0.0.358 without amount', async function () { - const emptyByteArray = new Uint8Array(0); - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); + // EQUIVALENCE-042; + it('internal STATICCALL to address 0.0.2 should execute the SHA-256 precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, + MAKE_STATICCALL, params, + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); }); - //EQUIVALENCE-024 - PRECOMPILE ERROR - it('internal CALL to 0.0.358 without amount', async function () { - const emptyByteArray = new Uint8Array(0); + // EQUIVALENCE-043; + it('internal STATICCALL to address 0.0.3 should execute the RIPEMD-160 precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_STATICCALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + }); + + // EQUIVALENCE-044; + it('internal STATICCALL to address 0.0.4 should execute the identity precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); + const message = 'Encode me!'; + const messageBytes = toUtf8Bytes(message); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(messageBytes); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_STATICCALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + expect(message).to.equal( + decodeResultData(childRecord.result_data), + `Decoded 'result_data' from child record is not '${message}'`, + ); + }); + + // EQUIVALENCE-045 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS + // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 + ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { + it(`STATICCALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { + const evmAddress = Utils.idToEvmAddress(address); + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_STATICCALL, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.call_type).to.equal('CALL'); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_SOLIDITY_ADDRESS); + /* + '{ + "call_depth": 1, + "call_operation_type": "STATICCALL", + "call_type": "CALL", + "caller": "0.0.1038", + "caller_type": "CONTRACT", + "from": "0x000000000000000000000000000000000000040e", + "gas": 466983, + "gas_used": 466983, + "index": 1, + "input": "0x", + "recipient": "0.0.10", + "recipient_type": "ACCOUNT", + "result_data": "0x505245434f4d50494c455f4552524f52", + "result_data_type": "ERROR", + "timestamp": "1715935738.322606675", + "to": "0x000000000000000000000000000000000000000a", + "value": 0 + }' + */ + }); + }); + + // EQUIVALENCE-046; + // BUG: [Direct call w/o value to system accounts [0.0.361-0.0.750] result in precompile error] https://github.com/hashgraph/hedera-services/issues/11033 + it('internal STATICCALL to address 0.0.358 without amount should execute the HTS precompiles through the intermediary contract', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITH_AMOUNT, + MAKE_STATICCALL, params, - 1_000_000, - 100, //add the amount + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); }); - //EQUIVALENCE-025 - PRECOMPILE ERROR - it('internal CALL to 0.0.359 without amount', async function () { - const emptyByteArray = new Uint8Array(0); + // EQUIVALENCE-047; + it('internal STATICCALL to addresses 0.0.359 (ExchangeRate precompile) without amount should execute the ExchangeRate precompile through the intermediary contract', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); const params = new ContractFunctionParameters().addAddress(tokenAddress); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_HTS_CALL_WITHOUT_AMOUNT, + MAKE_HTS_STATICCALL, params, + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + /* + '{ + "call_depth": 1, + "call_operation_type": "STATICCALL", + "call_type": "SYSTEM", + "caller": "0.0.1049", + "caller_type": "CONTRACT", + "from": "0x0000000000000000000000000000000000000419", + "gas": 467412, + "gas_used": 100, + "index": 1, + "input": "0x19f37361000000000000000000000000000000000000000000000000000000000000041b", + "recipient": "0.0.359", + "recipient_type": "CONTRACT", + "result_data": "0x00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000001", + "result_data_type": "OUTPUT", + "timestamp": "1716203726.922385630", + "to": "0x0000000000000000000000000000000000000167", + "value": 0 + }' + */ }); - //EQUIVALENCE-027 - it('internal CALL to 0.0.359 with amount', async function () { - const emptyByteArray = new Uint8Array(0); + // EQUIVALENCE-048; + it('internal STATICCALL to addresses 0.0.361 (PRNG precompile) without amount should execute the PRNG precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ETH_PRECOMPILE_0x361); + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + 'getPseudorandomSeedStaticCall', + EMPTY_FUNCTION_PARAMS, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + + expect(childRecord.call_operation_type).to.equal('STATICCALL'); + expect(childRecord.call_type).to.equal('SYSTEM'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + }); + + // EQUIVALENCE-049 range 0.0.361..0.0.750 !!PRNG is 361!! + [ + // '0.0.362', + // '0.0.450', + // '0.0.599', + // '0.0.700', + // '0.0.751', + // '0.0.785', + // '0.0.1000', + ].forEach((address) => { + it(`internal STATICCALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { + const params = new ContractFunctionParameters() + .addAddress(Utils.idToEvmAddress(address)) + .addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp, contractExecutedTransactionId } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_STATICCALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const actions = await getContractActions(record.hash); + const childRecordError = decodeResultData(actions[1].result_data); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecordError).to.equal(INVALID_SOLIDITY_ADDRESS); + }); + }); + + // EQUIVALENCE-050; + // STATICCALL to address(over 0.0.1000) wi/o amount (case contract). Create a contract within the test. Grab the contractID and execute InternalCall + // Expected to succeed if the contract exists and is called through an intermediary contract. + // it('internal STATICCALL to address(over 0.0.1000) wi/o amount (case contract)', async function () { + // const accounts = await mirrorClient.get('/accounts?account.id=gt:0.0.1000'); + // console.log(JSON.stringify(accounts)) + // }); + + // EQUIVALENCE-051; + // STATICCALL to address(over 0.0.1000) wi/o amount (case invalid contractID): In here we should either hit an invalid contractID(this is challenging due to the different environments) + // Or we should create an account and use accountID instead (needs to be verified) + // Should fail with INVALID_SOLIDITY_ADDRESS through the intermediary contract. + // it('internal STATICCALL to address(over 0.0.1000) wi/o amount (case invalid contractID)', async function () { + // }); + + // EQUIVALENCE-052 - PRECOMPILE ERROR - Same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED + it('internal DELEGATECALL to address(0.0.0) without amount should succeed with noop through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.call_type).to.equal('CALL'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + }); + + // EQUIVALENCE-053 + it('internal DELEGATECALL to address 0.0.1 with known hash and signature should execute the ecrecover precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + }); + + // EQUIVALENCE-054 + it('internal DELEGATECALL to address 0.0.2 should execute the SHA-256 precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + }); + + // EQUIVALENCE-055 + it('internal DELEGATECALL to address 0.0.3 should execute the RIPEMD-160 precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + }); + + // EQUIVALENCE-056 + it('internal DELEGATECALL to address 0.0.4 should execute the identity precompile through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); + const message = 'Encode me!'; + const messageBytes = toUtf8Bytes(message); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(messageBytes); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.call_type).to.equal('PRECOMPILE'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + expect(message).to.equal( + decodeResultData(childRecord.result_data), + `Decoded 'result_data' from child record is not '${message}'`, + ); + }); + + // EQUIVALENCE-057 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS + // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 + ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { + it(`internal DELEGATECALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { + const evmAddress = Utils.idToEvmAddress(address); + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.call_type).to.equal('CALL'); + expect(childRecord.result_data_type).to.equal('ERROR'); + expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_SOLIDITY_ADDRESS); + /* + '{ + "call_depth": 1, + "call_operation_type": "DELEGATECALL", + "call_type": "CALL", + "caller": "0.0.1038", + "caller_type": "CONTRACT", + "from": "0x000000000000000000000000000000000000040e", + "gas": 466962, + "gas_used": 466962, + "index": 1, + "input": "0x", + "recipient": "0.0.10", + "recipient_type": "ACCOUNT", + "result_data": "0x505245434f4d50494c455f4552524f52", + "result_data_type": "ERROR", + "timestamp": "1715935792.722483506", + "to": "0x000000000000000000000000000000000000000a", + "value": 0 + }' + */ + }); + }); + + // EQUIVALENCE-058 + // BUG: [Direct call w/o value to system accounts [0.0.361-0.0.750] result in precompile error] https://github.com/hashgraph/hedera-services/issues/11033 + it('internal DELEGATECALL to address 0.0.358 without amount should execute the HTS precompiles through the intermediary contract', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); + const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + }); + + // EQUIVALENCE-059 + // Investigate + it.only('internal DELEGATECALL to addresses 0.0.359 (ExchangeRate precompile) without amount should execute the ExchangeRate precompile through the intermediary contract', async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); const params = new ContractFunctionParameters().addAddress(tokenAddress); - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_HTS_CALL_WITH_AMOUNT, + 'htsDelegateCall', params, - 1_000_000, - 100, + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); + /* + '{ + "call_depth": 1, + "call_operation_type": "DELEGATECALL", + "call_type": "SYSTEM", + "caller": "0.0.1049", + "caller_type": "CONTRACT", + "from": "0x0000000000000000000000000000000000000419", + "gas": 467413, + "gas_used": 467413, + "index": 1, + "input": "0x19f37361000000000000000000000000000000000000000000000000000000000000041b", + "recipient": "0.0.359", + "recipient_type": "CONTRACT", + "result_data": "0x505245434f4d50494c455f4552524f52", + "result_data_type": "ERROR", + "timestamp": "1716203893.732399055", + "to": "0x0000000000000000000000000000000000000167", + "value": 0 + }' + */ }); - //EQUIVALENCE-027 - it('internal CALL to PRNG precompile (0.0.360) withоут amount', async function () { - // const emptyByteArray = new Uint8Array(0); - // const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); - // const params = new ContractFunctionParameters().addAddress(tokenAddress); + it.only('DELEGATECALL to addresses 0.0.359 (ExchangeRate precompile)...', async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_360); // address 360 is 0000000000000000000000000000000000000168, mirror node calls that address + const evmAddress1 = Utils.idToEvmAddress(ADDRESS_0_0_359); // 359 is 0000000000000000000000000000000000000167 - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - 'getPseudorandomSeed', - EMPTY_FUNCTION_PARAMS, + 'makeDelegateCall', + new ContractFunctionParameters() + .addAddress(evmAddress) + .addFunction(tokenAddress, new ContractFunctionSelector('isToken(address)')), + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); }); - //EQUIVALENCE-028 - it.only('internal CALL to PRNG precompile (0.0.360) with amount', async function () { - // const emptyByteArray = new Uint8Array(0); - // const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); - // const params = new ContractFunctionParameters().addAddress(tokenAddress); - - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + // EQUIVALENCE-060 + it('internal DELEGATECALL to addresses 0.0.361 (PRNG precompile) without amount should execute the PRNG precompile through the intermediary contract', async function () { + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - 'getPseudorandomSeedWithAmount', + 'getPseudorandomSeedDelegateCall', EMPTY_FUNCTION_PARAMS, - 1_000_000, - 100, + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; expect(record.contract_id).to.equal(equivalenceContractId); expect(record.result).to.equal(SUCCESS); expect(record.status).to.equal(STATUS_SUCCESS); + + expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); + expect(childRecord.call_type).to.equal('SYSTEM'); + expect(childRecord.to).to.equal(Utils.idToEvmAddress(ETH_PRECOMPILE_0x361)); + expect(childRecord.result_data_type).to.equal( + 'OUTPUT', + `Error in child record: "${decodeResultData(childRecord.result_data)}"`, + ); }); - async function testRejection(errorMessage, method, checkMessage, thisObj, args?) { - await expect(method.apply(thisObj, args), `${errorMessage}`).to.eventually.be.rejected.and.satisfy((err) => { - return err.message.includes(errorMessage); + // EQUIVALENCE-061 range 0.0.361..0.0.750 !!PRNG is 361!! + // BUG: 3 different outcomes for address range + [ + // '0.0.362', + // '0.0.450', + // '0.0.751', + // '0.0.741', + // '0.0.800', + // '0.0.1000', + ].forEach((address) => { + it(`internal DELEGATECALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { + const params = new ContractFunctionParameters() + .addAddress(Utils.idToEvmAddress(address)) + .addBytes(new Uint8Array(0)); + + const { contractExecuteTimestamp, contractExecutedTransactionId } = await servicesClient.executeContractCall( + equivalenceContractId, + MAKE_DELEGATECALL, + params, + 500_000, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + const childRecordError = hexToASCII(childRecord.result_data); + + expect(record.contract_id).to.equal(equivalenceContractId); + expect(record.result).to.equal(SUCCESS); + expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecordError).to.equal(INVALID_SOLIDITY_ADDRESS); }); - } + }); }); diff --git a/packages/server/tests/contracts/EquivalenceContract.json b/packages/server/tests/contracts/EquivalenceContract.json index d096f2282a..bd4b90b8b5 100644 --- a/packages/server/tests/contracts/EquivalenceContract.json +++ b/packages/server/tests/contracts/EquivalenceContract.json @@ -191,6 +191,32 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "getPseudorandomSeedDelegateCall", + "outputs": [ + { + "internalType": "bytes32", + "name": "randomBytes", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getPseudorandomSeedStaticCall", + "outputs": [ + { + "internalType": "bytes32", + "name": "randomBytes", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "getPseudorandomSeedWithAmount", @@ -489,8 +515,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b5061114c806100206000396000f3fe6080604052600436106101705760003560e01c80638771074f116100d6578063b51c4f961161007f578063e34c93b811610059578063e34c93b8146103b9578063e717ea61146103cc578063f8b2cb4f146103e757600080fd5b8063b51c4f9614610370578063d558bd8e1461038f578063d83bf9a1146103a457600080fd5b8063b0d869b1116100b0578063b0d869b11461032a578063b2539b2e1461034a578063b2a07c3e1461035d57600080fd5b80638771074f146102ef57806388b0764a1461030f578063915482281461031757600080fd5b806367edbd6d116101385780637e38d3a3116101125780637e38d3a31461029057806381ea4408146102b0578063845b27cd146102cf57600080fd5b806367edbd6d146102465780636f68a38e1461025b57806375ce65191461027b57600080fd5b806315ed14111461017557806319f88c7d146101ac5780631d620e2a146101da5780634e731583146102115780636144ef6d1461023e575b600080fd5b34801561018157600080fd5b50610195610190366004610e92565b61040f565b6040516101a3929190610fb0565b60405180910390f35b3480156101b857600080fd5b506101cc6101c7366004610fcb565b610477565b6040519081526020016101a3565b3480156101e657600080fd5b506101fa6101f536600461104e565b6104a8565b6040805192151583529015156020830152016101a3565b34801561021d57600080fd5b5061023161022c36600461104e565b610587565b6040516101a39190611070565b6101cc6105e3565b34801561025257600080fd5b5061019561068f565b34801561026757600080fd5b506101fa61027636600461104e565b61075d565b34801561028757600080fd5b506101956107ef565b34801561029c57600080fd5b506101956102ab366004610e92565b6108aa565b3480156102bc57600080fd5b506101cc6102cb36600461104e565b3f90565b3480156102db57600080fd5b506101fa6102ea36600461104e565b610903565b3480156102fb57600080fd5b5061019561030a366004610e92565b610997565b6101956109ee565b610195610325366004610e92565b610aac565b34801561033657600080fd5b506101cc610345366004611083565b610b06565b6101fa61035836600461104e565b610bcc565b6101cc61036b366004611083565b610c62565b34801561037c57600080fd5b506101cc61038b36600461104e565b3b90565b34801561039b57600080fd5b50610195610cf9565b3480156103b057600080fd5b506101cc610db4565b6101cc6103c7366004610fcb565b610e30565b3480156103d857600080fd5b506040513081526020016101a3565b3480156103f357600080fd5b506101cc61040236600461104e565b6001600160a01b03163190565b60006060836001600160a01b03168360405161042b919061109c565b600060405180830381855afa9150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b606091505b50909590945092505050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2505195945050505050565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516104ff919061109c565b600060405180830381855afa9150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b606091505b50915091508115610580576000808280602001905181019061056191906110b8565b915060030b91508160070b601614955085801561057b5750805b945050505b5050915091565b6060813b60008167ffffffffffffffff8111156105a6576105a6610e7c565b6040519080825280601f01601f1916602001820160405280156105d0576020820181803683370190505b50905081600060208301863c9392505050565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b1790529051600091829182916101699134916106239161109c565b60006040518083038185875af1925050503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b606091505b50915091508161067457600080fd5b8080602001905181019061068891906110fd565b9250505090565b6000606060006040516020016106c5906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016106f59190611070565b60408051601f198184030181529082905261070f9161109c565b6000604051808303816000865af19150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b606091505b50915091505050509091565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516107b4919061109c565b600060405180830381855af49150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b600060606000604051602001610825906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016108559190611070565b60408051601f198184030181529082905261086f9161109c565b600060405180830381855af49150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b0316836040516108c6919061109c565b6000604051808303816000865af19150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b1790525161095a919061109c565b6000604051808303816000865af19150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60006060836001600160a01b0316836040516109b3919061109c565b600060405180830381855af49150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b600060606000604051602001610a24906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b03163484604051602001610a559190611070565b60408051601f1981840301815290829052610a6f9161109c565b60006040518083038185875af1925050503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b03163484604051610ac9919061109c565b60006040518083038185875af1925050503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b60008060006101686001600160a01b031684604051602401610b2a91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610b5f919061109c565b6000604051808303816000865af19150503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b606091505b509150915081610bb057600080fd5b80806020019051810190610bc491906110fd565b949350505050565b6040516001600160a01b038216602482015260009081908190819061016790349060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610c25919061109c565b60006040518083038185875af1925050503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60008060006101686001600160a01b03163485604051602401610c8791815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610cbc919061109c565b60006040518083038185875af1925050503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b600060606000604051602001610d2f906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001610d5f9190611070565b60408051601f1981840301815290829052610d799161109c565b600060405180830381855afa9150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b17905290516000918291829161016991610df3919061109c565b6000604051808303816000865af19150503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2505195945050505050565b80356001600160a01b0381168114610e7757600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610ea557600080fd5b610eae83610e60565b9150602083013567ffffffffffffffff80821115610ecb57600080fd5b818501915085601f830112610edf57600080fd5b813581811115610ef157610ef1610e7c565b604051601f8201601f19908116603f01168101908382118183101715610f1957610f19610e7c565b81604052828152886020848701011115610f3257600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b83811015610f6f578181015183820152602001610f57565b83811115610f7e576000848401525b50505050565b60008151808452610f9c816020860160208601610f54565b601f01601f19169290920160200192915050565b8215158152604060208201526000610bc46040830184610f84565b600080600060408486031215610fe057600080fd5b610fe984610e60565b9250602084013567ffffffffffffffff8082111561100657600080fd5b818601915086601f83011261101a57600080fd5b81358181111561102957600080fd5b87602082850101111561103b57600080fd5b6020830194508093505050509250925092565b60006020828403121561106057600080fd5b61106982610e60565b9392505050565b6020815260006110696020830184610f84565b60006020828403121561109557600080fd5b5035919050565b600082516110ae818460208701610f54565b9190910192915050565b600080604083850312156110cb57600080fd5b82518060030b81146110dc57600080fd5b602084015190925080151581146110f257600080fd5b809150509250929050565b60006020828403121561110f57600080fd5b505191905056fea2646970667358221220b774fcfcede95d5d0a658065ae9aba74acf713de4417f59168cf907ed0bbe87764736f6c63430008090033", - "deployedBytecode": "0x6080604052600436106101705760003560e01c80638771074f116100d6578063b51c4f961161007f578063e34c93b811610059578063e34c93b8146103b9578063e717ea61146103cc578063f8b2cb4f146103e757600080fd5b8063b51c4f9614610370578063d558bd8e1461038f578063d83bf9a1146103a457600080fd5b8063b0d869b1116100b0578063b0d869b11461032a578063b2539b2e1461034a578063b2a07c3e1461035d57600080fd5b80638771074f146102ef57806388b0764a1461030f578063915482281461031757600080fd5b806367edbd6d116101385780637e38d3a3116101125780637e38d3a31461029057806381ea4408146102b0578063845b27cd146102cf57600080fd5b806367edbd6d146102465780636f68a38e1461025b57806375ce65191461027b57600080fd5b806315ed14111461017557806319f88c7d146101ac5780631d620e2a146101da5780634e731583146102115780636144ef6d1461023e575b600080fd5b34801561018157600080fd5b50610195610190366004610e92565b61040f565b6040516101a3929190610fb0565b60405180910390f35b3480156101b857600080fd5b506101cc6101c7366004610fcb565b610477565b6040519081526020016101a3565b3480156101e657600080fd5b506101fa6101f536600461104e565b6104a8565b6040805192151583529015156020830152016101a3565b34801561021d57600080fd5b5061023161022c36600461104e565b610587565b6040516101a39190611070565b6101cc6105e3565b34801561025257600080fd5b5061019561068f565b34801561026757600080fd5b506101fa61027636600461104e565b61075d565b34801561028757600080fd5b506101956107ef565b34801561029c57600080fd5b506101956102ab366004610e92565b6108aa565b3480156102bc57600080fd5b506101cc6102cb36600461104e565b3f90565b3480156102db57600080fd5b506101fa6102ea36600461104e565b610903565b3480156102fb57600080fd5b5061019561030a366004610e92565b610997565b6101956109ee565b610195610325366004610e92565b610aac565b34801561033657600080fd5b506101cc610345366004611083565b610b06565b6101fa61035836600461104e565b610bcc565b6101cc61036b366004611083565b610c62565b34801561037c57600080fd5b506101cc61038b36600461104e565b3b90565b34801561039b57600080fd5b50610195610cf9565b3480156103b057600080fd5b506101cc610db4565b6101cc6103c7366004610fcb565b610e30565b3480156103d857600080fd5b506040513081526020016101a3565b3480156103f357600080fd5b506101cc61040236600461104e565b6001600160a01b03163190565b60006060836001600160a01b03168360405161042b919061109c565b600060405180830381855afa9150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b606091505b50909590945092505050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2505195945050505050565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516104ff919061109c565b600060405180830381855afa9150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b606091505b50915091508115610580576000808280602001905181019061056191906110b8565b915060030b91508160070b601614955085801561057b5750805b945050505b5050915091565b6060813b60008167ffffffffffffffff8111156105a6576105a6610e7c565b6040519080825280601f01601f1916602001820160405280156105d0576020820181803683370190505b50905081600060208301863c9392505050565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b1790529051600091829182916101699134916106239161109c565b60006040518083038185875af1925050503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b606091505b50915091508161067457600080fd5b8080602001905181019061068891906110fd565b9250505090565b6000606060006040516020016106c5906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016106f59190611070565b60408051601f198184030181529082905261070f9161109c565b6000604051808303816000865af19150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b606091505b50915091505050509091565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516107b4919061109c565b600060405180830381855af49150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b600060606000604051602001610825906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016108559190611070565b60408051601f198184030181529082905261086f9161109c565b600060405180830381855af49150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b0316836040516108c6919061109c565b6000604051808303816000865af19150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b1790525161095a919061109c565b6000604051808303816000865af19150503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60006060836001600160a01b0316836040516109b3919061109c565b600060405180830381855af49150503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b600060606000604051602001610a24906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b03163484604051602001610a559190611070565b60408051601f1981840301815290829052610a6f9161109c565b60006040518083038185875af1925050503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60006060836001600160a01b03163484604051610ac9919061109c565b60006040518083038185875af1925050503d8060008114610466576040519150601f19603f3d011682016040523d82523d6000602084013e61046b565b60008060006101686001600160a01b031684604051602401610b2a91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610b5f919061109c565b6000604051808303816000865af19150503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b606091505b509150915081610bb057600080fd5b80806020019051810190610bc491906110fd565b949350505050565b6040516001600160a01b038216602482015260009081908190819061016790349060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610c25919061109c565b60006040518083038185875af1925050503d806000811461053a576040519150601f19603f3d011682016040523d82523d6000602084013e61053f565b60008060006101686001600160a01b03163485604051602401610c8791815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610cbc919061109c565b60006040518083038185875af1925050503d8060008114610b9c576040519150601f19603f3d011682016040523d82523d6000602084013e610ba1565b600060606000604051602001610d2f906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001610d5f9190611070565b60408051601f1981840301815290829052610d799161109c565b600060405180830381855afa9150503d806000811461074c576040519150601f19603f3d011682016040523d82523d6000602084013e610751565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b17905290516000918291829161016991610df3919061109c565b6000604051808303816000865af19150503d8060008114610660576040519150601f19603f3d011682016040523d82523d6000602084013e610665565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2505195945050505050565b80356001600160a01b0381168114610e7757600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610ea557600080fd5b610eae83610e60565b9150602083013567ffffffffffffffff80821115610ecb57600080fd5b818501915085601f830112610edf57600080fd5b813581811115610ef157610ef1610e7c565b604051601f8201601f19908116603f01168101908382118183101715610f1957610f19610e7c565b81604052828152886020848701011115610f3257600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b83811015610f6f578181015183820152602001610f57565b83811115610f7e576000848401525b50505050565b60008151808452610f9c816020860160208601610f54565b601f01601f19169290920160200192915050565b8215158152604060208201526000610bc46040830184610f84565b600080600060408486031215610fe057600080fd5b610fe984610e60565b9250602084013567ffffffffffffffff8082111561100657600080fd5b818601915086601f83011261101a57600080fd5b81358181111561102957600080fd5b87602082850101111561103b57600080fd5b6020830194508093505050509250925092565b60006020828403121561106057600080fd5b61106982610e60565b9392505050565b6020815260006110696020830184610f84565b60006020828403121561109557600080fd5b5035919050565b600082516110ae818460208701610f54565b9190910192915050565b600080604083850312156110cb57600080fd5b82518060030b81146110dc57600080fd5b602084015190925080151581146110f257600080fd5b809150509250929050565b60006020828403121561110f57600080fd5b505191905056fea2646970667358221220b774fcfcede95d5d0a658065ae9aba74acf713de4417f59168cf907ed0bbe87764736f6c63430008090033", + "bytecode": "0x608060405234801561001057600080fd5b50611f76806100206000396000f3fe6080604052600436106101665760003560e01c8063845b27cd116100d1578063b2a07c3e1161008a578063d83bf9a111610064578063d83bf9a1146105b9578063e34c93b8146105e4578063e717ea6114610614578063f8b2cb4f1461063f57610166565b8063b2a07c3e14610520578063b51c4f9614610550578063d558bd8e1461058d57610166565b8063845b27cd146103e65780638771074f1461042457806388b0764a146104625780639154822814610481578063b0d869b1146104b2578063b2539b2e146104ef57610166565b80636144ef6d116101235780636144ef6d146102b757806367edbd6d146102d55780636f68a38e1461030157806375ce65191461033f5780637e38d3a31461036b57806381ea4408146103a957610166565b806315ed14111461016b57806319f88c7d146101a95780631d620e2a146101e657806343b82d46146102245780634c3ff83a1461024f5780634e7315831461027a575b600080fd5b34801561017757600080fd5b50610192600480360381019061018d91906119e7565b61067c565b6040516101a0929190611add565b60405180910390f35b3480156101b557600080fd5b506101d060048036038101906101cb9190611b6d565b6106f5565b6040516101dd9190611be6565b60405180910390f35b3480156101f257600080fd5b5061020d60048036038101906102089190611c01565b610729565b60405161021b929190611c2e565b60405180910390f35b34801561023057600080fd5b5061023961086b565b6040516102469190611be6565b60405180910390f35b34801561025b57600080fd5b50610264610983565b6040516102719190611be6565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611c01565b610a9b565b6040516102ae9190611c57565b60405180910390f35b6102bf610b07565b6040516102cc9190611be6565b60405180910390f35b3480156102e157600080fd5b506102ea610c22565b6040516102f8929190611add565b60405180910390f35b34801561030d57600080fd5b5061032860048036038101906103239190611c01565b610cde565b604051610336929190611c2e565b60405180910390f35b34801561034b57600080fd5b50610354610e20565b604051610362929190611add565b60405180910390f35b34801561037757600080fd5b50610392600480360381019061038d91906119e7565b610eda565b6040516103a0929190611add565b60405180910390f35b3480156103b557600080fd5b506103d060048036038101906103cb9190611c01565b610f55565b6040516103dd9190611be6565b60405180910390f35b3480156103f257600080fd5b5061040d60048036038101906104089190611c01565b610f65565b60405161041b929190611c2e565b60405180910390f35b34801561043057600080fd5b5061044b600480360381019061044691906119e7565b6110a9565b604051610459929190611add565b60405180910390f35b61046a611122565b604051610478929190611add565b60405180910390f35b61049b600480360381019061049691906119e7565b6111df565b6040516104a9929190611add565b60405180910390f35b3480156104be57600080fd5b506104d960048036038101906104d49190611caf565b61125b565b6040516104e69190611ceb565b60405180910390f35b61050960048036038101906105049190611c01565b611382565b604051610517929190611c2e565b60405180910390f35b61053a60048036038101906105359190611caf565b6114c7565b6040516105479190611ceb565b60405180910390f35b34801561055c57600080fd5b5061057760048036038101906105729190611c01565b6115ef565b6040516105849190611ceb565b60405180910390f35b34801561059957600080fd5b506105a26115ff565b6040516105b0929190611add565b60405180910390f35b3480156105c557600080fd5b506105ce6116b9565b6040516105db9190611be6565b60405180910390f35b6105fe60048036038101906105f99190611b6d565b6117d3565b60405161060b9190611be6565b60405180910390f35b34801561062057600080fd5b50610629611806565b6040516106369190611d15565b60405180910390f35b34801561064b57600080fd5b5061066660048036038101906106619190611c01565b61180e565b6040516106739190611ceb565b60405180910390f35b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516106a59190611d6c565b600060405180830381855afa9150503d80600081146106e0576040519150601f19603f3d011682016040523d82523d6000602084013e6106e5565b606091505b5080925081935050509250929050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2815193505050509392505050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff16856040516024016107599190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516107e39190611d6c565b600060405180830381855afa9150503d806000811461081e576040519150601f19603f3d011682016040523d82523d6000602084013e610823565b606091505b5091509150811561086457600080828060200190518101906108459190611de8565b915060030b915060168260070b14955085801561085f5750805b945050505b5050915091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516109199190611d6c565b600060405180830381855af49150503d8060008114610954576040519150601f19603f3d011682016040523d82523d6000602084013e610959565b606091505b50915091508161096857600080fd5b8080602001905181019061097c9190611e54565b9250505090565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610a319190611d6c565b600060405180830381855afa9150503d8060008114610a6c576040519150601f19603f3d011682016040523d82523d6000602084013e610a71565b606091505b509150915081610a8057600080fd5b80806020019051810190610a949190611e54565b9250505090565b60606000823b905060008167ffffffffffffffff811115610abf57610abe6118bc565b5b6040519080825280601f01601f191660200182016040528015610af15781602001600182028036833780820191505090505b50905081600060208301863c8092505050919050565b600080600061016973ffffffffffffffffffffffffffffffffffffffff16346040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610bb69190611d6c565b60006040518083038185875af1925050503d8060008114610bf3576040519150601f19603f3d011682016040523d82523d6000602084013e610bf8565b606091505b509150915081610c0757600080fd5b80806020019051810190610c1b9190611e54565b9250505090565b600060606000604051602001610c3790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610c749190611c57565b604051602081830303815290604052604051610c909190611d6c565b6000604051808303816000865af19150503d8060008114610ccd576040519150601f19603f3d011682016040523d82523d6000602084013e610cd2565b606091505b50915091505050509091565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610d0e9190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610d989190611d6c565b600060405180830381855af49150503d8060008114610dd3576040519150601f19603f3d011682016040523d82523d6000602084013e610dd8565b606091505b50915091508115610e195760008082806020019051810190610dfa9190611de8565b915060030b915060168260070b149550858015610e145750805b945050505b5050915091565b600060606000604051602001610e3590611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610e729190611c57565b604051602081830303815290604052604051610e8e9190611d6c565b600060405180830381855af49150503d8060008114610ec9576040519150601f19603f3d011682016040523d82523d6000602084013e610ece565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1683604051610f039190611d6c565b6000604051808303816000865af19150503d8060008114610f40576040519150601f19603f3d011682016040523d82523d6000602084013e610f45565b606091505b5080925081935050509250929050565b600080823f905080915050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610f959190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161101f9190611d6c565b6000604051808303816000865af19150503d806000811461105c576040519150601f19603f3d011682016040523d82523d6000602084013e611061565b606091505b509150915081156110a257600080828060200190518101906110839190611de8565b915060030b915060168260070b14955085801561109d5750805b945050505b5050915091565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516110d29190611d6c565b600060405180830381855af49150503d806000811461110d576040519150601f19603f3d011682016040523d82523d6000602084013e611112565b606091505b5080925081935050509250929050565b60006060600060405160200161113790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1634846040516020016111759190611c57565b6040516020818303038152906040526040516111919190611d6c565b60006040518083038185875af1925050503d80600081146111ce576040519150601f19603f3d011682016040523d82523d6000602084013e6111d3565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1634846040516112099190611d6c565b60006040518083038185875af1925050503d8060008114611246576040519150601f19603f3d011682016040523d82523d6000602084013e61124b565b606091505b5080925081935050509250929050565b600080600061016873ffffffffffffffffffffffffffffffffffffffff168460405160240161128a9190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516113149190611d6c565b6000604051808303816000865af19150503d8060008114611351576040519150601f19603f3d011682016040523d82523d6000602084013e611356565b606091505b50915091508161136557600080fd5b808060200190518101906113799190611f13565b92505050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1634866040516024016113b39190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161143d9190611d6c565b60006040518083038185875af1925050503d806000811461147a576040519150601f19603f3d011682016040523d82523d6000602084013e61147f565b606091505b509150915081156114c057600080828060200190518101906114a19190611de8565b915060030b915060168260070b1495508580156114bb5750805b945050505b5050915091565b600080600061016873ffffffffffffffffffffffffffffffffffffffff1634856040516024016114f79190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516115819190611d6c565b60006040518083038185875af1925050503d80600081146115be576040519150601f19603f3d011682016040523d82523d6000602084013e6115c3565b606091505b5091509150816115d257600080fd5b808060200190518101906115e69190611f13565b92505050919050565b600080823b905080915050919050565b60006060600060405160200161161490611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff16836040516020016116519190611c57565b60405160208183030381529060405260405161166d9190611d6c565b600060405180830381855afa9150503d80600081146116a8576040519150601f19603f3d011682016040523d82523d6000602084013e6116ad565b606091505b50915091505050509091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516117679190611d6c565b6000604051808303816000865af19150503d80600081146117a4576040519150601f19603f3d011682016040523d82523d6000602084013e6117a9565b606091505b5091509150816117b857600080fd5b808060200190518101906117cc9190611e54565b9250505090565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2815193505050509392505050565b600030905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061186e82611843565b9050919050565b61187e81611863565b811461188957600080fd5b50565b60008135905061189b81611875565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6118f4826118ab565b810181811067ffffffffffffffff82111715611913576119126118bc565b5b80604052505050565b600061192661182f565b905061193282826118eb565b919050565b600067ffffffffffffffff821115611952576119516118bc565b5b61195b826118ab565b9050602081019050919050565b82818337600083830152505050565b600061198a61198584611937565b61191c565b9050828152602081018484840111156119a6576119a56118a6565b5b6119b1848285611968565b509392505050565b600082601f8301126119ce576119cd6118a1565b5b81356119de848260208601611977565b91505092915050565b600080604083850312156119fe576119fd611839565b5b6000611a0c8582860161188c565b925050602083013567ffffffffffffffff811115611a2d57611a2c61183e565b5b611a39858286016119b9565b9150509250929050565b60008115159050919050565b611a5881611a43565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611a98578082015181840152602081019050611a7d565b60008484015250505050565b6000611aaf82611a5e565b611ab98185611a69565b9350611ac9818560208601611a7a565b611ad2816118ab565b840191505092915050565b6000604082019050611af26000830185611a4f565b8181036020830152611b048184611aa4565b90509392505050565b600080fd5b600080fd5b60008083601f840112611b2d57611b2c6118a1565b5b8235905067ffffffffffffffff811115611b4a57611b49611b0d565b5b602083019150836001820283011115611b6657611b65611b12565b5b9250929050565b600080600060408486031215611b8657611b85611839565b5b6000611b948682870161188c565b935050602084013567ffffffffffffffff811115611bb557611bb461183e565b5b611bc186828701611b17565b92509250509250925092565b6000819050919050565b611be081611bcd565b82525050565b6000602082019050611bfb6000830184611bd7565b92915050565b600060208284031215611c1757611c16611839565b5b6000611c258482850161188c565b91505092915050565b6000604082019050611c436000830185611a4f565b611c506020830184611a4f565b9392505050565b60006020820190508181036000830152611c718184611aa4565b905092915050565b6000819050919050565b611c8c81611c79565b8114611c9757600080fd5b50565b600081359050611ca981611c83565b92915050565b600060208284031215611cc557611cc4611839565b5b6000611cd384828501611c9a565b91505092915050565b611ce581611c79565b82525050565b6000602082019050611d006000830184611cdc565b92915050565b611d0f81611863565b82525050565b6000602082019050611d2a6000830184611d06565b92915050565b600081905092915050565b6000611d4682611a5e565b611d508185611d30565b9350611d60818560208601611a7a565b80840191505092915050565b6000611d788284611d3b565b915081905092915050565b60008160030b9050919050565b611d9981611d83565b8114611da457600080fd5b50565b600081519050611db681611d90565b92915050565b611dc581611a43565b8114611dd057600080fd5b50565b600081519050611de281611dbc565b92915050565b60008060408385031215611dff57611dfe611839565b5b6000611e0d85828601611da7565b9250506020611e1e85828601611dd3565b9150509250929050565b611e3181611bcd565b8114611e3c57600080fd5b50565b600081519050611e4e81611e28565b92915050565b600060208284031215611e6a57611e69611839565b5b6000611e7884828501611e3f565b91505092915050565b600082825260208201905092915050565b7f48656c6c6f2c20576f726c640000000000000000000000000000000000000000600082015250565b6000611ec8600c83611e81565b9150611ed382611e92565b602082019050919050565b60006020820190508181036000830152611ef781611ebb565b9050919050565b600081519050611f0d81611c83565b92915050565b600060208284031215611f2957611f28611839565b5b6000611f3784828501611efe565b9150509291505056fea26469706673582212206bda6fdee0213387de2ad256148f515e0616c170d9d76630f24a35e4833729b364736f6c63430008130033", + "deployedBytecode": "0x6080604052600436106101665760003560e01c8063845b27cd116100d1578063b2a07c3e1161008a578063d83bf9a111610064578063d83bf9a1146105b9578063e34c93b8146105e4578063e717ea6114610614578063f8b2cb4f1461063f57610166565b8063b2a07c3e14610520578063b51c4f9614610550578063d558bd8e1461058d57610166565b8063845b27cd146103e65780638771074f1461042457806388b0764a146104625780639154822814610481578063b0d869b1146104b2578063b2539b2e146104ef57610166565b80636144ef6d116101235780636144ef6d146102b757806367edbd6d146102d55780636f68a38e1461030157806375ce65191461033f5780637e38d3a31461036b57806381ea4408146103a957610166565b806315ed14111461016b57806319f88c7d146101a95780631d620e2a146101e657806343b82d46146102245780634c3ff83a1461024f5780634e7315831461027a575b600080fd5b34801561017757600080fd5b50610192600480360381019061018d91906119e7565b61067c565b6040516101a0929190611add565b60405180910390f35b3480156101b557600080fd5b506101d060048036038101906101cb9190611b6d565b6106f5565b6040516101dd9190611be6565b60405180910390f35b3480156101f257600080fd5b5061020d60048036038101906102089190611c01565b610729565b60405161021b929190611c2e565b60405180910390f35b34801561023057600080fd5b5061023961086b565b6040516102469190611be6565b60405180910390f35b34801561025b57600080fd5b50610264610983565b6040516102719190611be6565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611c01565b610a9b565b6040516102ae9190611c57565b60405180910390f35b6102bf610b07565b6040516102cc9190611be6565b60405180910390f35b3480156102e157600080fd5b506102ea610c22565b6040516102f8929190611add565b60405180910390f35b34801561030d57600080fd5b5061032860048036038101906103239190611c01565b610cde565b604051610336929190611c2e565b60405180910390f35b34801561034b57600080fd5b50610354610e20565b604051610362929190611add565b60405180910390f35b34801561037757600080fd5b50610392600480360381019061038d91906119e7565b610eda565b6040516103a0929190611add565b60405180910390f35b3480156103b557600080fd5b506103d060048036038101906103cb9190611c01565b610f55565b6040516103dd9190611be6565b60405180910390f35b3480156103f257600080fd5b5061040d60048036038101906104089190611c01565b610f65565b60405161041b929190611c2e565b60405180910390f35b34801561043057600080fd5b5061044b600480360381019061044691906119e7565b6110a9565b604051610459929190611add565b60405180910390f35b61046a611122565b604051610478929190611add565b60405180910390f35b61049b600480360381019061049691906119e7565b6111df565b6040516104a9929190611add565b60405180910390f35b3480156104be57600080fd5b506104d960048036038101906104d49190611caf565b61125b565b6040516104e69190611ceb565b60405180910390f35b61050960048036038101906105049190611c01565b611382565b604051610517929190611c2e565b60405180910390f35b61053a60048036038101906105359190611caf565b6114c7565b6040516105479190611ceb565b60405180910390f35b34801561055c57600080fd5b5061057760048036038101906105729190611c01565b6115ef565b6040516105849190611ceb565b60405180910390f35b34801561059957600080fd5b506105a26115ff565b6040516105b0929190611add565b60405180910390f35b3480156105c557600080fd5b506105ce6116b9565b6040516105db9190611be6565b60405180910390f35b6105fe60048036038101906105f99190611b6d565b6117d3565b60405161060b9190611be6565b60405180910390f35b34801561062057600080fd5b50610629611806565b6040516106369190611d15565b60405180910390f35b34801561064b57600080fd5b5061066660048036038101906106619190611c01565b61180e565b6040516106739190611ceb565b60405180910390f35b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516106a59190611d6c565b600060405180830381855afa9150503d80600081146106e0576040519150601f19603f3d011682016040523d82523d6000602084013e6106e5565b606091505b5080925081935050509250929050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2815193505050509392505050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff16856040516024016107599190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516107e39190611d6c565b600060405180830381855afa9150503d806000811461081e576040519150601f19603f3d011682016040523d82523d6000602084013e610823565b606091505b5091509150811561086457600080828060200190518101906108459190611de8565b915060030b915060168260070b14955085801561085f5750805b945050505b5050915091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516109199190611d6c565b600060405180830381855af49150503d8060008114610954576040519150601f19603f3d011682016040523d82523d6000602084013e610959565b606091505b50915091508161096857600080fd5b8080602001905181019061097c9190611e54565b9250505090565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610a319190611d6c565b600060405180830381855afa9150503d8060008114610a6c576040519150601f19603f3d011682016040523d82523d6000602084013e610a71565b606091505b509150915081610a8057600080fd5b80806020019051810190610a949190611e54565b9250505090565b60606000823b905060008167ffffffffffffffff811115610abf57610abe6118bc565b5b6040519080825280601f01601f191660200182016040528015610af15781602001600182028036833780820191505090505b50905081600060208301863c8092505050919050565b600080600061016973ffffffffffffffffffffffffffffffffffffffff16346040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610bb69190611d6c565b60006040518083038185875af1925050503d8060008114610bf3576040519150601f19603f3d011682016040523d82523d6000602084013e610bf8565b606091505b509150915081610c0757600080fd5b80806020019051810190610c1b9190611e54565b9250505090565b600060606000604051602001610c3790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610c749190611c57565b604051602081830303815290604052604051610c909190611d6c565b6000604051808303816000865af19150503d8060008114610ccd576040519150601f19603f3d011682016040523d82523d6000602084013e610cd2565b606091505b50915091505050509091565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610d0e9190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610d989190611d6c565b600060405180830381855af49150503d8060008114610dd3576040519150601f19603f3d011682016040523d82523d6000602084013e610dd8565b606091505b50915091508115610e195760008082806020019051810190610dfa9190611de8565b915060030b915060168260070b149550858015610e145750805b945050505b5050915091565b600060606000604051602001610e3590611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610e729190611c57565b604051602081830303815290604052604051610e8e9190611d6c565b600060405180830381855af49150503d8060008114610ec9576040519150601f19603f3d011682016040523d82523d6000602084013e610ece565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1683604051610f039190611d6c565b6000604051808303816000865af19150503d8060008114610f40576040519150601f19603f3d011682016040523d82523d6000602084013e610f45565b606091505b5080925081935050509250929050565b600080823f905080915050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610f959190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161101f9190611d6c565b6000604051808303816000865af19150503d806000811461105c576040519150601f19603f3d011682016040523d82523d6000602084013e611061565b606091505b509150915081156110a257600080828060200190518101906110839190611de8565b915060030b915060168260070b14955085801561109d5750805b945050505b5050915091565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516110d29190611d6c565b600060405180830381855af49150503d806000811461110d576040519150601f19603f3d011682016040523d82523d6000602084013e611112565b606091505b5080925081935050509250929050565b60006060600060405160200161113790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1634846040516020016111759190611c57565b6040516020818303038152906040526040516111919190611d6c565b60006040518083038185875af1925050503d80600081146111ce576040519150601f19603f3d011682016040523d82523d6000602084013e6111d3565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1634846040516112099190611d6c565b60006040518083038185875af1925050503d8060008114611246576040519150601f19603f3d011682016040523d82523d6000602084013e61124b565b606091505b5080925081935050509250929050565b600080600061016873ffffffffffffffffffffffffffffffffffffffff168460405160240161128a9190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516113149190611d6c565b6000604051808303816000865af19150503d8060008114611351576040519150601f19603f3d011682016040523d82523d6000602084013e611356565b606091505b50915091508161136557600080fd5b808060200190518101906113799190611f13565b92505050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1634866040516024016113b39190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161143d9190611d6c565b60006040518083038185875af1925050503d806000811461147a576040519150601f19603f3d011682016040523d82523d6000602084013e61147f565b606091505b509150915081156114c057600080828060200190518101906114a19190611de8565b915060030b915060168260070b1495508580156114bb5750805b945050505b5050915091565b600080600061016873ffffffffffffffffffffffffffffffffffffffff1634856040516024016114f79190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516115819190611d6c565b60006040518083038185875af1925050503d80600081146115be576040519150601f19603f3d011682016040523d82523d6000602084013e6115c3565b606091505b5091509150816115d257600080fd5b808060200190518101906115e69190611f13565b92505050919050565b600080823b905080915050919050565b60006060600060405160200161161490611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff16836040516020016116519190611c57565b60405160208183030381529060405260405161166d9190611d6c565b600060405180830381855afa9150503d80600081146116a8576040519150601f19603f3d011682016040523d82523d6000602084013e6116ad565b606091505b50915091505050509091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516117679190611d6c565b6000604051808303816000865af19150503d80600081146117a4576040519150601f19603f3d011682016040523d82523d6000602084013e6117a9565b606091505b5091509150816117b857600080fd5b808060200190518101906117cc9190611e54565b9250505090565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2815193505050509392505050565b600030905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061186e82611843565b9050919050565b61187e81611863565b811461188957600080fd5b50565b60008135905061189b81611875565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6118f4826118ab565b810181811067ffffffffffffffff82111715611913576119126118bc565b5b80604052505050565b600061192661182f565b905061193282826118eb565b919050565b600067ffffffffffffffff821115611952576119516118bc565b5b61195b826118ab565b9050602081019050919050565b82818337600083830152505050565b600061198a61198584611937565b61191c565b9050828152602081018484840111156119a6576119a56118a6565b5b6119b1848285611968565b509392505050565b600082601f8301126119ce576119cd6118a1565b5b81356119de848260208601611977565b91505092915050565b600080604083850312156119fe576119fd611839565b5b6000611a0c8582860161188c565b925050602083013567ffffffffffffffff811115611a2d57611a2c61183e565b5b611a39858286016119b9565b9150509250929050565b60008115159050919050565b611a5881611a43565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611a98578082015181840152602081019050611a7d565b60008484015250505050565b6000611aaf82611a5e565b611ab98185611a69565b9350611ac9818560208601611a7a565b611ad2816118ab565b840191505092915050565b6000604082019050611af26000830185611a4f565b8181036020830152611b048184611aa4565b90509392505050565b600080fd5b600080fd5b60008083601f840112611b2d57611b2c6118a1565b5b8235905067ffffffffffffffff811115611b4a57611b49611b0d565b5b602083019150836001820283011115611b6657611b65611b12565b5b9250929050565b600080600060408486031215611b8657611b85611839565b5b6000611b948682870161188c565b935050602084013567ffffffffffffffff811115611bb557611bb461183e565b5b611bc186828701611b17565b92509250509250925092565b6000819050919050565b611be081611bcd565b82525050565b6000602082019050611bfb6000830184611bd7565b92915050565b600060208284031215611c1757611c16611839565b5b6000611c258482850161188c565b91505092915050565b6000604082019050611c436000830185611a4f565b611c506020830184611a4f565b9392505050565b60006020820190508181036000830152611c718184611aa4565b905092915050565b6000819050919050565b611c8c81611c79565b8114611c9757600080fd5b50565b600081359050611ca981611c83565b92915050565b600060208284031215611cc557611cc4611839565b5b6000611cd384828501611c9a565b91505092915050565b611ce581611c79565b82525050565b6000602082019050611d006000830184611cdc565b92915050565b611d0f81611863565b82525050565b6000602082019050611d2a6000830184611d06565b92915050565b600081905092915050565b6000611d4682611a5e565b611d508185611d30565b9350611d60818560208601611a7a565b80840191505092915050565b6000611d788284611d3b565b915081905092915050565b60008160030b9050919050565b611d9981611d83565b8114611da457600080fd5b50565b600081519050611db681611d90565b92915050565b611dc581611a43565b8114611dd057600080fd5b50565b600081519050611de281611dbc565b92915050565b60008060408385031215611dff57611dfe611839565b5b6000611e0d85828601611da7565b9250506020611e1e85828601611dd3565b9150509250929050565b611e3181611bcd565b8114611e3c57600080fd5b50565b600081519050611e4e81611e28565b92915050565b600060208284031215611e6a57611e69611839565b5b6000611e7884828501611e3f565b91505092915050565b600082825260208201905092915050565b7f48656c6c6f2c20576f726c640000000000000000000000000000000000000000600082015250565b6000611ec8600c83611e81565b9150611ed382611e92565b602082019050919050565b60006020820190508181036000830152611ef781611ebb565b9050919050565b600081519050611f0d81611c83565b92915050565b600060208284031215611f2957611f28611839565b5b6000611f3784828501611efe565b9150509291505056fea26469706673582212206bda6fdee0213387de2ad256148f515e0616c170d9d76630f24a35e4833729b364736f6c63430008130033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/packages/server/tests/contracts/EquivalenceContract.sol b/packages/server/tests/contracts/EquivalenceContract.sol index eabd7b685e..06dbe5604c 100644 --- a/packages/server/tests/contracts/EquivalenceContract.sol +++ b/packages/server/tests/contracts/EquivalenceContract.sol @@ -115,6 +115,20 @@ contract EquivalenceContract { randomBytes = abi.decode(result, (bytes32)); } + function getPseudorandomSeedStaticCall() external returns (bytes32 randomBytes) { + (bool success, bytes memory result) = PRNG_PRECOMPILE_ADDRESS.staticcall( + abi.encodeWithSignature("getPseudorandomSeed()")); + require(success); + randomBytes = abi.decode(result, (bytes32)); + } + + function getPseudorandomSeedDelegateCall() external returns (bytes32 randomBytes) { + (bool success, bytes memory result) = PRNG_PRECOMPILE_ADDRESS.delegatecall( + abi.encodeWithSignature("getPseudorandomSeed()")); + require(success); + randomBytes = abi.decode(result, (bytes32)); + } + function exchangeRateWithoutAmount(uint256 tinycents) external returns (uint256 tinybars) { (bool success, bytes memory result) = EXCHANGE_RATE_PRECOMPILE_ADDRESS.call( abi.encodeWithSignature("tinycentsToTinybars(uint256)", tinycents)); From 00c0f93b592b9a2417d455f44f9c3248df57672e Mon Sep 17 00:00:00 2001 From: emilevgenievgeorgiev Date: Thu, 30 May 2024 17:36:13 +0300 Subject: [PATCH 09/14] Added and updated equivalence tests for internal calls Signed-off-by: emilevgenievgeorgiev --- .../tests/acceptance/equivalence.spec.ts | 1769 ++++------------- .../tests/contracts/EquivalenceContract.json | 42 +- .../tests/contracts/EquivalenceContract.sol | 14 + 3 files changed, 477 insertions(+), 1348 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 846d9d8d0c..eb8d3c5083 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -18,11 +18,10 @@ * */ -import { expect } from 'chai'; +import { assert, expect } from 'chai'; import { Utils } from '../helpers/utils'; import ServicesClient, { AliasAccount } from '../clients/servicesClient'; -import { ContractFunctionParameters, ContractFunctionSelector } from '@hashgraph/sdk'; -import RelayAssertions from '../../../relay/tests/assertions'; +import { ContractFunctionParameters } from '@hashgraph/sdk'; import EstimatePrecompileContractJson from '../contracts/EstimatePrecompileContract.json'; import Constants from '../helpers/constants'; import EquivalenceContractJson from '../contracts/EquivalenceContract.json'; @@ -30,26 +29,23 @@ import { ethers, toUtf8Bytes } from 'ethers'; import { Precheck } from '../../../relay/src/lib/precheck'; import pino from 'pino'; import { MirrorNodeClient } from '../../../relay/src/lib/clients'; -import { CacheService } from '../../../relay/src/lib/services/cacheService/cacheService'; import EquivalenceDestructContractJson from '../contracts/EquivalenceDestruct.json'; -import ERC20MockJson from '../contracts/ERC20Mock.json'; -import { AccountId, Hbar, ContractId } from '@hashgraph/sdk'; -import TokenManagementContractJson from '../contracts/TokenManagementContract.json'; import RelayClient from '../clients/relayClient'; -import { - decodeErrorMessage, - hexToASCII, - prepend0x, - toHexString, - trimPrecedingZeros, -} from '../../../relay/src/formatters'; -import MirrorClient from '../clients/mirrorClient'; +import { hexToASCII } from '../../../relay/src/formatters'; const logger = pino(); const removeLeading0x = (input: string) => { return input.startsWith('0x') ? input.replace('0x', '') : input; }; +const removeLeadingZeros = (input) => { + let result = input; + while (result[0] === '0') { + result = result.substring(1); + } + return result; +}; + const decodeResultData = (input: string) => { return hexToASCII(removeLeading0x(input)); }; @@ -62,11 +58,10 @@ async function testRejection(errorMessage, method, checkMessage, thisObj, args?) describe.only('Equivalence tests', async function () { const signers: AliasAccount[] = []; - // const { servicesNode, mirrorNode, relay }: {servicesNode: ServicesClient; mirrorNode: MirrorNodeClient; relay: RelayClient;} = global; const { servicesNode, mirrorNode, relay }: any = global; const servicesClient = servicesNode as ServicesClient; - const mirrorClient = mirrorNode as MirrorClient; - const relayClient = relay as RelayClient; + const mirrorNodeClient = mirrorNode as MirrorNodeClient; + let precheck: Precheck; const SUCCESS = 'SUCCESS'; const STATUS_SUCCESS = '0x1'; @@ -77,36 +72,29 @@ describe.only('Equivalence tests', async function () { const prefix = '0x'; const ETH_PRECOMPILE_0x1 = '0.0.1'; const ETH_PRECOMPILE_0x361 = '0.0.361'; - const ETH_PRECOMPILE_0x751 = '0.0.751'; const ADDRESS_0x800 = '0.0.800'; const ETH_PRECOMPILE_0x1001 = '0.0.1001'; const NON_EXISTING_CONTRACT_ID = '0.0.564400'; const NON_EXISTING_FUNCTION = 'nxxixxkxxi'; const EMPTY_FUNCTION_PARAMS = new ContractFunctionParameters(); + const EMPTY_UINT8_ARRAY = new Uint8Array(0); const ADDRESS_0_0_0 = '0.0.0'; const ADDRESS_0_0_1 = '0.0.1'; const ADDRESS_0_0_2 = '0.0.2'; const ADDRESS_0_0_3 = '0.0.3'; const ADDRESS_0_0_4 = '0.0.4'; - const ADDRESS_0_0_100 = '0.0.100'; - const ADDRESS_0_0_358 = '0.0.358'; const ADDRESS_0_0_359 = '0.0.359'; const ADDRESS_0_0_360 = '0.0.360'; + const ADDRESS_0_0_361 = '0.0.361'; const MAKE_CALL_WITHOUT_AMOUNT = 'makeCallWithoutAmount'; const MAKE_CALL_WITH_AMOUNT = 'makeCallWithAmount'; - const MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithoutAmountToIdentityPrecompile'; - const MAKE_CALL_WITH_AMOUNT_TO_IDENTITY_PRECOMPILE = 'makeCallWithAmountToIdentityPrecompile'; const MAKE_HTS_CALL_WITHOUT_AMOUNT = 'htsCallWithoutAmount'; const MAKE_HTS_CALL_WITH_AMOUNT = 'htsCallWithAmount'; - const MAKE_STATICCALL = 'makeStaticCall'; - const MAKE_HTS_STATICCALL = 'htsStaticCall'; - const MAKE_DELEGATECALL = 'makeDelegateCall'; const accounts: AliasAccount[] = []; let tokenAddress; let estimatePrecompileContractReceipt; let estimatePrecompileContractAddress; let estimatePrecompileSolidityAddress; - let HederaAddress; let equivalenceContractReceipt; let equivalenceContractId; let equivalenceContract; @@ -114,9 +102,17 @@ describe.only('Equivalence tests', async function () { let equivalenceDestructContractId; let equivalenceDestructContractReceipt; let estimateContract; - let TokenManager; let requestId; + const expectSuccessfulContractCall = (record) => { + expect(record.contract_id).to.equal( + equivalenceContractId, + 'Contract Id from record did not match with equivalence contract Id.', + ); + expect(record.result).to.equal(SUCCESS, 'Result from record was not as expected.'); + expect(record.status).to.equal(STATUS_SUCCESS, 'Status from record was not as expected'); + }; + before(async function () { signers[0] = await servicesNode.createAliasAccount(150, relay.provider, Utils.generateRequestId()); @@ -154,7 +150,7 @@ describe.only('Equivalence tests', async function () { console.log(`equivalenceContract:\n${JSON.stringify(equivalenceContract, null, 2)}`); requestId = Utils.generateRequestId(); - const contractMirror = await mirrorClient.get(`/contracts/${estimatePrecompileSolidityAddress}`, requestId); + const contractMirror = await mirrorNodeClient.get(`/contracts/${estimatePrecompileSolidityAddress}`, requestId); accounts[0] = await servicesClient.createAccountWithContractIdKey( contractMirror.contract_id, @@ -165,8 +161,168 @@ describe.only('Equivalence tests', async function () { tokenAddress = await createFungibleToken(); console.log(`tokenAddress: ${tokenAddress}`); + precheck = new Precheck(mirrorNodeClient, logger, '0x12a'); }); + enum Opcodes { + Call = 'Call', + StaticCall = 'StaticCall', + DelegateCall = 'DelegateCall', + CallCode = 'CallCode', + } + + enum Outcomes { + Output = 'OUTPUT', + Error = 'ERROR', + } + + const getTestSummaryAmount = (amount: number): string => { + return amount === 0 ? 'without' : 'with'; + }; + + const getTestSummaryOutcome = (outcome: Outcomes, hederaAddress: string, errorMessage?: string): string => { + let addressName: string; + switch (hederaAddress) { + case ADDRESS_0_0_1: { + addressName = `ecrecover precompile`; + break; + } + case ADDRESS_0_0_2: { + addressName = `SHA-256 precompile`; + break; + } + case ADDRESS_0_0_3: { + addressName = `RIPEMD-160 precompile`; + break; + } + case ADDRESS_0_0_4: { + addressName = `identity precompile`; + break; + } + case ADDRESS_0_0_359: { + addressName = `HTS precompile`; + break; + } + case ADDRESS_0_0_360: { + addressName = `ExchangeRate precompile`; + break; + } + case ADDRESS_0_0_361: { + addressName = `PRNG precompile`; + break; + } + // no need to identify unnamed addresses + default: { + addressName = ''; + } + } + + switch (outcome) { + case Outcomes.Error: { + return `should fail ${addressName ? `executing ${addressName}` : ''}${ + errorMessage ? ` with ${errorMessage}` : '' + }`; + } + case Outcomes.Output: { + return addressName ? `should execute the ${addressName}` : 'should succeed'; + } + default: { + assert.fail(`Unsupported outcome: ${outcome} type provided in test data`); + } + } + }; + + const getFunctionName = (opcode: Opcodes, amount: number, hederaAddress: string): string => { + let functionName = ''; + const isWithAmount = amount > 0; + switch (hederaAddress) { + case ADDRESS_0_0_359: { + switch (opcode) { + case Opcodes.Call: { + functionName = isWithAmount ? MAKE_HTS_CALL_WITH_AMOUNT : MAKE_HTS_CALL_WITHOUT_AMOUNT; + break; + } + case Opcodes.StaticCall: + case Opcodes.DelegateCall: { + functionName = `hts${opcode}`; + break; + } + } + break; + } + case ADDRESS_0_0_360: { + switch (opcode) { + case Opcodes.Call: { + functionName = isWithAmount ? 'exchangeRateWithAmount' : 'exchangeRateWithoutAmount'; + break; + } + case Opcodes.StaticCall: + case Opcodes.DelegateCall: { + functionName = `exchangeRate${opcode}`; + break; + } + } + break; + } + case ADDRESS_0_0_361: { + switch (opcode) { + case Opcodes.Call: { + functionName = isWithAmount ? 'getPseudorandomSeedWithAmount' : 'getPseudorandomSeed'; + break; + } + case Opcodes.StaticCall: + case Opcodes.DelegateCall: { + functionName = `getPseudorandomSeed${opcode}`; + break; + } + } + break; + } + default: { + switch (opcode) { + case Opcodes.Call: { + functionName = isWithAmount ? MAKE_CALL_WITH_AMOUNT : MAKE_CALL_WITHOUT_AMOUNT; + break; + } + case Opcodes.StaticCall: + case Opcodes.DelegateCall: { + functionName = `make${opcode}`; + break; + } + } + } + } + if (!functionName) { + assert.fail('function name not found'); + } + + return functionName; + }; + + const getContractFunctionParams = (hederaAddress: string): ContractFunctionParameters => { + let params: ContractFunctionParameters; + switch (hederaAddress) { + case '0.0.359': { + params = new ContractFunctionParameters().addAddress(tokenAddress); + break; + } + case '0.0.360': { + params = new ContractFunctionParameters().addUint256(100); + break; + } + case '0.0.361': { + params = EMPTY_FUNCTION_PARAMS; + break; + } + default: { + params = new ContractFunctionParameters() + .addAddress(Utils.idToEvmAddress(hederaAddress)) + .addBytes(EMPTY_UINT8_ARRAY); + } + } + return params; + }; + async function getResultByEntityIdAndTxTimestamp(entityId, txTimestamp) { return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); } @@ -177,7 +333,7 @@ describe.only('Equivalence tests', async function () { * @returns list of ContractActions */ async function getContractActions(transactionIdOrHash: string) { - return await mirrorClient.get(`/contracts/results/${transactionIdOrHash}/actions`); + return await mirrorNodeClient.get(`/contracts/results/${transactionIdOrHash}/actions`, Utils.generateRequestId()); } async function createFungibleToken() { @@ -361,1387 +517,308 @@ describe.only('Equivalence tests', async function () { expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); }); - //----------Internal calls---------------- - // EQUIVALENCE-014 - Same as mirror node - fails with PRECOMPILE_ERROR it should be something else - it('internal CALL to 0.0.0 without amount - should be successfull', async function () { - const emptyByteArray = new Uint8Array(0); - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); - const params = new ContractFunctionParameters().addAddress(evmAddress); - params.addBytes(emptyByteArray); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - /** - * '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"CALL" - "caller":"0.0.1045" - "caller_type":"CONTRACT" - "from":"0x0000000000000000000000000000000000000415" - "gas":466906 - "gas_used":466906 - "index":1 - "input":"0x" - "recipient":null - "recipient_type":null - "result_data":"0x505245434f4d50494c455f4552524f52" - "result_data_type":"ERROR" - "timestamp":"1715842544.840853219" - "to":"0x0000000000000000000000000000000000000000" - "value":0}' - */ - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - }); - - // EQUIVALENCE-015 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED - it('internal CALL to 0.0.0 with amount should fail with INVALID_FEE_SUBMITTED, as the intermediary contract should not transfer funds to 0x0.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( - equivalenceContractId, - MAKE_CALL_WITH_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 1_000_000, - 100, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); - }); - - // EQUIVALENCE-016 - it('internal CALL to 0.0.1 without amount - should be successfull (Expected to execute the ecrecover precompile through the intermediary contract.)', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('OUTPUT'); - /* '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"PRECOMPILE" - "caller":"0.0.1122" - "caller_type":"CONTRACT" - "from":"0x0000000000000000000000000000000000000462" - "gas":469355 - "gas_used":3000 - "index":1 - "input":"0x" - "recipient":"0.0.1" - "recipient_type":"CONTRACT" - "result_data":"0x" - "result_data_type":"OUTPUT" - "timestamp":"1715845647.860197419" - "to":"0x0000000000000000000000000000000000000001" - "value":0}' - */ - }); - - // EQUIVALENCE-017 - it('internal CALL to 0.0.2 without amount should execute the SHA-256 precompile through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('OUTPUT'); - /* - '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"PRECOMPILE" - "caller":"0.0.1131" - "caller_type":"CONTRACT" - "from":"0x000000000000000000000000000000000000046b" - "gas":469355 - "gas_used":60 - "index":1 - "input":"0x" - "recipient":"0.0.2" - "recipient_type":"CONTRACT" - "result_data":"0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - "result_data_type":"OUTPUT" - "timestamp":"1715846596.410665470" - "to":"0x0000000000000000000000000000000000000002" - "value":0}' - */ - }); - - // EQUIVALENCE-018 - it('internal CALL to 0.0.3 without amount should execute the RIPEMD-160 precompile through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('OUTPUT'); - /* - '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"PRECOMPILE" - "caller":"0.0.1149" - "caller_type":"CONTRACT" - "from":"0x000000000000000000000000000000000000047d" - "gas":469355 - "gas_used":600 - "index":1 - "input":"0x" - "recipient":"0.0.3" - "recipient_type":"CONTRACT" - "result_data":"0x0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31" - "result_data_type":"OUTPUT" - "timestamp":"1715847501.800742931" - "to":"0x0000000000000000000000000000000000000003" - "value":0}' - */ - }); - - // EQUIVALENCE-019 - it('internal CALL to 0.0.4 without amount should execute the identity precompile through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT_TO_IDENTITY_PRECOMPILE, - EMPTY_FUNCTION_PARAMS, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('OUTPUT'); - /* - '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"PRECOMPILE" - "caller":"0.0.1158" - "caller_type":"CONTRACT" - "from":"0x0000000000000000000000000000000000000486" - "gas":469422 - "gas_used":30 - "index":1 - "input":"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f2c20576f726c640000000000000000000000000000000000000000" - "recipient":"0.0.4" - "recipient_type":"CONTRACT" - "result_data":"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f2c20576f726c640000000000000000000000000000000000000000" - "result_data_type":"OUTPUT" - "timestamp":"1715847778.640519586" - "to":"0x0000000000000000000000000000000000000004" - "value":0}' - */ + [ + { + // Equivalence 014, 040, 052 + opcodes: [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall], + addresses: [ADDRESS_0_0_0], + amount: 0, + outcome: Outcomes.Output, + errorMessage: '', + }, + { + // Equivalence 015 + opcodes: [Opcodes.Call], + addresses: [ADDRESS_0_0_0], + amount: 100, + outcome: Outcomes.Error, + errorMessage: INVALID_FEE_SUBMITTED, + }, + { + // Equivalence 020 + opcodes: [Opcodes.Call], + addresses: ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7', '0.0.8', '0.0.9'], + amount: 100, + outcome: Outcomes.Error, + errorMessage: INVALID_FEE_SUBMITTED, + }, + { + // Equivalence 022 + opcodes: [Opcodes.Call], + addresses: ['0.0.10', '0.0.100', '0.0.357'], + amount: 100, + outcome: Outcomes.Error, + errorMessage: INVALID_FEE_SUBMITTED, + }, + { + // Equivalence 021, 045, 057 + opcodes: [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall], + addresses: ['0.0.10', '0.0.100', '0.0.357'], + amount: 0, + outcome: Outcomes.Error, + errorMessage: INVALID_SOLIDITY_ADDRESS, + }, + { + // Equivalence 030 + opcodes: [Opcodes.Call], + addresses: ['0.0.362', '0.0.556', '0.0.750'], + amount: 100, + outcome: Outcomes.Error, + errorMessage: INVALID_FEE_SUBMITTED, + }, + { + // Equivalence 031 - Updated, same as mirror node, call to 751..799 fails with INVALID_ALIAS_KEY + // This should be verified, what is the behaviour? + opcodes: [Opcodes.Call], + addresses: ['0.0.751', '0.0.799', '0.0.800', '0.0.1000'], + amount: 100, + outcome: Outcomes.Output, + errorMessage: '', + }, + { + // Equivalence 029, 049, 061 + opcodes: [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall], + addresses: ['0.0.362', '0.0.750', '0.0.751', '0.0.1000'], + amount: 0, + outcome: Outcomes.Error, + errorMessage: INVALID_SOLIDITY_ADDRESS, + }, + ].forEach((test) => { + test.opcodes.forEach((opcode) => { + test.addresses.forEach((address) => { + it(`internal ${opcode.toUpperCase()} to address ${address} ${getTestSummaryAmount( + test.amount, + )} amount ${getTestSummaryOutcome( + test.outcome, + address, + test.errorMessage, + )} when called through an intermediary contract`, async function () { + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + getFunctionName(opcode, test.amount, address), + getContractFunctionParams(address), + 500_000, + test.amount, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + expectSuccessfulContractCall(record); + + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(childRecord.to).to.equal(Utils.idToEvmAddress(address)); + expect(childRecord.call_operation_type).to.equal(opcode.toUpperCase()); + + if (test.outcome === Outcomes.Error) { + expect(childRecord.result_data_type).to.equal( + Outcomes.Error, + 'Expected "Error" but contract call was executed successfully.', + ); + if (test.errorMessage) { + expect(decodeResultData(childRecord.result_data)).to.equal( + test.errorMessage, + 'Error received was not as expected.', + ); + } + } else if (test.outcome === Outcomes.Output) { + expect(childRecord.result_data_type).to.equal( + Outcomes.Output, + `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, + ); + } else { + assert.fail(`Test outcome "${test.outcome}" is not handled in test code`); + } + }); + }); + }); }); - // EQUIVALENCE-020 - // FROM https://www.notion.so/limechain/In-Equivalance-Test-plan-json-rpc-relay-10a7cd268daa44e9b414f3f5410bc08d says 'These should all fail with INVALID_FEE_SUBMITTED when called through an intermediary contract.' - ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7', '0.0.8', '0.0.9'].forEach((address) => { - it(`internal CALL to precompile address ${address} with amount should fail with INVALID_FEE_SUBMITTED when called through an intermediary contract.`, async function () { - const evmAddress = Utils.idToEvmAddress(address); - const message = 'Encode me!'; - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + // Equivalence 016, 041, 053 + [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { + it(`internal ${opcode.toUpperCase()} to address 0.0.1 without amount with knowh hash and signature should execute the ecrecover precompile through the intermediary contract.`, async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); + const messageSignerAddress = '05fba803be258049a27b820088bab1cad2058871'; + const hashedMessage = + '0xf950ac8b7f08b2f5ffa0f893d0f85398135301759b768dc20c1e16d9cdba5b53000000000000000000000000000000000000000000000000000000000000001b45e5f9dc145b79479820a9dfa925bb698333e7f17b7d570391e8487c96a39e07675b682b2519f6232152a9f6f4f5923d171dfb7636daceee2c776edecc6c8b64'; + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITH_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), - 2_000_000, - 100, + getFunctionName(opcode, 0, ADDRESS_0_0_1), + new ContractFunctionParameters().addAddress(evmAddress).addBytes(precheck.hexToBytes(hashedMessage)), + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + expectSuccessfulContractCall(record); + const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); + expect(childRecord.result_data_type).to.equal( + Outcomes.Output, + `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, + ); + expect(childRecord.result_data).to.include(messageSignerAddress); }); }); - // EQUIVALENCE-021 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS - // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 - ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { - it(`internal CALL to ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract.`, async function () { - const evmAddress = Utils.idToEvmAddress(address); + // Equivalence 017, 042, 054 + [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { + it(`internal ${opcode.toUpperCase()} to address 0.0.2 without amount with knowh hash and signature should execute the SHA-256 precompile through the intermediary contract.`, async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); + const message = 'Encode me!'; + const hashedMessage = '0x68907fbd785a694c3617d35a6ce49477ac5704d75f0e727e353da7bc664aacc2'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + getFunctionName(opcode, 0, ADDRESS_0_0_2), + new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + expectSuccessfulContractCall(record); + const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.call_operation_type).to.equal('CALL'); - expect(childRecord.call_type).to.equal('CALL'); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_SOLIDITY_ADDRESS); - /* - '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"CALL" - "caller":"0.0.1224" - "caller_type":"CONTRACT" - "from":"0x00000000000000000000000000000000000004c8" - "gas":466894 - "gas_used":466894 - "index":1 - "input":"0x" - "recipient":"0.0.100" - "recipient_type":"ACCOUNT" - "result_data":"0x505245434f4d50494c455f4552524f52" - "result_data_type":"ERROR" - "timestamp":"1715855739.520497716" - "to":"0x0000000000000000000000000000000000000064" - "value":0}' - */ + expect(childRecord.result_data_type).to.equal( + Outcomes.Output, + `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, + ); + expect(childRecord.result_data).to.equal(hashedMessage); }); }); - // EQUIVALENCE-022 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED - // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 - ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { - it(`internal CALL to adress '${address}' with amount should fail with 'INVALID_FEE_SUBMITTED' when attempted through an intermediary contract.`, async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_100); - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + // Equivalence 018, 043, 055 + [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { + it(`internal ${opcode.toUpperCase()} to address 0.0.3 without amount with knowh hash and signature should execute the RIPEMD-160 precompile through the intermediary contract.`, async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); + const message = 'Encode me!'; + const hashedMessage = '4f0c39893f4c1c805aea87a95b5d359a218920d6'; + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_CALL_WITH_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 1_000_000, - 100, + getFunctionName(opcode, 0, ADDRESS_0_0_3), + new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), + 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + expectSuccessfulContractCall(record); + const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.call_operation_type).to.equal('CALL'); - expect(childRecord.call_type).to.equal('CALL'); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); - /* - '{ - "call_depth": 1, - "call_operation_type": "CALL", - "call_type": "CALL", - "caller": "0.0.1038", - "caller_type": "CONTRACT", - "from": "0x000000000000000000000000000000000000040e", - "gas": 927957, - "gas_used": 927957, - "index": 1, - "input": "0x", - "recipient": "0.0.100", - "recipient_type": "ACCOUNT", - "result_data": "0x505245434f4d50494c455f4552524f52", - "result_data_type": "ERROR", - "timestamp": "1715935668.132923212", - "to": "0x0000000000000000000000000000000000000064", - "value": 100 - }' - */ - }); - }); - - // EQUIVALENCE-023 - Same as mirror node - PRECOMPILE ERROR - it('internal CALL to 0.0.358 without amount should execute the HTS precompiles through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - /* - '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"CALL" - "caller":"0.0.1256" - "caller_type":"CONTRACT" - "from":"0x00000000000000000000000000000000000004e8" - "gas":466883 - "gas_used":466883 - "index":1 - "input":"0x" - "recipient":null - "recipient_type":null - "result_data":"0x505245434f4d50494c455f4552524f52" - "result_data_type":"ERROR" - "timestamp":"1715860160.719665055" - "to":"0x0000000000000000000000000000000000000166" - "value":0}' - */ - }); - - // EQUIVALENCE-024 - Same as mirror node - PRECOMPILE ERROR - it('internal CALL to address 0.0.358 with amount for other functions than TokenCreate i.e. Approve/Associate should result in INVALID_FEE_SUBMITTED through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( - equivalenceContractId, - MAKE_CALL_WITH_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 1_000_000, - 100, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); - /* - '{"call_depth":1 - "call_operation_type":"CALL" - "call_type":"CALL" - "caller":"0.0.1265" - "caller_type":"CONTRACT" - "from":"0x00000000000000000000000000000000000004f1" - "gas":959113 - "gas_used":959113 - "index":1 - "input":"0x" - "recipient":null - "recipient_type":null - "result_data":"0x505245434f4d50494c455f4552524f52" - "result_data_type":"ERROR" - "timestamp":"1715860279.970723513" - "to":"0x0000000000000000000000000000000000000166" - "value":0}' - */ - }); - - // EQUIVALENCE-025 - Same as mirror node - it.only('internal CALL to 0.0.360 without amount should execute the ExchangeRate precompile through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_360); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_HTS_CALL_WITHOUT_AMOUNT, - new ContractFunctionParameters().addAddress(tokenAddress), - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('CALL'); - expect(childRecord.call_type).to.equal('SYSTEM'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('OUTPUT'); - /* - '{ - "call_depth": 1, - "call_operation_type": "CALL", - "call_type": "SYSTEM", - "caller": "0.0.1297", - "caller_type": "CONTRACT", - "from": "0x0000000000000000000000000000000000000511", - "gas": 49094, - "gas_used": 100, - "index": 1, - "input": "0x19f373610000000000000000000000000000000000000000000000000000000000000513", - "recipient": "0.0.359", - "recipient_type": "CONTRACT", - "result_data": "0x00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000001", - "result_data_type": "OUTPUT", - "timestamp": "1715867826.679620882", - "to": "0x0000000000000000000000000000000000000167", - "value": 0 - }' - */ - }); - - // EQUIVALENCE-026 - Same as mirror node - it.only('internal CALL to addresses 0.0.361 without amount(PRNG precompile) should execute it through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ETH_PRECOMPILE_0x361); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - 'getPseudorandomSeed', - EMPTY_FUNCTION_PARAMS, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('CALL'); - expect(childRecord.call_type).to.equal('SYSTEM'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - /* - * '{ - "call_depth": 1, - "call_operation_type": "CALL", - "call_type": "SYSTEM", - "caller": "0.0.1306", - "caller_type": "CONTRACT", - "from": "0x000000000000000000000000000000000000051a", - "gas": 49808, - "gas_used": 0, - "index": 1, - "input": "0xd83bf9a1", - "recipient": "0.0.361", - "recipient_type": "CONTRACT", - "result_data": "0x62f0bc0bb45c2750c1afebdefb68e6d0a8ea70351a43ce98820d30571fdd4ad5", - "result_data_type": "OUTPUT", - "timestamp": "1715869135.530404307", - "to": "0x0000000000000000000000000000000000000169", - "value": 0 - }' - */ - }); - - // EQUIVALENCE-027 - Same as mirror node - succeeds but should result in INVALID_FEE_SUBMITTED - it('internal CALL to addresses 0.0.359 with amount(ExchangeRate precompile) should result in INVALID_FEE_SUBMITTED through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( - equivalenceContractId, - MAKE_HTS_CALL_WITH_AMOUNT, - new ContractFunctionParameters().addAddress(tokenAddress), - 1_000_000, - 100, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('CALL'); - expect(childRecord.call_type).to.equal('SYSTEM'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); - /* - '{ - "call_depth": 1, - "call_operation_type": "CALL", - "call_type": "SYSTEM", - "caller": "0.0.1065", - "caller_type": "CONTRACT", - "from": "0x0000000000000000000000000000000000000429", - "gas": 959554, - "gas_used": 100, - "index": 1, - "input": "0x19f37361000000000000000000000000000000000000000000000000000000000000042b", - "recipient": "0.0.359", - "recipient_type": "CONTRACT", - "result_data": "0x00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000001", - "result_data_type": "OUTPUT", - "timestamp": "1716205365.582565806", - "to": "0x0000000000000000000000000000000000000167", - "value": 0 - }' - */ - }); - - // EQUIVALENCE-028 - Same as mirror node - succeeds but should result in INVALID_FEE_SUBMITTED - it('internal CALL to addresses 0.0.361 with amount (PRNG precompile) should result in INVALID_FEE_SUBMITTED through the intermediary contract.', async function () { - const evmAddress = Utils.idToEvmAddress(ETH_PRECOMPILE_0x361); - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( - equivalenceContractId, - 'getPseudorandomSeedWithAmount', - EMPTY_FUNCTION_PARAMS, - 1_000_000, - 100, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_FEE_SUBMITTED); - /* - '{ - "call_depth": 1, - "call_operation_type": "CALL", - "call_type": "SYSTEM", - "caller": "0.0.1315", - "caller_type": "CONTRACT", - "from": "0x0000000000000000000000000000000000000523", - "gas": 960397, - "gas_used": 0, - "index": 1, - "input": "0xd83bf9a1", - "recipient": "0.0.361", - "recipient_type": "CONTRACT", - "result_data": "0x29cd8faf6c267af3e6a2aa76d0609dafdcc5fe766141df230ab892398a4db347", - "result_data_type": "OUTPUT", - "timestamp": "1715869282.420056139", - "to": "0x0000000000000000000000000000000000000169", - "value": 0 - }' - */ - }); - - // EQUIVALENCE-029 range 0.0.361..0.0.750 !!PRNG is 361!! - [ - // '0.0.362', - // '0.0.450', - // '0.0.599', - // '0.0.700', - // '0.0.751', - // '0.0.785', - // '0.0.1000', - ].forEach((address) => { - it(`internal CALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { - const evmAddress = Utils.idToEvmAddress(address); - const { contractExecuteTimestamp, contractExecutedTransactionId } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_CALL_WITHOUT_AMOUNT, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), - 500_000, + expect(childRecord.result_data_type).to.equal( + Outcomes.Output, + `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const actions = await getContractActions(record.hash); - const childRecordError = decodeResultData(actions[1].result_data); - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecordError).to.equal(INVALID_SOLIDITY_ADDRESS); + expect(removeLeadingZeros(removeLeading0x(childRecord.result_data))).to.equal(hashedMessage); }); }); - // EQUIVALENCE-030 range 0.0.361..0.0.750 !!PRNG is 361!! - [ - // '0.0.362', - // '0.0.450', - // '0.0.599', - // '0.0.750', - ].forEach((address) => { - it(`internal CALL to address ${address} with amount should fail with INVALID_FEE_SUBMITTED when called through an intermediary contract.`, async function () {}); - }); - - /* EQUIVALENCE-031 range 0.0.751..0.0.1000 - * This should be verified what is the behaviour - * Per other notes sheet: - * This should work for address 0.0.800 - * And if account exists and has receiverSigRequired = false - * And if (account exists) and (account has receiverSigRequired = true) and (account is sender) - */ - - [ - // '0.0.751', - // '0.0.800', - // '0.0.1000', - ].forEach((address) => { - it(`internal CALL to address ${address} with amount`, async function () {}); - }); - - // EQUIVALENCE-032 - // EQUIVALENCE-033 - // EQUIVALENCE-034 - // EQUIVALENCE-035 - // EQUIVALENCE-036 - // EQUIVALENCE-037 - // EQUIVALENCE-038 - // EQUIVALENCE-039 - - // EQUIVALENCE-040 - PRECOMPILE ERROR - Same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED - it('internal STATICCALL to address(0.0.0) without amount should succeed with noop through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_STATICCALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.call_type).to.equal('CALL'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-041; - it('internal STATICCALL to address 0.0.1 with known hash and signature should execute the ecrecover precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_STATICCALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-042; - it('internal STATICCALL to address 0.0.2 should execute the SHA-256 precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_STATICCALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-043; - it('internal STATICCALL to address 0.0.3 should execute the RIPEMD-160 precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_STATICCALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-044; - it('internal STATICCALL to address 0.0.4 should execute the identity precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); - const message = 'Encode me!'; - const messageBytes = toUtf8Bytes(message); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(messageBytes); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_STATICCALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - expect(message).to.equal( - decodeResultData(childRecord.result_data), - `Decoded 'result_data' from child record is not '${message}'`, - ); - }); - - // EQUIVALENCE-045 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS - // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 - ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { - it(`STATICCALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { - const evmAddress = Utils.idToEvmAddress(address); + // Equivalence 019, 044, 056 + [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { + it(`internal ${opcode.toUpperCase()} to address 0.0.4 without amount with knowh hash and signature should execute the identity precompile through the intermediary contract.`, async function () { + const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); + const message = 'Encode me!'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - MAKE_STATICCALL, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + getFunctionName(opcode, 0, ADDRESS_0_0_4), + new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + expectSuccessfulContractCall(record); + const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_type).to.equal('PRECOMPILE'); expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.call_type).to.equal('CALL'); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_SOLIDITY_ADDRESS); - /* - '{ - "call_depth": 1, - "call_operation_type": "STATICCALL", - "call_type": "CALL", - "caller": "0.0.1038", - "caller_type": "CONTRACT", - "from": "0x000000000000000000000000000000000000040e", - "gas": 466983, - "gas_used": 466983, - "index": 1, - "input": "0x", - "recipient": "0.0.10", - "recipient_type": "ACCOUNT", - "result_data": "0x505245434f4d50494c455f4552524f52", - "result_data_type": "ERROR", - "timestamp": "1715935738.322606675", - "to": "0x000000000000000000000000000000000000000a", - "value": 0 - }' - */ - }); - }); - - // EQUIVALENCE-046; - // BUG: [Direct call w/o value to system accounts [0.0.361-0.0.750] result in precompile error] https://github.com/hashgraph/hedera-services/issues/11033 - it('internal STATICCALL to address 0.0.358 without amount should execute the HTS precompiles through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_STATICCALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-047; - it('internal STATICCALL to addresses 0.0.359 (ExchangeRate precompile) without amount should execute the ExchangeRate precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); - const params = new ContractFunctionParameters().addAddress(tokenAddress); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_HTS_STATICCALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - /* - '{ - "call_depth": 1, - "call_operation_type": "STATICCALL", - "call_type": "SYSTEM", - "caller": "0.0.1049", - "caller_type": "CONTRACT", - "from": "0x0000000000000000000000000000000000000419", - "gas": 467412, - "gas_used": 100, - "index": 1, - "input": "0x19f37361000000000000000000000000000000000000000000000000000000000000041b", - "recipient": "0.0.359", - "recipient_type": "CONTRACT", - "result_data": "0x00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000001", - "result_data_type": "OUTPUT", - "timestamp": "1716203726.922385630", - "to": "0x0000000000000000000000000000000000000167", - "value": 0 - }' - */ - }); - - // EQUIVALENCE-048; - it('internal STATICCALL to addresses 0.0.361 (PRNG precompile) without amount should execute the PRNG precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ETH_PRECOMPILE_0x361); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - 'getPseudorandomSeedStaticCall', - EMPTY_FUNCTION_PARAMS, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - - expect(childRecord.call_operation_type).to.equal('STATICCALL'); - expect(childRecord.call_type).to.equal('SYSTEM'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-049 range 0.0.361..0.0.750 !!PRNG is 361!! - [ - // '0.0.362', - // '0.0.450', - // '0.0.599', - // '0.0.700', - // '0.0.751', - // '0.0.785', - // '0.0.1000', - ].forEach((address) => { - it(`internal STATICCALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { - const params = new ContractFunctionParameters() - .addAddress(Utils.idToEvmAddress(address)) - .addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp, contractExecutedTransactionId } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_STATICCALL, - params, - 500_000, + expect(childRecord.result_data_type).to.equal( + Outcomes.Output, + `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, + ); + expect(decodeResultData(childRecord.result_data)).to.equal( + message, + `Decoded 'result_data' from child record is not '${message}'`, ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const actions = await getContractActions(record.hash); - const childRecordError = decodeResultData(actions[1].result_data); - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecordError).to.equal(INVALID_SOLIDITY_ADDRESS); }); }); - // EQUIVALENCE-050; - // STATICCALL to address(over 0.0.1000) wi/o amount (case contract). Create a contract within the test. Grab the contractID and execute InternalCall - // Expected to succeed if the contract exists and is called through an intermediary contract. - // it('internal STATICCALL to address(over 0.0.1000) wi/o amount (case contract)', async function () { - // const accounts = await mirrorClient.get('/accounts?account.id=gt:0.0.1000'); - // console.log(JSON.stringify(accounts)) - // }); - - // EQUIVALENCE-051; - // STATICCALL to address(over 0.0.1000) wi/o amount (case invalid contractID): In here we should either hit an invalid contractID(this is challenging due to the different environments) - // Or we should create an account and use accountID instead (needs to be verified) - // Should fail with INVALID_SOLIDITY_ADDRESS through the intermediary contract. - // it('internal STATICCALL to address(over 0.0.1000) wi/o amount (case invalid contractID)', async function () { - // }); - - // EQUIVALENCE-052 - PRECOMPILE ERROR - Same as mirror node but it fails with PRECOMPILE_ERROR it should be INVALID_FEE_SUBMITTED - it('internal DELEGATECALL to address(0.0.0) without amount should succeed with noop through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_0); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_DELEGATECALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.call_type).to.equal('CALL'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-053 - it('internal DELEGATECALL to address 0.0.1 with known hash and signature should execute the ecrecover precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_DELEGATECALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-054 - it('internal DELEGATECALL to address 0.0.2 should execute the SHA-256 precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_DELEGATECALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-055 - it('internal DELEGATECALL to address 0.0.3 should execute the RIPEMD-160 precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_DELEGATECALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-056 - it('internal DELEGATECALL to address 0.0.4 should execute the identity precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); - const message = 'Encode me!'; - const messageBytes = toUtf8Bytes(message); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(messageBytes); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_DELEGATECALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - expect(message).to.equal( - decodeResultData(childRecord.result_data), - `Decoded 'result_data' from child record is not '${message}'`, - ); - }); - - // EQUIVALENCE-057 - Same as mirror node - fails with PRECOMPILE_ERROR it should be INVALID_SOLIDITY_ADDRESS - // BUG: [Executing an internal call without amount against system accounts results in PRECOMPILE_ERROR] https://github.com/hashgraph/hedera-services/issues/11158 - ['0.0.10', '0.0.100', '0.0.357'].forEach((address) => { - it(`internal DELEGATECALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { + // Equivalence 024, 027, 028 + [ADDRESS_0_0_359, ADDRESS_0_0_360, ADDRESS_0_0_361].forEach((address) => { + it(`internal CALL to address ${address} with amount ${getTestSummaryOutcome( + Outcomes.Error, + address, + INVALID_FEE_SUBMITTED, + )} through the intermediary contract`, async function () { const evmAddress = Utils.idToEvmAddress(address); - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( equivalenceContractId, - MAKE_DELEGATECALL, - new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)), + getFunctionName(Opcodes.Call, 100, address), + getContractFunctionParams(address), 500_000, + 100, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + expectSuccessfulContractCall(record); + const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + expect(childRecord.call_operation_type).to.equal(Opcodes.Call.toUpperCase()); + expect(childRecord.call_type).to.equal('SYSTEM'); expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.call_type).to.equal('CALL'); - expect(childRecord.result_data_type).to.equal('ERROR'); - expect(decodeResultData(childRecord.result_data)).to.equal(INVALID_SOLIDITY_ADDRESS); - /* - '{ - "call_depth": 1, - "call_operation_type": "DELEGATECALL", - "call_type": "CALL", - "caller": "0.0.1038", - "caller_type": "CONTRACT", - "from": "0x000000000000000000000000000000000000040e", - "gas": 466962, - "gas_used": 466962, - "index": 1, - "input": "0x", - "recipient": "0.0.10", - "recipient_type": "ACCOUNT", - "result_data": "0x505245434f4d50494c455f4552524f52", - "result_data_type": "ERROR", - "timestamp": "1715935792.722483506", - "to": "0x000000000000000000000000000000000000000a", - "value": 0 - }' - */ + expect(childRecord.result_data_type).to.equal( + Outcomes.Error, + 'Expected "Error" but contract call was executed successfully.', + ); + expect(decodeResultData(childRecord.result_data)).to.equal( + INVALID_FEE_SUBMITTED, + 'Error received was not as expected.', + ); }); }); - // EQUIVALENCE-058 - // BUG: [Direct call w/o value to system accounts [0.0.361-0.0.750] result in precompile error] https://github.com/hashgraph/hedera-services/issues/11033 - it('internal DELEGATECALL to address 0.0.358 without amount should execute the HTS precompiles through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_358); - const params = new ContractFunctionParameters().addAddress(evmAddress).addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_DELEGATECALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-059 - // Investigate - it.only('internal DELEGATECALL to addresses 0.0.359 (ExchangeRate precompile) without amount should execute the ExchangeRate precompile through the intermediary contract', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_359); - const params = new ContractFunctionParameters().addAddress(tokenAddress); - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - 'htsDelegateCall', - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - /* - '{ - "call_depth": 1, - "call_operation_type": "DELEGATECALL", - "call_type": "SYSTEM", - "caller": "0.0.1049", - "caller_type": "CONTRACT", - "from": "0x0000000000000000000000000000000000000419", - "gas": 467413, - "gas_used": 467413, - "index": 1, - "input": "0x19f37361000000000000000000000000000000000000000000000000000000000000041b", - "recipient": "0.0.359", - "recipient_type": "CONTRACT", - "result_data": "0x505245434f4d50494c455f4552524f52", - "result_data_type": "ERROR", - "timestamp": "1716203893.732399055", - "to": "0x0000000000000000000000000000000000000167", - "value": 0 - }' - */ - }); - - it.only('DELEGATECALL to addresses 0.0.359 (ExchangeRate precompile)...', async function () { - const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_360); // address 360 is 0000000000000000000000000000000000000168, mirror node calls that address - const evmAddress1 = Utils.idToEvmAddress(ADDRESS_0_0_359); // 359 is 0000000000000000000000000000000000000167 - - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - 'makeDelegateCall', - new ContractFunctionParameters() - .addAddress(evmAddress) - .addFunction(tokenAddress, new ContractFunctionSelector('isToken(address)')), - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-060 - it('internal DELEGATECALL to addresses 0.0.361 (PRNG precompile) without amount should execute the PRNG precompile through the intermediary contract', async function () { - const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceContractId, - 'getPseudorandomSeedDelegateCall', - EMPTY_FUNCTION_PARAMS, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - - expect(childRecord.call_operation_type).to.equal('DELEGATECALL'); - expect(childRecord.call_type).to.equal('SYSTEM'); - expect(childRecord.to).to.equal(Utils.idToEvmAddress(ETH_PRECOMPILE_0x361)); - expect(childRecord.result_data_type).to.equal( - 'OUTPUT', - `Error in child record: "${decodeResultData(childRecord.result_data)}"`, - ); - }); - - // EQUIVALENCE-061 range 0.0.361..0.0.750 !!PRNG is 361!! - // BUG: 3 different outcomes for address range - [ - // '0.0.362', - // '0.0.450', - // '0.0.751', - // '0.0.741', - // '0.0.800', - // '0.0.1000', - ].forEach((address) => { - it(`internal DELEGATECALL to address ${address} without amount should fail with INVALID_SOLIDITY_ADDRESS when called through an intermediary contract`, async function () { - const params = new ContractFunctionParameters() - .addAddress(Utils.idToEvmAddress(address)) - .addBytes(new Uint8Array(0)); - - const { contractExecuteTimestamp, contractExecutedTransactionId } = await servicesClient.executeContractCall( - equivalenceContractId, - MAKE_DELEGATECALL, - params, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - const childRecordError = hexToASCII(childRecord.result_data); - - expect(record.contract_id).to.equal(equivalenceContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); - expect(childRecordError).to.equal(INVALID_SOLIDITY_ADDRESS); + // Equivalence 023, 025, 026, 046, 047, 048, 058, 059, 060 + [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { + [ADDRESS_0_0_359, ADDRESS_0_0_360, ADDRESS_0_0_361].forEach((address) => { + it(`internal ${opcode.toUpperCase()} to address ${address} without amount ${getTestSummaryOutcome( + Outcomes.Output, + address, + )} through the intermediary contract`, async function () { + const evmAddress = Utils.idToEvmAddress(address); + const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + getFunctionName(opcode, 0, address), + getContractFunctionParams(address), + 500_000, + 0, + ); + + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + expectSuccessfulContractCall(record); + + const contractActions = await getContractActions(record.hash); + const childRecord = contractActions.actions[1]; + + expect(childRecord.call_operation_type).to.equal(opcode.toUpperCase()); + expect(childRecord.call_type).to.equal('SYSTEM'); + expect(childRecord.to).to.equal(evmAddress); + expect(childRecord.result_data_type).to.equal( + Outcomes.Output, + `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, + ); + }); }); }); }); diff --git a/packages/server/tests/contracts/EquivalenceContract.json b/packages/server/tests/contracts/EquivalenceContract.json index bd4b90b8b5..d942466cfb 100644 --- a/packages/server/tests/contracts/EquivalenceContract.json +++ b/packages/server/tests/contracts/EquivalenceContract.json @@ -70,6 +70,44 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tinycents", + "type": "uint256" + } + ], + "name": "exchangeRateDelegateCall", + "outputs": [ + { + "internalType": "uint256", + "name": "tinybars", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tinycents", + "type": "uint256" + } + ], + "name": "exchangeRateStaticCall", + "outputs": [ + { + "internalType": "uint256", + "name": "tinybars", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -515,8 +553,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b50611f76806100206000396000f3fe6080604052600436106101665760003560e01c8063845b27cd116100d1578063b2a07c3e1161008a578063d83bf9a111610064578063d83bf9a1146105b9578063e34c93b8146105e4578063e717ea6114610614578063f8b2cb4f1461063f57610166565b8063b2a07c3e14610520578063b51c4f9614610550578063d558bd8e1461058d57610166565b8063845b27cd146103e65780638771074f1461042457806388b0764a146104625780639154822814610481578063b0d869b1146104b2578063b2539b2e146104ef57610166565b80636144ef6d116101235780636144ef6d146102b757806367edbd6d146102d55780636f68a38e1461030157806375ce65191461033f5780637e38d3a31461036b57806381ea4408146103a957610166565b806315ed14111461016b57806319f88c7d146101a95780631d620e2a146101e657806343b82d46146102245780634c3ff83a1461024f5780634e7315831461027a575b600080fd5b34801561017757600080fd5b50610192600480360381019061018d91906119e7565b61067c565b6040516101a0929190611add565b60405180910390f35b3480156101b557600080fd5b506101d060048036038101906101cb9190611b6d565b6106f5565b6040516101dd9190611be6565b60405180910390f35b3480156101f257600080fd5b5061020d60048036038101906102089190611c01565b610729565b60405161021b929190611c2e565b60405180910390f35b34801561023057600080fd5b5061023961086b565b6040516102469190611be6565b60405180910390f35b34801561025b57600080fd5b50610264610983565b6040516102719190611be6565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611c01565b610a9b565b6040516102ae9190611c57565b60405180910390f35b6102bf610b07565b6040516102cc9190611be6565b60405180910390f35b3480156102e157600080fd5b506102ea610c22565b6040516102f8929190611add565b60405180910390f35b34801561030d57600080fd5b5061032860048036038101906103239190611c01565b610cde565b604051610336929190611c2e565b60405180910390f35b34801561034b57600080fd5b50610354610e20565b604051610362929190611add565b60405180910390f35b34801561037757600080fd5b50610392600480360381019061038d91906119e7565b610eda565b6040516103a0929190611add565b60405180910390f35b3480156103b557600080fd5b506103d060048036038101906103cb9190611c01565b610f55565b6040516103dd9190611be6565b60405180910390f35b3480156103f257600080fd5b5061040d60048036038101906104089190611c01565b610f65565b60405161041b929190611c2e565b60405180910390f35b34801561043057600080fd5b5061044b600480360381019061044691906119e7565b6110a9565b604051610459929190611add565b60405180910390f35b61046a611122565b604051610478929190611add565b60405180910390f35b61049b600480360381019061049691906119e7565b6111df565b6040516104a9929190611add565b60405180910390f35b3480156104be57600080fd5b506104d960048036038101906104d49190611caf565b61125b565b6040516104e69190611ceb565b60405180910390f35b61050960048036038101906105049190611c01565b611382565b604051610517929190611c2e565b60405180910390f35b61053a60048036038101906105359190611caf565b6114c7565b6040516105479190611ceb565b60405180910390f35b34801561055c57600080fd5b5061057760048036038101906105729190611c01565b6115ef565b6040516105849190611ceb565b60405180910390f35b34801561059957600080fd5b506105a26115ff565b6040516105b0929190611add565b60405180910390f35b3480156105c557600080fd5b506105ce6116b9565b6040516105db9190611be6565b60405180910390f35b6105fe60048036038101906105f99190611b6d565b6117d3565b60405161060b9190611be6565b60405180910390f35b34801561062057600080fd5b50610629611806565b6040516106369190611d15565b60405180910390f35b34801561064b57600080fd5b5061066660048036038101906106619190611c01565b61180e565b6040516106739190611ceb565b60405180910390f35b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516106a59190611d6c565b600060405180830381855afa9150503d80600081146106e0576040519150601f19603f3d011682016040523d82523d6000602084013e6106e5565b606091505b5080925081935050509250929050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2815193505050509392505050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff16856040516024016107599190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516107e39190611d6c565b600060405180830381855afa9150503d806000811461081e576040519150601f19603f3d011682016040523d82523d6000602084013e610823565b606091505b5091509150811561086457600080828060200190518101906108459190611de8565b915060030b915060168260070b14955085801561085f5750805b945050505b5050915091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516109199190611d6c565b600060405180830381855af49150503d8060008114610954576040519150601f19603f3d011682016040523d82523d6000602084013e610959565b606091505b50915091508161096857600080fd5b8080602001905181019061097c9190611e54565b9250505090565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610a319190611d6c565b600060405180830381855afa9150503d8060008114610a6c576040519150601f19603f3d011682016040523d82523d6000602084013e610a71565b606091505b509150915081610a8057600080fd5b80806020019051810190610a949190611e54565b9250505090565b60606000823b905060008167ffffffffffffffff811115610abf57610abe6118bc565b5b6040519080825280601f01601f191660200182016040528015610af15781602001600182028036833780820191505090505b50905081600060208301863c8092505050919050565b600080600061016973ffffffffffffffffffffffffffffffffffffffff16346040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610bb69190611d6c565b60006040518083038185875af1925050503d8060008114610bf3576040519150601f19603f3d011682016040523d82523d6000602084013e610bf8565b606091505b509150915081610c0757600080fd5b80806020019051810190610c1b9190611e54565b9250505090565b600060606000604051602001610c3790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610c749190611c57565b604051602081830303815290604052604051610c909190611d6c565b6000604051808303816000865af19150503d8060008114610ccd576040519150601f19603f3d011682016040523d82523d6000602084013e610cd2565b606091505b50915091505050509091565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610d0e9190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610d989190611d6c565b600060405180830381855af49150503d8060008114610dd3576040519150601f19603f3d011682016040523d82523d6000602084013e610dd8565b606091505b50915091508115610e195760008082806020019051810190610dfa9190611de8565b915060030b915060168260070b149550858015610e145750805b945050505b5050915091565b600060606000604051602001610e3590611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610e729190611c57565b604051602081830303815290604052604051610e8e9190611d6c565b600060405180830381855af49150503d8060008114610ec9576040519150601f19603f3d011682016040523d82523d6000602084013e610ece565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1683604051610f039190611d6c565b6000604051808303816000865af19150503d8060008114610f40576040519150601f19603f3d011682016040523d82523d6000602084013e610f45565b606091505b5080925081935050509250929050565b600080823f905080915050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610f959190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161101f9190611d6c565b6000604051808303816000865af19150503d806000811461105c576040519150601f19603f3d011682016040523d82523d6000602084013e611061565b606091505b509150915081156110a257600080828060200190518101906110839190611de8565b915060030b915060168260070b14955085801561109d5750805b945050505b5050915091565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516110d29190611d6c565b600060405180830381855af49150503d806000811461110d576040519150601f19603f3d011682016040523d82523d6000602084013e611112565b606091505b5080925081935050509250929050565b60006060600060405160200161113790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1634846040516020016111759190611c57565b6040516020818303038152906040526040516111919190611d6c565b60006040518083038185875af1925050503d80600081146111ce576040519150601f19603f3d011682016040523d82523d6000602084013e6111d3565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1634846040516112099190611d6c565b60006040518083038185875af1925050503d8060008114611246576040519150601f19603f3d011682016040523d82523d6000602084013e61124b565b606091505b5080925081935050509250929050565b600080600061016873ffffffffffffffffffffffffffffffffffffffff168460405160240161128a9190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516113149190611d6c565b6000604051808303816000865af19150503d8060008114611351576040519150601f19603f3d011682016040523d82523d6000602084013e611356565b606091505b50915091508161136557600080fd5b808060200190518101906113799190611f13565b92505050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1634866040516024016113b39190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161143d9190611d6c565b60006040518083038185875af1925050503d806000811461147a576040519150601f19603f3d011682016040523d82523d6000602084013e61147f565b606091505b509150915081156114c057600080828060200190518101906114a19190611de8565b915060030b915060168260070b1495508580156114bb5750805b945050505b5050915091565b600080600061016873ffffffffffffffffffffffffffffffffffffffff1634856040516024016114f79190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516115819190611d6c565b60006040518083038185875af1925050503d80600081146115be576040519150601f19603f3d011682016040523d82523d6000602084013e6115c3565b606091505b5091509150816115d257600080fd5b808060200190518101906115e69190611f13565b92505050919050565b600080823b905080915050919050565b60006060600060405160200161161490611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff16836040516020016116519190611c57565b60405160208183030381529060405260405161166d9190611d6c565b600060405180830381855afa9150503d80600081146116a8576040519150601f19603f3d011682016040523d82523d6000602084013e6116ad565b606091505b50915091505050509091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516117679190611d6c565b6000604051808303816000865af19150503d80600081146117a4576040519150601f19603f3d011682016040523d82523d6000602084013e6117a9565b606091505b5091509150816117b857600080fd5b808060200190518101906117cc9190611e54565b9250505090565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2815193505050509392505050565b600030905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061186e82611843565b9050919050565b61187e81611863565b811461188957600080fd5b50565b60008135905061189b81611875565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6118f4826118ab565b810181811067ffffffffffffffff82111715611913576119126118bc565b5b80604052505050565b600061192661182f565b905061193282826118eb565b919050565b600067ffffffffffffffff821115611952576119516118bc565b5b61195b826118ab565b9050602081019050919050565b82818337600083830152505050565b600061198a61198584611937565b61191c565b9050828152602081018484840111156119a6576119a56118a6565b5b6119b1848285611968565b509392505050565b600082601f8301126119ce576119cd6118a1565b5b81356119de848260208601611977565b91505092915050565b600080604083850312156119fe576119fd611839565b5b6000611a0c8582860161188c565b925050602083013567ffffffffffffffff811115611a2d57611a2c61183e565b5b611a39858286016119b9565b9150509250929050565b60008115159050919050565b611a5881611a43565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611a98578082015181840152602081019050611a7d565b60008484015250505050565b6000611aaf82611a5e565b611ab98185611a69565b9350611ac9818560208601611a7a565b611ad2816118ab565b840191505092915050565b6000604082019050611af26000830185611a4f565b8181036020830152611b048184611aa4565b90509392505050565b600080fd5b600080fd5b60008083601f840112611b2d57611b2c6118a1565b5b8235905067ffffffffffffffff811115611b4a57611b49611b0d565b5b602083019150836001820283011115611b6657611b65611b12565b5b9250929050565b600080600060408486031215611b8657611b85611839565b5b6000611b948682870161188c565b935050602084013567ffffffffffffffff811115611bb557611bb461183e565b5b611bc186828701611b17565b92509250509250925092565b6000819050919050565b611be081611bcd565b82525050565b6000602082019050611bfb6000830184611bd7565b92915050565b600060208284031215611c1757611c16611839565b5b6000611c258482850161188c565b91505092915050565b6000604082019050611c436000830185611a4f565b611c506020830184611a4f565b9392505050565b60006020820190508181036000830152611c718184611aa4565b905092915050565b6000819050919050565b611c8c81611c79565b8114611c9757600080fd5b50565b600081359050611ca981611c83565b92915050565b600060208284031215611cc557611cc4611839565b5b6000611cd384828501611c9a565b91505092915050565b611ce581611c79565b82525050565b6000602082019050611d006000830184611cdc565b92915050565b611d0f81611863565b82525050565b6000602082019050611d2a6000830184611d06565b92915050565b600081905092915050565b6000611d4682611a5e565b611d508185611d30565b9350611d60818560208601611a7a565b80840191505092915050565b6000611d788284611d3b565b915081905092915050565b60008160030b9050919050565b611d9981611d83565b8114611da457600080fd5b50565b600081519050611db681611d90565b92915050565b611dc581611a43565b8114611dd057600080fd5b50565b600081519050611de281611dbc565b92915050565b60008060408385031215611dff57611dfe611839565b5b6000611e0d85828601611da7565b9250506020611e1e85828601611dd3565b9150509250929050565b611e3181611bcd565b8114611e3c57600080fd5b50565b600081519050611e4e81611e28565b92915050565b600060208284031215611e6a57611e69611839565b5b6000611e7884828501611e3f565b91505092915050565b600082825260208201905092915050565b7f48656c6c6f2c20576f726c640000000000000000000000000000000000000000600082015250565b6000611ec8600c83611e81565b9150611ed382611e92565b602082019050919050565b60006020820190508181036000830152611ef781611ebb565b9050919050565b600081519050611f0d81611c83565b92915050565b600060208284031215611f2957611f28611839565b5b6000611f3784828501611efe565b9150509291505056fea26469706673582212206bda6fdee0213387de2ad256148f515e0616c170d9d76630f24a35e4833729b364736f6c63430008130033", - "deployedBytecode": "0x6080604052600436106101665760003560e01c8063845b27cd116100d1578063b2a07c3e1161008a578063d83bf9a111610064578063d83bf9a1146105b9578063e34c93b8146105e4578063e717ea6114610614578063f8b2cb4f1461063f57610166565b8063b2a07c3e14610520578063b51c4f9614610550578063d558bd8e1461058d57610166565b8063845b27cd146103e65780638771074f1461042457806388b0764a146104625780639154822814610481578063b0d869b1146104b2578063b2539b2e146104ef57610166565b80636144ef6d116101235780636144ef6d146102b757806367edbd6d146102d55780636f68a38e1461030157806375ce65191461033f5780637e38d3a31461036b57806381ea4408146103a957610166565b806315ed14111461016b57806319f88c7d146101a95780631d620e2a146101e657806343b82d46146102245780634c3ff83a1461024f5780634e7315831461027a575b600080fd5b34801561017757600080fd5b50610192600480360381019061018d91906119e7565b61067c565b6040516101a0929190611add565b60405180910390f35b3480156101b557600080fd5b506101d060048036038101906101cb9190611b6d565b6106f5565b6040516101dd9190611be6565b60405180910390f35b3480156101f257600080fd5b5061020d60048036038101906102089190611c01565b610729565b60405161021b929190611c2e565b60405180910390f35b34801561023057600080fd5b5061023961086b565b6040516102469190611be6565b60405180910390f35b34801561025b57600080fd5b50610264610983565b6040516102719190611be6565b60405180910390f35b34801561028657600080fd5b506102a1600480360381019061029c9190611c01565b610a9b565b6040516102ae9190611c57565b60405180910390f35b6102bf610b07565b6040516102cc9190611be6565b60405180910390f35b3480156102e157600080fd5b506102ea610c22565b6040516102f8929190611add565b60405180910390f35b34801561030d57600080fd5b5061032860048036038101906103239190611c01565b610cde565b604051610336929190611c2e565b60405180910390f35b34801561034b57600080fd5b50610354610e20565b604051610362929190611add565b60405180910390f35b34801561037757600080fd5b50610392600480360381019061038d91906119e7565b610eda565b6040516103a0929190611add565b60405180910390f35b3480156103b557600080fd5b506103d060048036038101906103cb9190611c01565b610f55565b6040516103dd9190611be6565b60405180910390f35b3480156103f257600080fd5b5061040d60048036038101906104089190611c01565b610f65565b60405161041b929190611c2e565b60405180910390f35b34801561043057600080fd5b5061044b600480360381019061044691906119e7565b6110a9565b604051610459929190611add565b60405180910390f35b61046a611122565b604051610478929190611add565b60405180910390f35b61049b600480360381019061049691906119e7565b6111df565b6040516104a9929190611add565b60405180910390f35b3480156104be57600080fd5b506104d960048036038101906104d49190611caf565b61125b565b6040516104e69190611ceb565b60405180910390f35b61050960048036038101906105049190611c01565b611382565b604051610517929190611c2e565b60405180910390f35b61053a60048036038101906105359190611caf565b6114c7565b6040516105479190611ceb565b60405180910390f35b34801561055c57600080fd5b5061057760048036038101906105729190611c01565b6115ef565b6040516105849190611ceb565b60405180910390f35b34801561059957600080fd5b506105a26115ff565b6040516105b0929190611add565b60405180910390f35b3480156105c557600080fd5b506105ce6116b9565b6040516105db9190611be6565b60405180910390f35b6105fe60048036038101906105f99190611b6d565b6117d3565b60405161060b9190611be6565b60405180910390f35b34801561062057600080fd5b50610629611806565b6040516106369190611d15565b60405180910390f35b34801561064b57600080fd5b5061066660048036038101906106619190611c01565b61180e565b6040516106739190611ceb565b60405180910390f35b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516106a59190611d6c565b600060405180830381855afa9150503d80600081146106e0576040519150601f19603f3d011682016040523d82523d6000602084013e6106e5565b606091505b5080925081935050509250929050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2815193505050509392505050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff16856040516024016107599190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516107e39190611d6c565b600060405180830381855afa9150503d806000811461081e576040519150601f19603f3d011682016040523d82523d6000602084013e610823565b606091505b5091509150811561086457600080828060200190518101906108459190611de8565b915060030b915060168260070b14955085801561085f5750805b945050505b5050915091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516109199190611d6c565b600060405180830381855af49150503d8060008114610954576040519150601f19603f3d011682016040523d82523d6000602084013e610959565b606091505b50915091508161096857600080fd5b8080602001905181019061097c9190611e54565b9250505090565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610a319190611d6c565b600060405180830381855afa9150503d8060008114610a6c576040519150601f19603f3d011682016040523d82523d6000602084013e610a71565b606091505b509150915081610a8057600080fd5b80806020019051810190610a949190611e54565b9250505090565b60606000823b905060008167ffffffffffffffff811115610abf57610abe6118bc565b5b6040519080825280601f01601f191660200182016040528015610af15781602001600182028036833780820191505090505b50905081600060208301863c8092505050919050565b600080600061016973ffffffffffffffffffffffffffffffffffffffff16346040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610bb69190611d6c565b60006040518083038185875af1925050503d8060008114610bf3576040519150601f19603f3d011682016040523d82523d6000602084013e610bf8565b606091505b509150915081610c0757600080fd5b80806020019051810190610c1b9190611e54565b9250505090565b600060606000604051602001610c3790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610c749190611c57565b604051602081830303815290604052604051610c909190611d6c565b6000604051808303816000865af19150503d8060008114610ccd576040519150601f19603f3d011682016040523d82523d6000602084013e610cd2565b606091505b50915091505050509091565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610d0e9190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610d989190611d6c565b600060405180830381855af49150503d8060008114610dd3576040519150601f19603f3d011682016040523d82523d6000602084013e610dd8565b606091505b50915091508115610e195760008082806020019051810190610dfa9190611de8565b915060030b915060168260070b149550858015610e145750805b945050505b5050915091565b600060606000604051602001610e3590611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1683604051602001610e729190611c57565b604051602081830303815290604052604051610e8e9190611d6c565b600060405180830381855af49150503d8060008114610ec9576040519150601f19603f3d011682016040523d82523d6000602084013e610ece565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1683604051610f039190611d6c565b6000604051808303816000865af19150503d8060008114610f40576040519150601f19603f3d011682016040523d82523d6000602084013e610f45565b606091505b5080925081935050509250929050565b600080823f905080915050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1685604051602401610f959190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161101f9190611d6c565b6000604051808303816000865af19150503d806000811461105c576040519150601f19603f3d011682016040523d82523d6000602084013e611061565b606091505b509150915081156110a257600080828060200190518101906110839190611de8565b915060030b915060168260070b14955085801561109d5750805b945050505b5050915091565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516110d29190611d6c565b600060405180830381855af49150503d806000811461110d576040519150601f19603f3d011682016040523d82523d6000602084013e611112565b606091505b5080925081935050509250929050565b60006060600060405160200161113790611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff1634846040516020016111759190611c57565b6040516020818303038152906040526040516111919190611d6c565b60006040518083038185875af1925050503d80600081146111ce576040519150601f19603f3d011682016040523d82523d6000602084013e6111d3565b606091505b50915091505050509091565b600060608373ffffffffffffffffffffffffffffffffffffffff1634846040516112099190611d6c565b60006040518083038185875af1925050503d8060008114611246576040519150601f19603f3d011682016040523d82523d6000602084013e61124b565b606091505b5080925081935050509250929050565b600080600061016873ffffffffffffffffffffffffffffffffffffffff168460405160240161128a9190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516113149190611d6c565b6000604051808303816000865af19150503d8060008114611351576040519150601f19603f3d011682016040523d82523d6000602084013e611356565b606091505b50915091508161136557600080fd5b808060200190518101906113799190611f13565b92505050919050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1634866040516024016113b39190611d15565b6040516020818303038152906040527f19f37361000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161143d9190611d6c565b60006040518083038185875af1925050503d806000811461147a576040519150601f19603f3d011682016040523d82523d6000602084013e61147f565b606091505b509150915081156114c057600080828060200190518101906114a19190611de8565b915060030b915060168260070b1495508580156114bb5750805b945050505b5050915091565b600080600061016873ffffffffffffffffffffffffffffffffffffffff1634856040516024016114f79190611ceb565b6040516020818303038152906040527f2e3cff6a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516115819190611d6c565b60006040518083038185875af1925050503d80600081146115be576040519150601f19603f3d011682016040523d82523d6000602084013e6115c3565b606091505b5091509150816115d257600080fd5b808060200190518101906115e69190611f13565b92505050919050565b600080823b905080915050919050565b60006060600060405160200161161490611ede565b6040516020818303038152906040529050600080600473ffffffffffffffffffffffffffffffffffffffff16836040516020016116519190611c57565b60405160208183030381529060405260405161166d9190611d6c565b600060405180830381855afa9150503d80600081146116a8576040519150601f19603f3d011682016040523d82523d6000602084013e6116ad565b606091505b50915091505050509091565b600080600061016973ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527fd83bf9a1000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516117679190611d6c565b6000604051808303816000865af19150503d80600081146117a4576040519150601f19603f3d011682016040523d82523d6000602084013e6117a9565b606091505b5091509150816117b857600080fd5b808060200190518101906117cc9190611e54565b9250505090565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2815193505050509392505050565b600030905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061186e82611843565b9050919050565b61187e81611863565b811461188957600080fd5b50565b60008135905061189b81611875565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6118f4826118ab565b810181811067ffffffffffffffff82111715611913576119126118bc565b5b80604052505050565b600061192661182f565b905061193282826118eb565b919050565b600067ffffffffffffffff821115611952576119516118bc565b5b61195b826118ab565b9050602081019050919050565b82818337600083830152505050565b600061198a61198584611937565b61191c565b9050828152602081018484840111156119a6576119a56118a6565b5b6119b1848285611968565b509392505050565b600082601f8301126119ce576119cd6118a1565b5b81356119de848260208601611977565b91505092915050565b600080604083850312156119fe576119fd611839565b5b6000611a0c8582860161188c565b925050602083013567ffffffffffffffff811115611a2d57611a2c61183e565b5b611a39858286016119b9565b9150509250929050565b60008115159050919050565b611a5881611a43565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611a98578082015181840152602081019050611a7d565b60008484015250505050565b6000611aaf82611a5e565b611ab98185611a69565b9350611ac9818560208601611a7a565b611ad2816118ab565b840191505092915050565b6000604082019050611af26000830185611a4f565b8181036020830152611b048184611aa4565b90509392505050565b600080fd5b600080fd5b60008083601f840112611b2d57611b2c6118a1565b5b8235905067ffffffffffffffff811115611b4a57611b49611b0d565b5b602083019150836001820283011115611b6657611b65611b12565b5b9250929050565b600080600060408486031215611b8657611b85611839565b5b6000611b948682870161188c565b935050602084013567ffffffffffffffff811115611bb557611bb461183e565b5b611bc186828701611b17565b92509250509250925092565b6000819050919050565b611be081611bcd565b82525050565b6000602082019050611bfb6000830184611bd7565b92915050565b600060208284031215611c1757611c16611839565b5b6000611c258482850161188c565b91505092915050565b6000604082019050611c436000830185611a4f565b611c506020830184611a4f565b9392505050565b60006020820190508181036000830152611c718184611aa4565b905092915050565b6000819050919050565b611c8c81611c79565b8114611c9757600080fd5b50565b600081359050611ca981611c83565b92915050565b600060208284031215611cc557611cc4611839565b5b6000611cd384828501611c9a565b91505092915050565b611ce581611c79565b82525050565b6000602082019050611d006000830184611cdc565b92915050565b611d0f81611863565b82525050565b6000602082019050611d2a6000830184611d06565b92915050565b600081905092915050565b6000611d4682611a5e565b611d508185611d30565b9350611d60818560208601611a7a565b80840191505092915050565b6000611d788284611d3b565b915081905092915050565b60008160030b9050919050565b611d9981611d83565b8114611da457600080fd5b50565b600081519050611db681611d90565b92915050565b611dc581611a43565b8114611dd057600080fd5b50565b600081519050611de281611dbc565b92915050565b60008060408385031215611dff57611dfe611839565b5b6000611e0d85828601611da7565b9250506020611e1e85828601611dd3565b9150509250929050565b611e3181611bcd565b8114611e3c57600080fd5b50565b600081519050611e4e81611e28565b92915050565b600060208284031215611e6a57611e69611839565b5b6000611e7884828501611e3f565b91505092915050565b600082825260208201905092915050565b7f48656c6c6f2c20576f726c640000000000000000000000000000000000000000600082015250565b6000611ec8600c83611e81565b9150611ed382611e92565b602082019050919050565b60006020820190508181036000830152611ef781611ebb565b9050919050565b600081519050611f0d81611c83565b92915050565b600060208284031215611f2957611f28611839565b5b6000611f3784828501611efe565b9150509291505056fea26469706673582212206bda6fdee0213387de2ad256148f515e0616c170d9d76630f24a35e4833729b364736f6c63430008130033", + "bytecode": "0x608060405234801561001057600080fd5b5061140e806100206000396000f3fe6080604052600436106101ac5760003560e01c806381ea4408116100ec578063b2a07c3e1161008a578063d83bf9a111610064578063d83bf9a11461044a578063e34c93b81461045f578063e717ea6114610472578063f8b2cb4f1461048d57600080fd5b8063b2a07c3e14610403578063b51c4f9614610416578063d558bd8e1461043557600080fd5b806388b0764a116100c657806388b0764a146103b557806391548228146103bd578063b0d869b1146103d0578063b2539b2e146103f057600080fd5b806381ea440814610356578063845b27cd146103755780638771074f1461039557600080fd5b80634e7315831161015957806367edbd6d1161013357806367edbd6d146102ec5780636f68a38e1461030157806375ce6519146103215780637e38d3a31461033657600080fd5b80634e73158314610297578063525c1362146102c45780636144ef6d146102e457600080fd5b8063310664501161018a578063310664501461024d57806343b82d461461026d5780634c3ff83a1461028257600080fd5b806315ed1411146101b157806319f88c7d146101e85780631d620e2a14610216575b600080fd5b3480156101bd57600080fd5b506101d16101cc366004611154565b6104b5565b6040516101df929190611272565b60405180910390f35b3480156101f457600080fd5b5061020861020336600461128d565b61051d565b6040519081526020016101df565b34801561022257600080fd5b50610236610231366004611310565b61054e565b6040805192151583529015156020830152016101df565b34801561025957600080fd5b50610208610268366004611332565b61062d565b34801561027957600080fd5b506102086106f1565b34801561028e57600080fd5b5061020861079a565b3480156102a357600080fd5b506102b76102b2366004611310565b610814565b6040516101df919061134b565b3480156102d057600080fd5b506102086102df366004611332565b610870565b610208610904565b3480156102f857600080fd5b506101d1610981565b34801561030d57600080fd5b5061023661031c366004611310565b610a4f565b34801561032d57600080fd5b506101d1610ae1565b34801561034257600080fd5b506101d1610351366004611154565b610b9c565b34801561036257600080fd5b50610208610371366004611310565b3f90565b34801561038157600080fd5b50610236610390366004611310565b610bf5565b3480156103a157600080fd5b506101d16103b0366004611154565b610c89565b6101d1610ce0565b6101d16103cb366004611154565b610d9e565b3480156103dc57600080fd5b506102086103eb366004611332565b610df8565b6102366103fe366004611310565b610e8e565b610208610411366004611332565b610f24565b34801561042257600080fd5b50610208610431366004611310565b3b90565b34801561044157600080fd5b506101d1610fbb565b34801561045657600080fd5b50610208611076565b61020861046d36600461128d565b6110f2565b34801561047e57600080fd5b506040513081526020016101df565b34801561049957600080fd5b506102086104a8366004611310565b6001600160a01b03163190565b60006060836001600160a01b0316836040516104d1919061135e565b600060405180830381855afa9150503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b606091505b50909590945092505050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2505195945050505050565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516105a5919061135e565b600060405180830381855afa9150503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b606091505b509150915081156106265760008082806020019051810190610607919061137a565b915060030b91508160070b60161495508580156106215750805b945050505b5050915091565b60008060006101686001600160a01b03168460405160240161065191815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610686919061135e565b600060405180830381855af49150503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b606091505b5091509150816106d557600080fd5b808060200190518101906106e991906113bf565b949350505050565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b17905290516000918291829161016991610730919061135e565b600060405180830381855af49150503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b606091505b50915091508161077f57600080fd5b8080602001905181019061079391906113bf565b9250505090565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b179052905160009182918291610169916107d9919061135e565b600060405180830381855afa9150503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b6060813b60008167ffffffffffffffff8111156108335761083361113e565b6040519080825280601f01601f19166020018201604052801561085d576020820181803683370190505b50905081600060208301863c9392505050565b60008060006101686001600160a01b03168460405160240161089491815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b179052516108c9919061135e565b600060405180830381855afa9150503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b1790529051600091829182916101699134916109449161135e565b60006040518083038185875af1925050503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b6000606060006040516020016109b7906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016109e7919061134b565b60408051601f1981840301815290829052610a019161135e565b6000604051808303816000865af19150503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b50915091505050509091565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610aa6919061135e565b600060405180830381855af49150503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b600060606000604051602001610b17906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001610b47919061134b565b60408051601f1981840301815290829052610b619161135e565b600060405180830381855af49150503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b60006060836001600160a01b031683604051610bb8919061135e565b6000604051808303816000865af19150503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610c4c919061135e565b6000604051808303816000865af19150503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b60006060836001600160a01b031683604051610ca5919061135e565b600060405180830381855af49150503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b600060606000604051602001610d16906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b03163484604051602001610d47919061134b565b60408051601f1981840301815290829052610d619161135e565b60006040518083038185875af1925050503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b60006060836001600160a01b03163484604051610dbb919061135e565b60006040518083038185875af1925050503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b60008060006101686001600160a01b031684604051602401610e1c91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610e51919061135e565b6000604051808303816000865af19150503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b6040516001600160a01b038216602482015260009081908190819061016790349060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610ee7919061135e565b60006040518083038185875af1925050503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b60008060006101686001600160a01b03163485604051602401610f4991815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610f7e919061135e565b60006040518083038185875af1925050503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b600060606000604051602001610ff1906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001611021919061134b565b60408051601f198184030181529082905261103b9161135e565b600060405180830381855afa9150503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b179052905160009182918291610169916110b5919061135e565b6000604051808303816000865af19150503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2505195945050505050565b80356001600160a01b038116811461113957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561116757600080fd5b61117083611122565b9150602083013567ffffffffffffffff8082111561118d57600080fd5b818501915085601f8301126111a157600080fd5b8135818111156111b3576111b361113e565b604051601f8201601f19908116603f011681019083821181831017156111db576111db61113e565b816040528281528860208487010111156111f457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b83811015611231578181015183820152602001611219565b83811115611240576000848401525b50505050565b6000815180845261125e816020860160208601611216565b601f01601f19169290920160200192915050565b82151581526040602082015260006106e96040830184611246565b6000806000604084860312156112a257600080fd5b6112ab84611122565b9250602084013567ffffffffffffffff808211156112c857600080fd5b818601915086601f8301126112dc57600080fd5b8135818111156112eb57600080fd5b8760208285010111156112fd57600080fd5b6020830194508093505050509250925092565b60006020828403121561132257600080fd5b61132b82611122565b9392505050565b60006020828403121561134457600080fd5b5035919050565b60208152600061132b6020830184611246565b60008251611370818460208701611216565b9190910192915050565b6000806040838503121561138d57600080fd5b82518060030b811461139e57600080fd5b602084015190925080151581146113b457600080fd5b809150509250929050565b6000602082840312156113d157600080fd5b505191905056fea2646970667358221220598be3cfa75797a8cbff2f91407ad6b3da1c4d876789a695d25fafcc3f2a7db764736f6c63430008090033", + "deployedBytecode": "0x6080604052600436106101ac5760003560e01c806381ea4408116100ec578063b2a07c3e1161008a578063d83bf9a111610064578063d83bf9a11461044a578063e34c93b81461045f578063e717ea6114610472578063f8b2cb4f1461048d57600080fd5b8063b2a07c3e14610403578063b51c4f9614610416578063d558bd8e1461043557600080fd5b806388b0764a116100c657806388b0764a146103b557806391548228146103bd578063b0d869b1146103d0578063b2539b2e146103f057600080fd5b806381ea440814610356578063845b27cd146103755780638771074f1461039557600080fd5b80634e7315831161015957806367edbd6d1161013357806367edbd6d146102ec5780636f68a38e1461030157806375ce6519146103215780637e38d3a31461033657600080fd5b80634e73158314610297578063525c1362146102c45780636144ef6d146102e457600080fd5b8063310664501161018a578063310664501461024d57806343b82d461461026d5780634c3ff83a1461028257600080fd5b806315ed1411146101b157806319f88c7d146101e85780631d620e2a14610216575b600080fd5b3480156101bd57600080fd5b506101d16101cc366004611154565b6104b5565b6040516101df929190611272565b60405180910390f35b3480156101f457600080fd5b5061020861020336600461128d565b61051d565b6040519081526020016101df565b34801561022257600080fd5b50610236610231366004611310565b61054e565b6040805192151583529015156020830152016101df565b34801561025957600080fd5b50610208610268366004611332565b61062d565b34801561027957600080fd5b506102086106f1565b34801561028e57600080fd5b5061020861079a565b3480156102a357600080fd5b506102b76102b2366004611310565b610814565b6040516101df919061134b565b3480156102d057600080fd5b506102086102df366004611332565b610870565b610208610904565b3480156102f857600080fd5b506101d1610981565b34801561030d57600080fd5b5061023661031c366004611310565b610a4f565b34801561032d57600080fd5b506101d1610ae1565b34801561034257600080fd5b506101d1610351366004611154565b610b9c565b34801561036257600080fd5b50610208610371366004611310565b3f90565b34801561038157600080fd5b50610236610390366004611310565b610bf5565b3480156103a157600080fd5b506101d16103b0366004611154565b610c89565b6101d1610ce0565b6101d16103cb366004611154565b610d9e565b3480156103dc57600080fd5b506102086103eb366004611332565b610df8565b6102366103fe366004611310565b610e8e565b610208610411366004611332565b610f24565b34801561042257600080fd5b50610208610431366004611310565b3b90565b34801561044157600080fd5b506101d1610fbb565b34801561045657600080fd5b50610208611076565b61020861046d36600461128d565b6110f2565b34801561047e57600080fd5b506040513081526020016101df565b34801561049957600080fd5b506102086104a8366004611310565b6001600160a01b03163190565b60006060836001600160a01b0316836040516104d1919061135e565b600060405180830381855afa9150503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b606091505b50909590945092505050565b60006040513681016040523684823760405160208101604052602081368460008a620dbba0f2505195945050505050565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b179052516105a5919061135e565b600060405180830381855afa9150503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b606091505b509150915081156106265760008082806020019051810190610607919061137a565b915060030b91508160070b60161495508580156106215750805b945050505b5050915091565b60008060006101686001600160a01b03168460405160240161065191815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610686919061135e565b600060405180830381855af49150503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b606091505b5091509150816106d557600080fd5b808060200190518101906106e991906113bf565b949350505050565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b17905290516000918291829161016991610730919061135e565b600060405180830381855af49150503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b606091505b50915091508161077f57600080fd5b8080602001905181019061079391906113bf565b9250505090565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b179052905160009182918291610169916107d9919061135e565b600060405180830381855afa9150503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b6060813b60008167ffffffffffffffff8111156108335761083361113e565b6040519080825280601f01601f19166020018201604052801561085d576020820181803683370190505b50905081600060208301863c9392505050565b60008060006101686001600160a01b03168460405160240161089491815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b179052516108c9919061135e565b600060405180830381855afa9150503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b1790529051600091829182916101699134916109449161135e565b60006040518083038185875af1925050503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b6000606060006040516020016109b7906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b0316836040516020016109e7919061134b565b60408051601f1981840301815290829052610a019161135e565b6000604051808303816000865af19150503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b50915091505050509091565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610aa6919061135e565b600060405180830381855af49150503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b600060606000604051602001610b17906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001610b47919061134b565b60408051601f1981840301815290829052610b619161135e565b600060405180830381855af49150503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b60006060836001600160a01b031683604051610bb8919061135e565b6000604051808303816000865af19150503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b6040516001600160a01b03821660248201526000908190819081906101679060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610c4c919061135e565b6000604051808303816000865af19150503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b60006060836001600160a01b031683604051610ca5919061135e565b600060405180830381855af49150503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b600060606000604051602001610d16906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b03163484604051602001610d47919061134b565b60408051601f1981840301815290829052610d619161135e565b60006040518083038185875af1925050503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b60006060836001600160a01b03163484604051610dbb919061135e565b60006040518083038185875af1925050503d806000811461050c576040519150601f19603f3d011682016040523d82523d6000602084013e610511565b60008060006101686001600160a01b031684604051602401610e1c91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610e51919061135e565b6000604051808303816000865af19150503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b6040516001600160a01b038216602482015260009081908190819061016790349060440160408051601f198184030181529181526020820180516001600160e01b03166319f3736160e01b17905251610ee7919061135e565b60006040518083038185875af1925050503d80600081146105e0576040519150601f19603f3d011682016040523d82523d6000602084013e6105e5565b60008060006101686001600160a01b03163485604051602401610f4991815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663171e7fb560e11b17905251610f7e919061135e565b60006040518083038185875af1925050503d80600081146106c1576040519150601f19603f3d011682016040523d82523d6000602084013e6106c6565b600060606000604051602001610ff1906020808252600c908201526b12195b1b1bcb0815dbdc9b1960a21b604082015260600190565b604051602081830303815290604052905060008060046001600160a01b031683604051602001611021919061134b565b60408051601f198184030181529082905261103b9161135e565b600060405180830381855afa9150503d8060008114610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b60408051600481526024810182526020810180516001600160e01b031663d83bf9a160e01b179052905160009182918291610169916110b5919061135e565b6000604051808303816000865af19150503d806000811461076b576040519150601f19603f3d011682016040523d82523d6000602084013e610770565b600060405136810160405236848237604051602081016040526020813684348a620dbba0f2505195945050505050565b80356001600160a01b038116811461113957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561116757600080fd5b61117083611122565b9150602083013567ffffffffffffffff8082111561118d57600080fd5b818501915085601f8301126111a157600080fd5b8135818111156111b3576111b361113e565b604051601f8201601f19908116603f011681019083821181831017156111db576111db61113e565b816040528281528860208487010111156111f457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b83811015611231578181015183820152602001611219565b83811115611240576000848401525b50505050565b6000815180845261125e816020860160208601611216565b601f01601f19169290920160200192915050565b82151581526040602082015260006106e96040830184611246565b6000806000604084860312156112a257600080fd5b6112ab84611122565b9250602084013567ffffffffffffffff808211156112c857600080fd5b818601915086601f8301126112dc57600080fd5b8135818111156112eb57600080fd5b8760208285010111156112fd57600080fd5b6020830194508093505050509250925092565b60006020828403121561132257600080fd5b61132b82611122565b9392505050565b60006020828403121561134457600080fd5b5035919050565b60208152600061132b6020830184611246565b60008251611370818460208701611216565b9190910192915050565b6000806040838503121561138d57600080fd5b82518060030b811461139e57600080fd5b602084015190925080151581146113b457600080fd5b809150509250929050565b6000602082840312156113d157600080fd5b505191905056fea2646970667358221220598be3cfa75797a8cbff2f91407ad6b3da1c4d876789a695d25fafcc3f2a7db764736f6c63430008090033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/packages/server/tests/contracts/EquivalenceContract.sol b/packages/server/tests/contracts/EquivalenceContract.sol index 06dbe5604c..97edaf4a7a 100644 --- a/packages/server/tests/contracts/EquivalenceContract.sol +++ b/packages/server/tests/contracts/EquivalenceContract.sol @@ -143,6 +143,20 @@ contract EquivalenceContract { tinybars = abi.decode(result, (uint256)); } + function exchangeRateStaticCall(uint256 tinycents) external view returns (uint256 tinybars) { + (bool success, bytes memory result) = EXCHANGE_RATE_PRECOMPILE_ADDRESS.staticcall( + abi.encodeWithSignature("tinycentsToTinybars(uint256)", tinycents)); + require(success); + tinybars = abi.decode(result, (uint256)); + } + + function exchangeRateDelegateCall(uint256 tinycents) external returns (uint256 tinybars) { + (bool success, bytes memory result) = EXCHANGE_RATE_PRECOMPILE_ADDRESS.delegatecall( + abi.encodeWithSignature("tinycentsToTinybars(uint256)", tinycents)); + require(success); + tinybars = abi.decode(result, (uint256)); + } + function htsCallWithoutAmount(address token) external returns (bool success, bool isToken) { (bool callSuccess, bytes memory result) = HTS_PRECOMPILE_ADDRESS.call( abi.encodeWithSignature("isToken(address)", token)); From 801e31bfcabb6d9da24d88000960d44d7bccb5e2 Mon Sep 17 00:00:00 2001 From: emilevgenievgeorgiev Date: Thu, 30 May 2024 17:53:05 +0300 Subject: [PATCH 10/14] Added and updated equivalence tests for internal calls Signed-off-by: emilevgenievgeorgiev --- packages/server/tests/acceptance/equivalence.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index eb8d3c5083..93b4452ed9 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -220,7 +220,7 @@ describe.only('Equivalence tests', async function () { switch (outcome) { case Outcomes.Error: { return `should fail ${addressName ? `executing ${addressName}` : ''}${ - errorMessage ? ` with ${errorMessage}` : '' + errorMessage ? `with ${errorMessage}` : '' }`; } case Outcomes.Output: { @@ -302,15 +302,15 @@ describe.only('Equivalence tests', async function () { const getContractFunctionParams = (hederaAddress: string): ContractFunctionParameters => { let params: ContractFunctionParameters; switch (hederaAddress) { - case '0.0.359': { + case ADDRESS_0_0_359: { params = new ContractFunctionParameters().addAddress(tokenAddress); break; } - case '0.0.360': { + case ADDRESS_0_0_360: { params = new ContractFunctionParameters().addUint256(100); break; } - case '0.0.361': { + case ADDRESS_0_0_361: { params = EMPTY_FUNCTION_PARAMS; break; } From 73aed43e8f37612068018f08634314acba5e6999 Mon Sep 17 00:00:00 2001 From: emilevgenievgeorgiev Date: Thu, 6 Jun 2024 12:01:06 +0300 Subject: [PATCH 11/14] Updated equivalence tests Signed-off-by: emilevgenievgeorgiev --- .../tests/acceptance/equivalence.spec.ts | 622 ++++++++++-------- .../acceptance/estimateGasPrecompile.spec.ts | 2 +- 2 files changed, 342 insertions(+), 282 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 93b4452ed9..e59f20e64a 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -30,7 +30,6 @@ import { Precheck } from '../../../relay/src/lib/precheck'; import pino from 'pino'; import { MirrorNodeClient } from '../../../relay/src/lib/clients'; import EquivalenceDestructContractJson from '../contracts/EquivalenceDestruct.json'; -import RelayClient from '../clients/relayClient'; import { hexToASCII } from '../../../relay/src/formatters'; const logger = pino(); @@ -50,6 +49,11 @@ const decodeResultData = (input: string) => { return hexToASCII(removeLeading0x(input)); }; +const getTransactionIdFromException = (error) => { + const idFromEx = error.transactionId.toString().split('@'); + return `${idFromEx[0]}-${idFromEx[1].replace('.', '-')}`; +}; + async function testRejection(errorMessage, method, checkMessage, thisObj, args?) { await expect(method.apply(thisObj, args), `${errorMessage}`).to.eventually.be.rejected.and.satisfy((err) => { return err.message.includes(errorMessage); @@ -67,12 +71,9 @@ describe.only('Equivalence tests', async function () { const STATUS_SUCCESS = '0x1'; const CONTRACT_EXECUTION_EXCEPTION = 'CONTRACT_EXECUTION_EXCEPTION'; const INVALID_FEE_SUBMITTED = 'INVALID_FEE_SUBMITTED'; - const INVALID_SOLIDITY_ADDRESS = 'INVALID_SOLIDITY_ADDRESS'; const CONTRACT_REVERT_EXECUTED = 'CONTRACT_REVERT_EXECUTED'; const prefix = '0x'; const ETH_PRECOMPILE_0x1 = '0.0.1'; - const ETH_PRECOMPILE_0x361 = '0.0.361'; - const ADDRESS_0x800 = '0.0.800'; const ETH_PRECOMPILE_0x1001 = '0.0.1001'; const NON_EXISTING_CONTRACT_ID = '0.0.564400'; const NON_EXISTING_FUNCTION = 'nxxixxkxxi'; @@ -104,13 +105,47 @@ describe.only('Equivalence tests', async function () { let estimateContract; let requestId; - const expectSuccessfulContractCall = (record) => { - expect(record.contract_id).to.equal( - equivalenceContractId, - 'Contract Id from record did not match with equivalence contract Id.', + const validateContractCall = ( + record, + expectedContractId = equivalenceContractId, + expectedResult = SUCCESS, + expectedStatus = STATUS_SUCCESS, + ) => { + expect(record.contract_id).to.equal(expectedContractId, "Record 'contract_id' was not as expected."); + expect(record.result).to.equal(expectedResult, "Record 'result' was not as expected."); + expect(record.status).to.equal(expectedStatus, "Record 'status' was not as expected."); + }; + + const validateContractActions = (contractAction, callType: CallTypes, outcome: Outcomes, message?: string) => { + const txLookup = `\nCheck transaction record by timestamp:'${contractAction.timestamp}'`; + expect(contractAction.call_operation_type).to.equal( + callType.toUpperCase(), + `Internal call type was not as expected`, ); - expect(record.result).to.equal(SUCCESS, 'Result from record was not as expected.'); - expect(record.status).to.equal(STATUS_SUCCESS, 'Status from record was not as expected'); + if (outcome === Outcomes.Error) { + expect(contractAction.result_data_type).to.equal( + Outcomes.Error, + `Expected "Error" but contract ${callType.toUpperCase()} was executed successfully.${txLookup}`, + ); + if (message) { + expect(decodeResultData(contractAction.result_data)).to.equal( + message, + `Error received was not as expected.${txLookup}`, + ); + } + } else if (outcome === Outcomes.Output) { + expect(contractAction.result_data_type).to.equal( + Outcomes.Output, + `Expected "${message === '0x' ? 'noop' : 'Output'}" but received error: "${decodeResultData( + contractAction.result_data, + )}".${txLookup}`, + ); + if (message) { + expect(contractAction.result_data).to.equal(message, `Output "result_data" was not as expected.${txLookup}`); + } + } else { + assert.fail(`Test outcome "${outcome}" is not handled in test code`); + } }; before(async function () { @@ -164,7 +199,7 @@ describe.only('Equivalence tests', async function () { precheck = new Precheck(mirrorNodeClient, logger, '0x12a'); }); - enum Opcodes { + enum CallTypes { Call = 'Call', StaticCall = 'StaticCall', DelegateCall = 'DelegateCall', @@ -180,7 +215,7 @@ describe.only('Equivalence tests', async function () { return amount === 0 ? 'without' : 'with'; }; - const getTestSummaryOutcome = (outcome: Outcomes, hederaAddress: string, errorMessage?: string): string => { + const getTestSummaryOutcome = (outcome: Outcomes, hederaAddress: string, message?: string): string => { let addressName: string; switch (hederaAddress) { case ADDRESS_0_0_1: { @@ -219,12 +254,21 @@ describe.only('Equivalence tests', async function () { switch (outcome) { case Outcomes.Error: { - return `should fail ${addressName ? `executing ${addressName}` : ''}${ - errorMessage ? `with ${errorMessage}` : '' - }`; + let result = 'should fail'; + if (addressName) { + result = result + ` executing ${addressName}`; + } + if (message) { + result = result + ` with ${message}`; + } + return result; } case Outcomes.Output: { - return addressName ? `should execute the ${addressName}` : 'should succeed'; + let result = addressName ? `should execute the ${addressName}` : 'should succeed'; + if (message) { + result = result + (message === '0x' ? ' with noop' : `with output "${message}"`); + } + return result; } default: { assert.fail(`Unsupported outcome: ${outcome} type provided in test data`); @@ -232,61 +276,61 @@ describe.only('Equivalence tests', async function () { } }; - const getFunctionName = (opcode: Opcodes, amount: number, hederaAddress: string): string => { + const getFunctionName = (callType: CallTypes, amount: number, hederaAddress: string): string => { let functionName = ''; const isWithAmount = amount > 0; switch (hederaAddress) { case ADDRESS_0_0_359: { - switch (opcode) { - case Opcodes.Call: { + switch (callType) { + case CallTypes.Call: { functionName = isWithAmount ? MAKE_HTS_CALL_WITH_AMOUNT : MAKE_HTS_CALL_WITHOUT_AMOUNT; break; } - case Opcodes.StaticCall: - case Opcodes.DelegateCall: { - functionName = `hts${opcode}`; + case CallTypes.StaticCall: + case CallTypes.DelegateCall: { + functionName = `hts${callType}`; break; } } break; } case ADDRESS_0_0_360: { - switch (opcode) { - case Opcodes.Call: { + switch (callType) { + case CallTypes.Call: { functionName = isWithAmount ? 'exchangeRateWithAmount' : 'exchangeRateWithoutAmount'; break; } - case Opcodes.StaticCall: - case Opcodes.DelegateCall: { - functionName = `exchangeRate${opcode}`; + case CallTypes.StaticCall: + case CallTypes.DelegateCall: { + functionName = `exchangeRate${callType}`; break; } } break; } case ADDRESS_0_0_361: { - switch (opcode) { - case Opcodes.Call: { + switch (callType) { + case CallTypes.Call: { functionName = isWithAmount ? 'getPseudorandomSeedWithAmount' : 'getPseudorandomSeed'; break; } - case Opcodes.StaticCall: - case Opcodes.DelegateCall: { - functionName = `getPseudorandomSeed${opcode}`; + case CallTypes.StaticCall: + case CallTypes.DelegateCall: { + functionName = `getPseudorandomSeed${callType}`; break; } } break; } default: { - switch (opcode) { - case Opcodes.Call: { + switch (callType) { + case CallTypes.Call: { functionName = isWithAmount ? MAKE_CALL_WITH_AMOUNT : MAKE_CALL_WITHOUT_AMOUNT; break; } - case Opcodes.StaticCall: - case Opcodes.DelegateCall: { - functionName = `make${opcode}`; + case CallTypes.StaticCall: + case CallTypes.DelegateCall: { + functionName = `make${callType}`; break; } } @@ -343,7 +387,7 @@ describe.only('Equivalence tests', async function () { accounts[0].wallet, ); const tx = await estimateContract.createFungibleTokenPublic(accounts[0].wallet.address, { - value: BigInt('10000000000000000000'), + value: BigInt('50000000000000000000'), gasLimit: 10_000_000, }); @@ -353,7 +397,7 @@ describe.only('Equivalence tests', async function () { return tokenAddress; } - it('should execute direct call to ethereum precompile 0x1', async function () { + it('direct CALL to ethereum precompile 0x1 should succeed', async function () { const { contractExecuteTimestamp } = await servicesClient.executeContractCall( ETH_PRECOMPILE_0x1, NON_EXISTING_FUNCTION, @@ -362,90 +406,143 @@ describe.only('Equivalence tests', async function () { ); const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1, contractExecuteTimestamp); + validateContractCall(record, ETH_PRECOMPILE_0x1, SUCCESS, STATUS_SUCCESS); - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + const contractActions = await getContractActions(record.hash); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output); }); - // EQV-003 - Should fail with INVALID_SOLIDIY_ADDRESS - // async function testRejection(errorMessage, method, checkMessage, thisObj, args?) - it('should execute direct call to address 361 to 750 without amount', async function () { - const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000]; - await testRejection(CONTRACT_EXECUTION_EXCEPTION, servicesNode.executeContractCall, true, servicesNode, args); + // Equivalence-003; Address 0.0.362 currently skipped because of ongoing issue being investigated + [/*'0.0.362',*/ '0.0.556', '0.0.750'].forEach((address) => { + it(`direct CALL to ethereum precompile ${address} without amount should succeed with noop`, async function () { + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCall( + address, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 500_000, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + return; + } + const record = await getResultByEntityIdAndTxTimestamp(address, contractCallResult.contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + }); }); - // EQV-004 - Should fail with INVALID_FEE_SUBMITTED - it('should execute direct call to ethereum precompile 361 to 750 with amount', async function () { - const args = [ETH_PRECOMPILE_0x361, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; - await testRejection(INVALID_FEE_SUBMITTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); + // Equivalence-004; Address 0.0.362 currently skipped because of ongoing issue being investigated + [, /*'0.0.362'*/ '0.0.556', '0.0.750'].forEach((address) => { + it(`direct CALL to ethereum precompile ${address} with amount should fail with INVALID_FEE_SUBMITTED`, async function () { + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCallWithAmount( + address, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 500_000, + 100, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Error, INVALID_FEE_SUBMITTED); + return; + } + const record = await getResultByEntityIdAndTxTimestamp(address, contractCallResult.contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Error, INVALID_FEE_SUBMITTED); + }); }); - // EQV-005 - OK - ??? Should it be like that? - Should it be successfull - it('should execute direct call to address 751 without amount', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - ADDRESS_0x800, - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - 500_000, - ); - - const record = await getResultByEntityIdAndTxTimestamp(ADDRESS_0x800, contractExecuteTimestamp); - - expect(record.contract_id).to.equal(ADDRESS_0x800); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); //Check if it should be CONTRACT_EXECUTION_EXCEPTION + // Equivalence-005 + ['0.0.751', '0.0.799', '0.0.800', '0.0.1000'].forEach((address) => { + it(`direct CALL to address ${address} without amount should succeed with noop`, async function () { + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCall( + address, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 500_000, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + return; + } + const record = await getResultByEntityIdAndTxTimestamp(address, contractCallResult.contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + }); }); - // EQV-005 - OK - ??? Should it be like that? - Should it be successfull - it('should execute direct call to address 751 with amount', async function () { - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( - ADDRESS_0x800, - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - 500_000, - 100, - ); - - const record = await getResultByEntityIdAndTxTimestamp(ADDRESS_0x800, contractExecuteTimestamp); - - expect(record.contract_id).to.equal(ADDRESS_0x800); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + // Equivalence-005; Address range [0.0.751-0.0.799] currently skipped because of ongoing issue being investigated + [/*'0.0.751', '0.0.799',*/ '0.0.800', '0.0.1000'].forEach((address) => { + it(`direct CALL to address ${address} with amount should succeed`, async function () { + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCallWithAmount( + address, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 500_000, + 100, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output); + return; + } + const record = await getResultByEntityIdAndTxTimestamp(address, contractCallResult.contractExecuteTimestamp); + const contractActions = await getContractActions(record.hash); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output); + }); }); // EQV-006 - should be successfull - OK - it('should execute direct call to address over 1000 without amount', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + it('direct CALL to address over 1000 without amount should succeed with noop', async function () { + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( ETH_PRECOMPILE_0x1001, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, ); const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); + validateContractCall(record, ETH_PRECOMPILE_0x1001, SUCCESS, STATUS_SUCCESS); - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1001); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + const contractActions = await getContractActions(record.hash); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); }); // EQV-006 - should be successfull - OK - it('should execute direct call to address over 1000 without amount - case contract ', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( - estimatePrecompileContractAddress, - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceDestructContractId, contractExecuteTimestamp); + // Skipped as test is not ready + it.skip('should execute direct call to address over 1000 without amount - case contract', async function () { + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCall( + estimatePrecompileContractAddress, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 1_000_000, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + assert.fail(`${e.message}\ncontact actions:\n${JSON.stringify(contractActions, null, 2)}`); + } - expect(record.contract_id).to.equal(equivalenceDestructContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + const record = await getResultByEntityIdAndTxTimestamp( + equivalenceDestructContractId, + contractCallResult.contractExecuteTimestamp, + ); + validateContractCall(record, equivalenceDestructContractId, SUCCESS, STATUS_SUCCESS); }); // EQV-007 - Should fail with INVALID_SOLIDIY_ADDRESS but it is SUCCESSFULL and creates Inactive EVM Address - ???????? - it('should execute direct call to non-existing contract', async function () { + // Skipped as test is not ready + it.skip('should execute direct call to non-existing contract', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( NON_EXISTING_CONTRACT_ID, NON_EXISTING_FUNCTION, @@ -454,25 +551,20 @@ describe.only('Equivalence tests', async function () { ); const record = await getResultByEntityIdAndTxTimestamp(NON_EXISTING_CONTRACT_ID, contractExecuteTimestamp); - - expect(record.contract_id).to.equal(NON_EXISTING_CONTRACT_ID); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + validateContractCall(record, NON_EXISTING_CONTRACT_ID, SUCCESS, STATUS_SUCCESS); }); // EQV-008 - should be successfull - OK - it('should execute direct call to address over 1000 without amount - case payable contract ', async function () { - const { contractExecuteTimestamp } = await servicesNode.executeContractCall( + // Skipped as test is not ready + it.skip('direct CALL to address over 1000 without amount (payable contract)', async function () { + const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceDestructContractId, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceDestructContractId, contractExecuteTimestamp); - - expect(record.contract_id).to.equal(equivalenceDestructContractId); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(STATUS_SUCCESS); + validateContractCall(record, equivalenceDestructContractId, SUCCESS, STATUS_SUCCESS); }); // EQV-009 - OK - should be unsuccessfull @@ -481,343 +573,311 @@ describe.only('Equivalence tests', async function () { await testRejection(CONTRACT_REVERT_EXECUTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); }); - it('should execute direct call to address over 1000 with amount', async function () { - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( - 'ETH_PRECOMPILE_0x1001', - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - 1_000_000, - 100, - ); - - const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); + // Skipped as test is not ready + it.skip('should execute direct call to address over 1000 with amount', async function () { + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCallWithAmount( + ETH_PRECOMPILE_0x1001, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 1_000_000, + 100, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + assert.fail(`${e.message}\ncontact actions:\n${JSON.stringify(contractActions, null, 2)}`); + } - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1001); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + const record = await getResultByEntityIdAndTxTimestamp( + ETH_PRECOMPILE_0x1001, + contractCallResult.contractExecuteTimestamp, + ); + validateContractCall(record, ETH_PRECOMPILE_0x1001, SUCCESS, CONTRACT_EXECUTION_EXCEPTION); }); // EQV-13 This should be successful due to hollow account creation - it('should execute direct call to address over 1000 with amount - case hollow acconut', async function () { + // Skipped as test is not ready + it.skip('should execute direct call to address over 1000 with amount - case hollow acconut', async function () { const hollowAccount = ethers.Wallet.createRandom(); + // convert evm address to hedera address const hollowAcAddress = hollowAccount.address.toString(); + let contractCallResult; + + try { + contractCallResult = await servicesClient.executeContractCallWithAmount( + hollowAcAddress, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + 1_000_000, + 100, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + assert.fail(`${e.message}\ncontact actions:\n${JSON.stringify(contractActions, null, 2)}`); + } - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( - hollowAcAddress, - NON_EXISTING_FUNCTION, - EMPTY_FUNCTION_PARAMS, - 1_000_000, - 100, + const record = await getResultByEntityIdAndTxTimestamp( + ETH_PRECOMPILE_0x1001, + contractCallResult.contractExecuteTimestamp, ); - - const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1001, contractExecuteTimestamp); - - expect(record.contract_id).to.equal(ETH_PRECOMPILE_0x1001); - expect(record.result).to.equal(SUCCESS); - expect(record.status).to.equal(CONTRACT_EXECUTION_EXCEPTION); + validateContractCall(record, ETH_PRECOMPILE_0x1001, SUCCESS, CONTRACT_EXECUTION_EXCEPTION); }); [ { // Equivalence 014, 040, 052 - opcodes: [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall], + callTypes: [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall], addresses: [ADDRESS_0_0_0], amount: 0, outcome: Outcomes.Output, - errorMessage: '', + message: '0x', }, { - // Equivalence 015 - opcodes: [Opcodes.Call], + // Equivalence-015 + callTypes: [CallTypes.Call], addresses: [ADDRESS_0_0_0], amount: 100, outcome: Outcomes.Error, - errorMessage: INVALID_FEE_SUBMITTED, + message: INVALID_FEE_SUBMITTED, }, { - // Equivalence 020 - opcodes: [Opcodes.Call], + // Equivalence-020 + callTypes: [CallTypes.Call], addresses: ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7', '0.0.8', '0.0.9'], amount: 100, outcome: Outcomes.Error, - errorMessage: INVALID_FEE_SUBMITTED, + message: INVALID_FEE_SUBMITTED, }, { - // Equivalence 022 - opcodes: [Opcodes.Call], - addresses: ['0.0.10', '0.0.100', '0.0.357'], + // Equivalence-022 + callTypes: [CallTypes.Call], + addresses: ['0.0.10', '0.0.100', '0.0.358'], amount: 100, outcome: Outcomes.Error, - errorMessage: INVALID_FEE_SUBMITTED, + message: INVALID_FEE_SUBMITTED, }, { - // Equivalence 021, 045, 057 - opcodes: [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall], - addresses: ['0.0.10', '0.0.100', '0.0.357'], + // Equivalence-021, 045, 057; Address 0.0.10 currently skipped because of ongoing issue being investigated + callTypes: [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall], + addresses: [/*'0.0.10',*/ '0.0.100', '0.0.358'], amount: 0, - outcome: Outcomes.Error, - errorMessage: INVALID_SOLIDITY_ADDRESS, + outcome: Outcomes.Output, + message: '0x', }, { - // Equivalence 030 - opcodes: [Opcodes.Call], - addresses: ['0.0.362', '0.0.556', '0.0.750'], + // Equivalence-030; Address 0.0.362 currently skipped because of ongoing issue being investigated + callTypes: [CallTypes.Call], + addresses: [/*'0.0.362',*/ '0.0.556', '0.0.750'], amount: 100, outcome: Outcomes.Error, - errorMessage: INVALID_FEE_SUBMITTED, + message: INVALID_FEE_SUBMITTED, }, { - // Equivalence 031 - Updated, same as mirror node, call to 751..799 fails with INVALID_ALIAS_KEY - // This should be verified, what is the behaviour? - opcodes: [Opcodes.Call], - addresses: ['0.0.751', '0.0.799', '0.0.800', '0.0.1000'], + // Equivalence 031; Address range [0.0.751-0.0.799] currently skipped because of ongoing issue being investigated + callTypes: [CallTypes.Call], + addresses: [/*'0.0.751', '0.0.799',*/ '0.0.800', '0.0.1000'], amount: 100, outcome: Outcomes.Output, - errorMessage: '', + message: '', }, { - // Equivalence 029, 049, 061 - opcodes: [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall], - addresses: ['0.0.362', '0.0.750', '0.0.751', '0.0.1000'], + // Equivalence-029, 049, 061; Address 0.0.362 currently skipped because of ongoing issue being investigated + callTypes: [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall], + addresses: [/*'0.0.362',*/ '0.0.750', '0.0.751', '0.0.1000'], amount: 0, - outcome: Outcomes.Error, - errorMessage: INVALID_SOLIDITY_ADDRESS, + outcome: Outcomes.Output, + message: '0x', }, ].forEach((test) => { - test.opcodes.forEach((opcode) => { + test.callTypes.forEach((callType) => { test.addresses.forEach((address) => { - it(`internal ${opcode.toUpperCase()} to address ${address} ${getTestSummaryAmount( + it(`internal ${callType.toUpperCase()} to address ${address} ${getTestSummaryAmount( test.amount, )} amount ${getTestSummaryOutcome( test.outcome, address, - test.errorMessage, + test.message, )} when called through an intermediary contract`, async function () { - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + getFunctionName(callType, test.amount, address), + getContractFunctionParams(address), + 500_000, + test.amount, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + validateContractActions(contractActions.actions[1], callType, test.outcome, test.message); + return; + } + + const record = await getResultByEntityIdAndTxTimestamp( equivalenceContractId, - getFunctionName(opcode, test.amount, address), - getContractFunctionParams(address), - 500_000, - test.amount, + contractCallResult.contractExecuteTimestamp, ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - expectSuccessfulContractCall(record); + validateContractCall(record); const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(childRecord.to).to.equal(Utils.idToEvmAddress(address)); - expect(childRecord.call_operation_type).to.equal(opcode.toUpperCase()); - - if (test.outcome === Outcomes.Error) { - expect(childRecord.result_data_type).to.equal( - Outcomes.Error, - 'Expected "Error" but contract call was executed successfully.', - ); - if (test.errorMessage) { - expect(decodeResultData(childRecord.result_data)).to.equal( - test.errorMessage, - 'Error received was not as expected.', - ); - } - } else if (test.outcome === Outcomes.Output) { - expect(childRecord.result_data_type).to.equal( - Outcomes.Output, - `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, - ); - } else { - assert.fail(`Test outcome "${test.outcome}" is not handled in test code`); - } + validateContractActions(contractActions.actions[1], callType, test.outcome, test.message); }); }); }); }); // Equivalence 016, 041, 053 - [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { - it(`internal ${opcode.toUpperCase()} to address 0.0.1 without amount with knowh hash and signature should execute the ecrecover precompile through the intermediary contract.`, async function () { + [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { + it(`internal ${callType.toUpperCase()} to address 0.0.1 without amount with knowh hash and signature should execute the ecrecover precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); const messageSignerAddress = '05fba803be258049a27b820088bab1cad2058871'; const hashedMessage = '0xf950ac8b7f08b2f5ffa0f893d0f85398135301759b768dc20c1e16d9cdba5b53000000000000000000000000000000000000000000000000000000000000001b45e5f9dc145b79479820a9dfa925bb698333e7f17b7d570391e8487c96a39e07675b682b2519f6232152a9f6f4f5923d171dfb7636daceee2c776edecc6c8b64'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(opcode, 0, ADDRESS_0_0_1), + getFunctionName(callType, 0, ADDRESS_0_0_1), new ContractFunctionParameters().addAddress(evmAddress).addBytes(precheck.hexToBytes(hashedMessage)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - expectSuccessfulContractCall(record); + validateContractCall(record); const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - Outcomes.Output, - `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, - ); + validateContractActions(childRecord, callType, Outcomes.Output); expect(childRecord.result_data).to.include(messageSignerAddress); }); }); // Equivalence 017, 042, 054 - [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { - it(`internal ${opcode.toUpperCase()} to address 0.0.2 without amount with knowh hash and signature should execute the SHA-256 precompile through the intermediary contract.`, async function () { + [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { + it(`internal ${callType.toUpperCase()} to address 0.0.2 without amount with knowh hash and signature should execute the SHA-256 precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); const message = 'Encode me!'; const hashedMessage = '0x68907fbd785a694c3617d35a6ce49477ac5704d75f0e727e353da7bc664aacc2'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(opcode, 0, ADDRESS_0_0_2), + getFunctionName(callType, 0, ADDRESS_0_0_2), new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - expectSuccessfulContractCall(record); + validateContractCall(record); const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - Outcomes.Output, - `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, - ); + validateContractActions(childRecord, callType, Outcomes.Output); expect(childRecord.result_data).to.equal(hashedMessage); }); }); // Equivalence 018, 043, 055 - [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { - it(`internal ${opcode.toUpperCase()} to address 0.0.3 without amount with knowh hash and signature should execute the RIPEMD-160 precompile through the intermediary contract.`, async function () { + [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { + it(`internal ${callType.toUpperCase()} to address 0.0.3 without amount with knowh hash and signature should execute the RIPEMD-160 precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); const message = 'Encode me!'; const hashedMessage = '4f0c39893f4c1c805aea87a95b5d359a218920d6'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(opcode, 0, ADDRESS_0_0_3), + getFunctionName(callType, 0, ADDRESS_0_0_3), new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - expectSuccessfulContractCall(record); + validateContractCall(record); const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - Outcomes.Output, - `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, - ); + validateContractActions(childRecord, callType, Outcomes.Output); expect(removeLeadingZeros(removeLeading0x(childRecord.result_data))).to.equal(hashedMessage); }); }); // Equivalence 019, 044, 056 - [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { - it(`internal ${opcode.toUpperCase()} to address 0.0.4 without amount with knowh hash and signature should execute the identity precompile through the intermediary contract.`, async function () { + [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { + it(`internal ${callType.toUpperCase()} to address 0.0.4 without amount with knowh hash and signature should execute the identity precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); const message = 'Encode me!'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(opcode, 0, ADDRESS_0_0_4), + getFunctionName(callType, 0, ADDRESS_0_0_4), new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), 500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - expectSuccessfulContractCall(record); + validateContractCall(record); const contractActions = await getContractActions(record.hash); const childRecord = contractActions.actions[1]; - expect(childRecord.call_type).to.equal('PRECOMPILE'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - Outcomes.Output, - `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, - ); + validateContractActions(childRecord, callType, Outcomes.Output); expect(decodeResultData(childRecord.result_data)).to.equal( message, - `Decoded 'result_data' from child record is not '${message}'`, + `Decoded 'result_data' from child record is not encoded message:'${message}'`, ); }); }); - // Equivalence 024, 027, 028 - [ADDRESS_0_0_359, ADDRESS_0_0_360, ADDRESS_0_0_361].forEach((address) => { + // Equivalence-024, 027, 028; Addresses 0.0.359 and 0.0.361 currently skipped because of ongoing issue being investigated + [/*ADDRESS_0_0_359,*/ ADDRESS_0_0_360 /*ADDRESS_0_0_361*/].forEach((address) => { it(`internal CALL to address ${address} with amount ${getTestSummaryOutcome( Outcomes.Error, address, INVALID_FEE_SUBMITTED, )} through the intermediary contract`, async function () { - const evmAddress = Utils.idToEvmAddress(address); - const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( + let contractCallResult; + try { + contractCallResult = await servicesClient.executeContractCallWithAmount( + equivalenceContractId, + getFunctionName(CallTypes.Call, 100, address), + getContractFunctionParams(address), + 500_000, + 100, + ); + } catch (e) { + const contractActions = await getContractActions(getTransactionIdFromException(e)); + validateContractActions(contractActions.actions[1], CallTypes.Call, Outcomes.Error, INVALID_FEE_SUBMITTED); + return; + } + + const record = await getResultByEntityIdAndTxTimestamp( equivalenceContractId, - getFunctionName(Opcodes.Call, 100, address), - getContractFunctionParams(address), - 500_000, - 100, + contractCallResult.contractExecuteTimestamp, ); - - const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - expectSuccessfulContractCall(record); - + validateContractCall(record); const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(childRecord.call_operation_type).to.equal(Opcodes.Call.toUpperCase()); - expect(childRecord.call_type).to.equal('SYSTEM'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - Outcomes.Error, - 'Expected "Error" but contract call was executed successfully.', - ); - expect(decodeResultData(childRecord.result_data)).to.equal( - INVALID_FEE_SUBMITTED, - 'Error received was not as expected.', - ); + validateContractActions(contractActions.actions[1], CallTypes.Call, Outcomes.Error, INVALID_FEE_SUBMITTED); }); }); - // Equivalence 023, 025, 026, 046, 047, 048, 058, 059, 060 - [Opcodes.Call, Opcodes.StaticCall, Opcodes.DelegateCall].forEach((opcode) => { - [ADDRESS_0_0_359, ADDRESS_0_0_360, ADDRESS_0_0_361].forEach((address) => { - it(`internal ${opcode.toUpperCase()} to address ${address} without amount ${getTestSummaryOutcome( + // Equivalence-023, 025, 026, 046, 047, 048, 058, 059, 060; Address 0.0.359 currently skipped because of ongoing issue being investigated + [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { + [/*ADDRESS_0_0_359,*/ ADDRESS_0_0_360, ADDRESS_0_0_361].forEach((address) => { + it(`internal ${callType.toUpperCase()} to address ${address} without amount ${getTestSummaryOutcome( Outcomes.Output, address, )} through the intermediary contract`, async function () { - const evmAddress = Utils.idToEvmAddress(address); const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( equivalenceContractId, - getFunctionName(opcode, 0, address), + getFunctionName(callType, 0, address), getContractFunctionParams(address), 500_000, 0, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); - expectSuccessfulContractCall(record); + validateContractCall(record); const contractActions = await getContractActions(record.hash); - const childRecord = contractActions.actions[1]; - - expect(childRecord.call_operation_type).to.equal(opcode.toUpperCase()); - expect(childRecord.call_type).to.equal('SYSTEM'); - expect(childRecord.to).to.equal(evmAddress); - expect(childRecord.result_data_type).to.equal( - Outcomes.Output, - `Expected "Output" but received error in child record: "${decodeResultData(childRecord.result_data)}".`, - ); + validateContractActions(contractActions.actions[1], callType, Outcomes.Output); }); }); }); diff --git a/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts b/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts index 00f5369e5d..28d13e4f81 100644 --- a/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts +++ b/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts @@ -74,7 +74,7 @@ describe('EstimatePrecompileContract tests', function () { accounts[0].wallet, ); const tx = await estimateContract.createFungibleTokenPublic(accounts[0].wallet.address, { - value: BigInt('10000000000000000000'), + value: BigInt('50000000000000000000'), gasLimit: 10_000_000, }); From a41cc8f0e789beef57a6226a78d586a44625368e Mon Sep 17 00:00:00 2001 From: emilevgenievgeorgiev Date: Thu, 6 Jun 2024 12:17:19 +0300 Subject: [PATCH 12/14] code cleanup Signed-off-by: emilevgenievgeorgiev --- packages/server/tests/acceptance/equivalence.spec.ts | 2 +- packages/server/tests/acceptance/index.spec.ts | 2 +- packages/server/tests/clients/servicesClient.ts | 4 ---- packages/server/tests/helpers/constants.ts | 1 - 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index e59f20e64a..7eab6f9bbf 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -60,7 +60,7 @@ async function testRejection(errorMessage, method, checkMessage, thisObj, args?) }); } -describe.only('Equivalence tests', async function () { +describe('Equivalence tests', async function () { const signers: AliasAccount[] = []; const { servicesNode, mirrorNode, relay }: any = global; const servicesClient = servicesNode as ServicesClient; diff --git a/packages/server/tests/acceptance/index.spec.ts b/packages/server/tests/acceptance/index.spec.ts index d520401bca..020427342e 100644 --- a/packages/server/tests/acceptance/index.spec.ts +++ b/packages/server/tests/acceptance/index.spec.ts @@ -85,7 +85,7 @@ describe('RPC Server Acceptance Tests', function () { logger.info(`E2E_RELAY_HOST: ${process.env.E2E_RELAY_HOST}`); if (global.relayIsLocal) { - // runLocalRelay(); + runLocalRelay(); } // cache start balance diff --git a/packages/server/tests/clients/servicesClient.ts b/packages/server/tests/clients/servicesClient.ts index ce978812bb..46759d1bae 100644 --- a/packages/server/tests/clients/servicesClient.ts +++ b/packages/server/tests/clients/servicesClient.ts @@ -241,10 +241,6 @@ export default class ServicesClient { .setFunction(functionName, params) .setTransactionMemo('Relay test contract execution'); - /* if (amount != 0) { - tx.setPayableAmount(Hbar.fromTinybars(amount)) - }*/ - const contractExecTransactionResponse = await this.executeTransaction(tx, requestId); // @ts-ignore diff --git a/packages/server/tests/helpers/constants.ts b/packages/server/tests/helpers/constants.ts index ecdc05fbdf..cb22986c35 100644 --- a/packages/server/tests/helpers/constants.ts +++ b/packages/server/tests/helpers/constants.ts @@ -167,7 +167,6 @@ const NON_EXISTING_BLOCK_HASH = '0x555555555555555555555555555555555555555555555 const NON_EXISTING_BLOCK_NUMBER = '0x5F5E0FF'; //99999999 const NON_EXISTING_INDEX = '0xF423F'; //999999 const ZERO_HEX = '0x0000000000000000000000000000000000000000'; -const ZERO_ADDRESS = '0.0.0.0'; const CALL_EXCEPTION = 'CALL_EXCEPTION'; From 6bd446121238f2464590294d698d26af355c0cc2 Mon Sep 17 00:00:00 2001 From: emilevgenievgeorgiev Date: Tue, 11 Jun 2024 17:23:11 +0300 Subject: [PATCH 13/14] updated equivalence.spec.ts tests after pr review remarks Signed-off-by: emilevgenievgeorgiev --- .../tests/acceptance/equivalence.spec.ts | 261 +++++++++--------- packages/server/tests/helpers/constants.ts | 3 + 2 files changed, 129 insertions(+), 135 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 7eab6f9bbf..f73646f7ea 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -29,12 +29,23 @@ import { ethers, toUtf8Bytes } from 'ethers'; import { Precheck } from '../../../relay/src/lib/precheck'; import pino from 'pino'; import { MirrorNodeClient } from '../../../relay/src/lib/clients'; -import EquivalenceDestructContractJson from '../contracts/EquivalenceDestruct.json'; import { hexToASCII } from '../../../relay/src/formatters'; const logger = pino(); +enum CallTypes { + Call = 'Call', + StaticCall = 'StaticCall', + DelegateCall = 'DelegateCall', + CallCode = 'CallCode', +} + +enum Outcomes { + Output = 'OUTPUT', + Error = 'ERROR', +} + const removeLeading0x = (input: string) => { - return input.startsWith('0x') ? input.replace('0x', '') : input; + return input.startsWith(Constants.EMPTY_HEX) ? input.replace(Constants.EMPTY_HEX, '') : input; }; const removeLeadingZeros = (input) => { @@ -60,8 +71,7 @@ async function testRejection(errorMessage, method, checkMessage, thisObj, args?) }); } -describe('Equivalence tests', async function () { - const signers: AliasAccount[] = []; +describe.only('Equivalence tests', async function () { const { servicesNode, mirrorNode, relay }: any = global; const servicesClient = servicesNode as ServicesClient; const mirrorNodeClient = mirrorNode as MirrorNodeClient; @@ -72,8 +82,6 @@ describe('Equivalence tests', async function () { const CONTRACT_EXECUTION_EXCEPTION = 'CONTRACT_EXECUTION_EXCEPTION'; const INVALID_FEE_SUBMITTED = 'INVALID_FEE_SUBMITTED'; const CONTRACT_REVERT_EXECUTED = 'CONTRACT_REVERT_EXECUTED'; - const prefix = '0x'; - const ETH_PRECOMPILE_0x1 = '0.0.1'; const ETH_PRECOMPILE_0x1001 = '0.0.1001'; const NON_EXISTING_CONTRACT_ID = '0.0.564400'; const NON_EXISTING_FUNCTION = 'nxxixxkxxi'; @@ -84,9 +92,24 @@ describe('Equivalence tests', async function () { const ADDRESS_0_0_2 = '0.0.2'; const ADDRESS_0_0_3 = '0.0.3'; const ADDRESS_0_0_4 = '0.0.4'; + const ADDRESS_0_0_5 = '0.0.5'; + const ADDRESS_0_0_6 = '0.0.6'; + const ADDRESS_0_0_7 = '0.0.7'; + const ADDRESS_0_0_8 = '0.0.8'; + const ADDRESS_0_0_9 = '0.0.9'; + const ADDRESS_0_0_10 = '0.0.10'; + const ADDRESS_0_0_100 = '0.0.100'; + const ADDRESS_0_0_358 = '0.0.358'; const ADDRESS_0_0_359 = '0.0.359'; const ADDRESS_0_0_360 = '0.0.360'; const ADDRESS_0_0_361 = '0.0.361'; + const ADDRESS_0_0_362 = '0.0.362'; + const ADDRESS_0_0_556 = '0.0.556'; + const ADDRESS_0_0_750 = '0.0.750'; + const ADDRESS_0_0_751 = '0.0.751'; + const ADDRESS_0_0_799 = '0.0.799'; + const ADDRESS_0_0_800 = '0.0.800'; + const ADDRESS_0_0_1000 = '0.0.1000'; const MAKE_CALL_WITHOUT_AMOUNT = 'makeCallWithoutAmount'; const MAKE_CALL_WITH_AMOUNT = 'makeCallWithAmount'; const MAKE_HTS_CALL_WITHOUT_AMOUNT = 'htsCallWithoutAmount'; @@ -98,10 +121,6 @@ describe('Equivalence tests', async function () { let estimatePrecompileSolidityAddress; let equivalenceContractReceipt; let equivalenceContractId; - let equivalenceContract; - let equivalenceContractSolidityAddress; - let equivalenceDestructContractId; - let equivalenceDestructContractReceipt; let estimateContract; let requestId; @@ -136,7 +155,7 @@ describe('Equivalence tests', async function () { } else if (outcome === Outcomes.Output) { expect(contractAction.result_data_type).to.equal( Outcomes.Output, - `Expected "${message === '0x' ? 'noop' : 'Output'}" but received error: "${decodeResultData( + `Expected "${message === Constants.EMPTY_HEX ? 'noop' : 'Output'}" but received error: "${decodeResultData( contractAction.result_data, )}".${txLookup}`, ); @@ -149,8 +168,6 @@ describe('Equivalence tests', async function () { }; before(async function () { - signers[0] = await servicesNode.createAliasAccount(150, relay.provider, Utils.generateRequestId()); - //Deploying Estimate Precompile contract estimatePrecompileContractReceipt = await servicesClient.deployContract( EstimatePrecompileContractJson, @@ -158,15 +175,6 @@ describe('Equivalence tests', async function () { ); estimatePrecompileContractAddress = estimatePrecompileContractReceipt.contractId.toString(); estimatePrecompileSolidityAddress = estimatePrecompileContractReceipt.contractId.toSolidityAddress(); - console.log(`estimatePrecompileContractAddress: ${estimatePrecompileContractAddress}`); - - //Deploying Equivalence Destrcut contract - equivalenceDestructContractReceipt = await servicesClient.deployContract( - EquivalenceDestructContractJson, - Constants.GAS_AS_NUMBER.LIMIT_5_000_000, - ); - equivalenceDestructContractId = equivalenceDestructContractReceipt.contractId.toString(); - console.log(`equivalenceDestructContractId: ${equivalenceDestructContractId}`); //Deploying Equivalence contract equivalenceContractReceipt = await servicesClient.deployContract( @@ -174,15 +182,6 @@ describe('Equivalence tests', async function () { Constants.GAS_AS_NUMBER.LIMIT_5_000_000, ); equivalenceContractId = equivalenceContractReceipt.contractId.toString(); - console.log(`equivalenceContractId: ${equivalenceContractId}`); - equivalenceContractSolidityAddress = equivalenceContractReceipt.contractId.toSolidityAddress(); - - equivalenceContract = new ethers.Contract( - prefix + equivalenceContractSolidityAddress, - EquivalenceContractJson.abi, - signers[0].wallet, - ); - console.log(`equivalenceContract:\n${JSON.stringify(equivalenceContract, null, 2)}`); requestId = Utils.generateRequestId(); const contractMirror = await mirrorNodeClient.get(`/contracts/${estimatePrecompileSolidityAddress}`, requestId); @@ -195,22 +194,9 @@ describe('Equivalence tests', async function () { ); tokenAddress = await createFungibleToken(); - console.log(`tokenAddress: ${tokenAddress}`); precheck = new Precheck(mirrorNodeClient, logger, '0x12a'); }); - enum CallTypes { - Call = 'Call', - StaticCall = 'StaticCall', - DelegateCall = 'DelegateCall', - CallCode = 'CallCode', - } - - enum Outcomes { - Output = 'OUTPUT', - Error = 'ERROR', - } - const getTestSummaryAmount = (amount: number): string => { return amount === 0 ? 'without' : 'with'; }; @@ -266,7 +252,7 @@ describe('Equivalence tests', async function () { case Outcomes.Output: { let result = addressName ? `should execute the ${addressName}` : 'should succeed'; if (message) { - result = result + (message === '0x' ? ' with noop' : `with output "${message}"`); + result = result + (message === Constants.EMPTY_HEX ? ' with noop' : `with output "${message}"`); } return result; } @@ -382,13 +368,13 @@ describe('Equivalence tests', async function () { async function createFungibleToken() { estimateContract = new ethers.Contract( - prefix + estimatePrecompileSolidityAddress, + Constants.EMPTY_HEX + estimatePrecompileSolidityAddress, EstimatePrecompileContractJson.abi, accounts[0].wallet, ); const tx = await estimateContract.createFungibleTokenPublic(accounts[0].wallet.address, { value: BigInt('50000000000000000000'), - gasLimit: 10_000_000, + gasLimit: Constants.GAS_AS_NUMBER.LIMIT_10_000_000, }); const tokenAddress = (await tx.wait()).logs.filter( @@ -399,21 +385,21 @@ describe('Equivalence tests', async function () { it('direct CALL to ethereum precompile 0x1 should succeed', async function () { const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - ETH_PRECOMPILE_0x1, + ADDRESS_0_0_1, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); - const record = await getResultByEntityIdAndTxTimestamp(ETH_PRECOMPILE_0x1, contractExecuteTimestamp); - validateContractCall(record, ETH_PRECOMPILE_0x1, SUCCESS, STATUS_SUCCESS); + const record = await getResultByEntityIdAndTxTimestamp(ADDRESS_0_0_1, contractExecuteTimestamp); + validateContractCall(record, ADDRESS_0_0_1, SUCCESS, STATUS_SUCCESS); const contractActions = await getContractActions(record.hash); validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output); }); - // Equivalence-003; Address 0.0.362 currently skipped because of ongoing issue being investigated - [/*'0.0.362',*/ '0.0.556', '0.0.750'].forEach((address) => { + // Address 0.0.362 currently skipped because of ongoing issue being investigated + [/*ADDRESS_0_0_362,*/ ADDRESS_0_0_556, ADDRESS_0_0_750].forEach((address) => { it(`direct CALL to ethereum precompile ${address} without amount should succeed with noop`, async function () { let contractCallResult; try { @@ -421,21 +407,21 @@ describe('Equivalence tests', async function () { address, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); - validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, Constants.EMPTY_HEX); return; } const record = await getResultByEntityIdAndTxTimestamp(address, contractCallResult.contractExecuteTimestamp); const contractActions = await getContractActions(record.hash); - validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, Constants.EMPTY_HEX); }); }); - // Equivalence-004; Address 0.0.362 currently skipped because of ongoing issue being investigated - [, /*'0.0.362'*/ '0.0.556', '0.0.750'].forEach((address) => { + // Address 0.0.362 currently skipped because of ongoing issue being investigated + [/*ADDRESS_0_0_362,*/ ADDRESS_0_0_556, ADDRESS_0_0_750].forEach((address) => { it(`direct CALL to ethereum precompile ${address} with amount should fail with INVALID_FEE_SUBMITTED`, async function () { let contractCallResult; try { @@ -443,8 +429,8 @@ describe('Equivalence tests', async function () { address, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 500_000, - 100, + Constants.GAS_AS_NUMBER.LIMIT_500_000, + Constants.AMOUNT.AMOUNT_100, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); @@ -457,8 +443,7 @@ describe('Equivalence tests', async function () { }); }); - // Equivalence-005 - ['0.0.751', '0.0.799', '0.0.800', '0.0.1000'].forEach((address) => { + [ADDRESS_0_0_751, ADDRESS_0_0_799, ADDRESS_0_0_800, ADDRESS_0_0_1000].forEach((address) => { it(`direct CALL to address ${address} without amount should succeed with noop`, async function () { let contractCallResult; try { @@ -466,21 +451,21 @@ describe('Equivalence tests', async function () { address, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); - validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, Constants.EMPTY_HEX); return; } const record = await getResultByEntityIdAndTxTimestamp(address, contractCallResult.contractExecuteTimestamp); const contractActions = await getContractActions(record.hash); - validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, Constants.EMPTY_HEX); }); }); - // Equivalence-005; Address range [0.0.751-0.0.799] currently skipped because of ongoing issue being investigated - [/*'0.0.751', '0.0.799',*/ '0.0.800', '0.0.1000'].forEach((address) => { + // Address range [0.0.751-0.0.799] currently skipped because of ongoing issue being investigated + [/*ADDRESS_0_0_751, ADDRESS_0_0_799,*/ ADDRESS_0_0_800, ADDRESS_0_0_1000].forEach((address) => { it(`direct CALL to address ${address} with amount should succeed`, async function () { let contractCallResult; try { @@ -488,8 +473,8 @@ describe('Equivalence tests', async function () { address, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 500_000, - 100, + Constants.GAS_AS_NUMBER.LIMIT_500_000, + Constants.AMOUNT.AMOUNT_100, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); @@ -502,7 +487,6 @@ describe('Equivalence tests', async function () { }); }); - // EQV-006 - should be successfull - OK it('direct CALL to address over 1000 without amount should succeed with noop', async function () { const { contractExecuteTimestamp } = await servicesClient.executeContractCall( ETH_PRECOMPILE_0x1001, @@ -514,10 +498,9 @@ describe('Equivalence tests', async function () { validateContractCall(record, ETH_PRECOMPILE_0x1001, SUCCESS, STATUS_SUCCESS); const contractActions = await getContractActions(record.hash); - validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, '0x'); + validateContractActions(contractActions.actions[0], CallTypes.Call, Outcomes.Output, Constants.EMPTY_HEX); }); - // EQV-006 - should be successfull - OK // Skipped as test is not ready it.skip('should execute direct call to address over 1000 without amount - case contract', async function () { let contractCallResult; @@ -526,7 +509,7 @@ describe('Equivalence tests', async function () { estimatePrecompileContractAddress, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 1_000_000, + Constants.GAS_AS_NUMBER.LIMIT_1_000_000, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); @@ -534,42 +517,45 @@ describe('Equivalence tests', async function () { } const record = await getResultByEntityIdAndTxTimestamp( - equivalenceDestructContractId, + estimatePrecompileContractAddress, contractCallResult.contractExecuteTimestamp, ); - validateContractCall(record, equivalenceDestructContractId, SUCCESS, STATUS_SUCCESS); + validateContractCall(record, estimatePrecompileContractAddress, SUCCESS, STATUS_SUCCESS); }); - // EQV-007 - Should fail with INVALID_SOLIDIY_ADDRESS but it is SUCCESSFULL and creates Inactive EVM Address - ???????? // Skipped as test is not ready it.skip('should execute direct call to non-existing contract', async function () { const { contractExecuteTimestamp } = await servicesNode.executeContractCall( NON_EXISTING_CONTRACT_ID, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); const record = await getResultByEntityIdAndTxTimestamp(NON_EXISTING_CONTRACT_ID, contractExecuteTimestamp); validateContractCall(record, NON_EXISTING_CONTRACT_ID, SUCCESS, STATUS_SUCCESS); }); - // EQV-008 - should be successfull - OK // Skipped as test is not ready it.skip('direct CALL to address over 1000 without amount (payable contract)', async function () { const { contractExecuteTimestamp } = await servicesClient.executeContractCall( - equivalenceDestructContractId, + equivalenceContractId, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, ); - const record = await getResultByEntityIdAndTxTimestamp(equivalenceDestructContractId, contractExecuteTimestamp); - validateContractCall(record, equivalenceDestructContractId, SUCCESS, STATUS_SUCCESS); + const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); + validateContractCall(record, equivalenceContractId, SUCCESS, STATUS_SUCCESS); }); - // EQV-009 - OK - should be unsuccessfull it('should execute direct call to address over 1000 with amount - case non-payable contract ', async function () { - const args = [estimatePrecompileContractAddress, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, 500_000, 100]; + const args = [ + estimatePrecompileContractAddress, + NON_EXISTING_FUNCTION, + EMPTY_FUNCTION_PARAMS, + Constants.GAS_AS_NUMBER.LIMIT_500_000, + 100, + ]; await testRejection(CONTRACT_REVERT_EXECUTED, servicesNode.executeContractCallWithAmount, true, servicesNode, args); }); @@ -581,8 +567,8 @@ describe('Equivalence tests', async function () { ETH_PRECOMPILE_0x1001, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 1_000_000, - 100, + Constants.GAS_AS_NUMBER.LIMIT_1_000_000, + Constants.AMOUNT.AMOUNT_100, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); @@ -596,7 +582,6 @@ describe('Equivalence tests', async function () { validateContractCall(record, ETH_PRECOMPILE_0x1001, SUCCESS, CONTRACT_EXECUTION_EXCEPTION); }); - // EQV-13 This should be successful due to hollow account creation // Skipped as test is not ready it.skip('should execute direct call to address over 1000 with amount - case hollow acconut', async function () { const hollowAccount = ethers.Wallet.createRandom(); @@ -609,8 +594,8 @@ describe('Equivalence tests', async function () { hollowAcAddress, NON_EXISTING_FUNCTION, EMPTY_FUNCTION_PARAMS, - 1_000_000, - 100, + Constants.GAS_AS_NUMBER.LIMIT_1_000_000, + Constants.AMOUNT.AMOUNT_100, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); @@ -624,72 +609,81 @@ describe('Equivalence tests', async function () { validateContractCall(record, ETH_PRECOMPILE_0x1001, SUCCESS, CONTRACT_EXECUTION_EXCEPTION); }); - [ + const basicInternalCallTests = [ { // Equivalence 014, 040, 052 callTypes: [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall], addresses: [ADDRESS_0_0_0], - amount: 0, + amount: Constants.AMOUNT.AMOUNT_0, outcome: Outcomes.Output, - message: '0x', + message: Constants.EMPTY_HEX, }, { - // Equivalence-015 callTypes: [CallTypes.Call], addresses: [ADDRESS_0_0_0], - amount: 100, + amount: Constants.AMOUNT.AMOUNT_100, outcome: Outcomes.Error, message: INVALID_FEE_SUBMITTED, }, { - // Equivalence-020 callTypes: [CallTypes.Call], - addresses: ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7', '0.0.8', '0.0.9'], - amount: 100, + addresses: [ + ADDRESS_0_0_1, + ADDRESS_0_0_2, + ADDRESS_0_0_3, + ADDRESS_0_0_4, + ADDRESS_0_0_5, + ADDRESS_0_0_6, + ADDRESS_0_0_7, + ADDRESS_0_0_8, + ADDRESS_0_0_9, + ], + amount: Constants.AMOUNT.AMOUNT_100, outcome: Outcomes.Error, message: INVALID_FEE_SUBMITTED, }, { - // Equivalence-022 callTypes: [CallTypes.Call], - addresses: ['0.0.10', '0.0.100', '0.0.358'], - amount: 100, + addresses: [ADDRESS_0_0_10, ADDRESS_0_0_100, ADDRESS_0_0_358], + amount: Constants.AMOUNT.AMOUNT_100, outcome: Outcomes.Error, message: INVALID_FEE_SUBMITTED, }, { - // Equivalence-021, 045, 057; Address 0.0.10 currently skipped because of ongoing issue being investigated + // Address 0.0.10 currently skipped because of ongoing issue being investigated callTypes: [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall], - addresses: [/*'0.0.10',*/ '0.0.100', '0.0.358'], - amount: 0, + addresses: [/*ADDRESS_0_0_10,*/ ADDRESS_0_0_100, ADDRESS_0_0_358], + amount: Constants.AMOUNT.AMOUNT_0, outcome: Outcomes.Output, - message: '0x', + message: Constants.EMPTY_HEX, }, { - // Equivalence-030; Address 0.0.362 currently skipped because of ongoing issue being investigated + // Address 0.0.362 currently skipped because of ongoing issue being investigated callTypes: [CallTypes.Call], - addresses: [/*'0.0.362',*/ '0.0.556', '0.0.750'], - amount: 100, + addresses: [/*ADDRESS_0_0_362,*/ ADDRESS_0_0_556, ADDRESS_0_0_750], + amount: Constants.AMOUNT.AMOUNT_100, outcome: Outcomes.Error, message: INVALID_FEE_SUBMITTED, }, { - // Equivalence 031; Address range [0.0.751-0.0.799] currently skipped because of ongoing issue being investigated + // Address range [0.0.751-0.0.799] currently skipped because of ongoing issue being investigated callTypes: [CallTypes.Call], - addresses: [/*'0.0.751', '0.0.799',*/ '0.0.800', '0.0.1000'], - amount: 100, + addresses: [/*ADDRESS_0_0_751, ADDRESS_0_0_799,*/ ADDRESS_0_0_800, ADDRESS_0_0_1000], + amount: Constants.AMOUNT.AMOUNT_100, outcome: Outcomes.Output, message: '', }, { - // Equivalence-029, 049, 061; Address 0.0.362 currently skipped because of ongoing issue being investigated + // Address 0.0.362 currently skipped because of ongoing issue being investigated callTypes: [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall], - addresses: [/*'0.0.362',*/ '0.0.750', '0.0.751', '0.0.1000'], - amount: 0, + addresses: [/*ADDRESS_0_0_362,*/ ADDRESS_0_0_750, ADDRESS_0_0_751, ADDRESS_0_0_1000], + amount: Constants.AMOUNT.AMOUNT_0, outcome: Outcomes.Output, - message: '0x', + message: Constants.EMPTY_HEX, }, - ].forEach((test) => { + ]; + + basicInternalCallTests.forEach((test) => { test.callTypes.forEach((callType) => { test.addresses.forEach((address) => { it(`internal ${callType.toUpperCase()} to address ${address} ${getTestSummaryAmount( @@ -705,7 +699,7 @@ describe('Equivalence tests', async function () { equivalenceContractId, getFunctionName(callType, test.amount, address), getContractFunctionParams(address), - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, test.amount, ); } catch (e) { @@ -727,7 +721,6 @@ describe('Equivalence tests', async function () { }); }); - // Equivalence 016, 041, 053 [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { it(`internal ${callType.toUpperCase()} to address 0.0.1 without amount with knowh hash and signature should execute the ecrecover precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_1); @@ -736,9 +729,9 @@ describe('Equivalence tests', async function () { '0xf950ac8b7f08b2f5ffa0f893d0f85398135301759b768dc20c1e16d9cdba5b53000000000000000000000000000000000000000000000000000000000000001b45e5f9dc145b79479820a9dfa925bb698333e7f17b7d570391e8487c96a39e07675b682b2519f6232152a9f6f4f5923d171dfb7636daceee2c776edecc6c8b64'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(callType, 0, ADDRESS_0_0_1), + getFunctionName(callType, Constants.AMOUNT.AMOUNT_0, ADDRESS_0_0_1), new ContractFunctionParameters().addAddress(evmAddress).addBytes(precheck.hexToBytes(hashedMessage)), - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); @@ -752,7 +745,6 @@ describe('Equivalence tests', async function () { }); }); - // Equivalence 017, 042, 054 [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { it(`internal ${callType.toUpperCase()} to address 0.0.2 without amount with knowh hash and signature should execute the SHA-256 precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_2); @@ -760,9 +752,9 @@ describe('Equivalence tests', async function () { const hashedMessage = '0x68907fbd785a694c3617d35a6ce49477ac5704d75f0e727e353da7bc664aacc2'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(callType, 0, ADDRESS_0_0_2), + getFunctionName(callType, Constants.AMOUNT.AMOUNT_0, ADDRESS_0_0_2), new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); @@ -776,7 +768,6 @@ describe('Equivalence tests', async function () { }); }); - // Equivalence 018, 043, 055 [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { it(`internal ${callType.toUpperCase()} to address 0.0.3 without amount with knowh hash and signature should execute the RIPEMD-160 precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_3); @@ -784,9 +775,9 @@ describe('Equivalence tests', async function () { const hashedMessage = '4f0c39893f4c1c805aea87a95b5d359a218920d6'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(callType, 0, ADDRESS_0_0_3), + getFunctionName(callType, Constants.AMOUNT.AMOUNT_0, ADDRESS_0_0_3), new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); @@ -800,16 +791,15 @@ describe('Equivalence tests', async function () { }); }); - // Equivalence 019, 044, 056 [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { it(`internal ${callType.toUpperCase()} to address 0.0.4 without amount with knowh hash and signature should execute the identity precompile through the intermediary contract.`, async function () { const evmAddress = Utils.idToEvmAddress(ADDRESS_0_0_4); const message = 'Encode me!'; const { contractExecuteTimestamp } = await servicesClient.executeContractCall( equivalenceContractId, - getFunctionName(callType, 0, ADDRESS_0_0_4), + getFunctionName(callType, Constants.AMOUNT.AMOUNT_0, ADDRESS_0_0_4), new ContractFunctionParameters().addAddress(evmAddress).addBytes(toUtf8Bytes(message)), - 500_000, + Constants.GAS_AS_NUMBER.LIMIT_500_000, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); @@ -826,7 +816,7 @@ describe('Equivalence tests', async function () { }); }); - // Equivalence-024, 027, 028; Addresses 0.0.359 and 0.0.361 currently skipped because of ongoing issue being investigated + // Addresses 0.0.359 and 0.0.361 currently skipped because of ongoing issue being investigated [/*ADDRESS_0_0_359,*/ ADDRESS_0_0_360 /*ADDRESS_0_0_361*/].forEach((address) => { it(`internal CALL to address ${address} with amount ${getTestSummaryOutcome( Outcomes.Error, @@ -837,10 +827,10 @@ describe('Equivalence tests', async function () { try { contractCallResult = await servicesClient.executeContractCallWithAmount( equivalenceContractId, - getFunctionName(CallTypes.Call, 100, address), + getFunctionName(CallTypes.Call, Constants.AMOUNT.AMOUNT_100, address), getContractFunctionParams(address), - 500_000, - 100, + Constants.GAS_AS_NUMBER.LIMIT_500_000, + Constants.AMOUNT.AMOUNT_100, ); } catch (e) { const contractActions = await getContractActions(getTransactionIdFromException(e)); @@ -853,12 +843,13 @@ describe('Equivalence tests', async function () { contractCallResult.contractExecuteTimestamp, ); validateContractCall(record); + const contractActions = await getContractActions(record.hash); validateContractActions(contractActions.actions[1], CallTypes.Call, Outcomes.Error, INVALID_FEE_SUBMITTED); }); }); - // Equivalence-023, 025, 026, 046, 047, 048, 058, 059, 060; Address 0.0.359 currently skipped because of ongoing issue being investigated + // Address 0.0.359 currently skipped because of ongoing issue being investigated [CallTypes.Call, CallTypes.StaticCall, CallTypes.DelegateCall].forEach((callType) => { [/*ADDRESS_0_0_359,*/ ADDRESS_0_0_360, ADDRESS_0_0_361].forEach((address) => { it(`internal ${callType.toUpperCase()} to address ${address} without amount ${getTestSummaryOutcome( @@ -867,10 +858,10 @@ describe('Equivalence tests', async function () { )} through the intermediary contract`, async function () { const { contractExecuteTimestamp } = await servicesClient.executeContractCallWithAmount( equivalenceContractId, - getFunctionName(callType, 0, address), + getFunctionName(callType, Constants.AMOUNT.AMOUNT_0, address), getContractFunctionParams(address), - 500_000, - 0, + Constants.GAS_AS_NUMBER.LIMIT_500_000, + Constants.AMOUNT.AMOUNT_0, ); const record = await getResultByEntityIdAndTxTimestamp(equivalenceContractId, contractExecuteTimestamp); diff --git a/packages/server/tests/helpers/constants.ts b/packages/server/tests/helpers/constants.ts index cb22986c35..e5dff0cd46 100644 --- a/packages/server/tests/helpers/constants.ts +++ b/packages/server/tests/helpers/constants.ts @@ -119,6 +119,7 @@ const GAS_AS_NUMBER = { }; const AMOUNT = { + AMOUNT_0: 0, AMOUNT_1: 1, AMOUNT_10: 10, AMOUNT_100: 100, @@ -167,6 +168,7 @@ const NON_EXISTING_BLOCK_HASH = '0x555555555555555555555555555555555555555555555 const NON_EXISTING_BLOCK_NUMBER = '0x5F5E0FF'; //99999999 const NON_EXISTING_INDEX = '0xF423F'; //999999 const ZERO_HEX = '0x0000000000000000000000000000000000000000'; +const EMPTY_HEX = '0x'; const CALL_EXCEPTION = 'CALL_EXCEPTION'; @@ -180,6 +182,7 @@ export default { NON_EXISTING_BLOCK_NUMBER, NON_EXISTING_INDEX, ZERO_HEX, + EMPTY_HEX, CALL_EXCEPTION, GAS_AS_NUMBER, AMOUNT, From 681b0726806bfe0650928820341c8be5e01cdf89 Mon Sep 17 00:00:00 2001 From: emilevgenievgeorgiev Date: Tue, 11 Jun 2024 18:08:09 +0300 Subject: [PATCH 14/14] fixed imports and removed describe.only in equivalence.spec.ts Signed-off-by: emilevgenievgeorgiev --- packages/server/tests/acceptance/equivalence.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index f73646f7ea..99ec7b04d4 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -20,7 +20,7 @@ import { assert, expect } from 'chai'; import { Utils } from '../helpers/utils'; -import ServicesClient, { AliasAccount } from '../clients/servicesClient'; +import ServicesClient from '../clients/servicesClient'; import { ContractFunctionParameters } from '@hashgraph/sdk'; import EstimatePrecompileContractJson from '../contracts/EstimatePrecompileContract.json'; import Constants from '../helpers/constants'; @@ -30,6 +30,7 @@ import { Precheck } from '../../../relay/src/lib/precheck'; import pino from 'pino'; import { MirrorNodeClient } from '../../../relay/src/lib/clients'; import { hexToASCII } from '../../../relay/src/formatters'; +import { AliasAccount } from '../types/AliasAccount'; const logger = pino(); enum CallTypes { @@ -71,7 +72,7 @@ async function testRejection(errorMessage, method, checkMessage, thisObj, args?) }); } -describe.only('Equivalence tests', async function () { +describe('Equivalence tests', async function () { const { servicesNode, mirrorNode, relay }: any = global; const servicesClient = servicesNode as ServicesClient; const mirrorNodeClient = mirrorNode as MirrorNodeClient;