Skip to content

Commit

Permalink
Refactor ContractDebugServiceTest dynamic calls, nested calls enum (#…
Browse files Browse the repository at this point in the history
…9179)

This pr refactors ContractDebugServiceTest. Migrated tests from ContractDebugServiceTest that uses DynamicCallsContractFunctions. Test were migrated into ContractCallDynamicCallsTests as it also tests DynamicCallsContractFunctions. We just add the opcode assertions in the already existing tests there since they use the same setup. This also decreases the time for all tests to run. Moved NestedEthCallContractFunctionsNegativeCases to the nested calls test classes and just added opcode tracer assertions in the specified test.

This PR modifies:
ContractDebugServiceTest - removed ContractDebugServiceTest function.
ContractCallDynamicCallsTests - added debug trace assertions in the tests testing dynamic calls functions - uses exactly the same function of the removed evmDynamicCallsTokenFunctions.
ContractCallNestedCallsTest - added debug trace assertions in the tests.
ContractCallNestedCallsHistoricalTest - added debug trace assertions in the tests.
Deleted ContractDebugServiceTests as all test cases are moved to the above mentioned classes.

This pr is related to #8944 but there will be a couple of prs more in future before we can close this task.

---------

Signed-off-by: Kristiyan Selveliev <[email protected]>
  • Loading branch information
kselveliev authored Aug 30, 2024
1 parent 764b172 commit df09959
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 746 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright (C) 2024 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.
*/

package com.hedera.mirror.web3.service;

import static com.hedera.mirror.web3.utils.OpcodeTracerUtil.OPTIONS;
import static com.hedera.mirror.web3.utils.OpcodeTracerUtil.gasComparator;
import static com.hedera.mirror.web3.utils.OpcodeTracerUtil.toHumanReadableMessage;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.doAnswer;

import com.hedera.mirror.web3.common.ContractCallContext;
import com.hedera.mirror.web3.convert.BytesDecoder;
import com.hedera.mirror.web3.evm.contracts.execution.OpcodesProcessingResult;
import com.hedera.mirror.web3.service.model.ContractDebugParameters;
import com.hedera.mirror.web3.utils.ContractFunctionProviderRecord;
import com.hedera.node.app.service.evm.contracts.execution.HederaEvmTransactionProcessingResult;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.web3j.tx.Contract;

abstract class AbstractContractCallServiceOpcodeTracerTest extends AbstractContractCallServiceTest {

@Resource
protected ContractDebugService contractDebugService;

@Captor
private ArgumentCaptor<ContractDebugParameters> paramsCaptor;

@Captor
private ArgumentCaptor<Long> gasCaptor;

private HederaEvmTransactionProcessingResult resultCaptor;
private ContractCallContext contextCaptor;

@BeforeEach
void setUpArgumentCaptors() {
doAnswer(invocation -> {
final var transactionProcessingResult =
(HederaEvmTransactionProcessingResult) invocation.callRealMethod();
resultCaptor = transactionProcessingResult;
contextCaptor = ContractCallContext.get();
return transactionProcessingResult;
})
.when(processor)
.execute(paramsCaptor.capture(), gasCaptor.capture());
}

protected void verifyOpcodeTracerCall(
final String callData, final ContractFunctionProviderRecord functionProvider) {
final var callDataBytes = Bytes.fromHexString(callData);
final var debugParameters = getDebugParameters(functionProvider, callDataBytes);

if (functionProvider.expectedErrorMessage() != null) {
verifyThrowingOpcodeTracerCall(debugParameters, functionProvider);
} else {
verifySuccessfulOpcodeTracerCall(debugParameters);
}
assertThat(paramsCaptor.getValue()).isEqualTo(debugParameters);
assertThat(gasCaptor.getValue()).isEqualTo(debugParameters.getGas());
}

protected void verifyOpcodeTracerCall(final String callData, final Contract contract) {
ContractFunctionProviderRecord functionProvider = ContractFunctionProviderRecord.builder()
.contractAddress(Address.fromHexString(contract.getContractAddress()))
.build();

final var callDataBytes = Bytes.fromHexString(callData);
final var debugParameters = getDebugParameters(functionProvider, callDataBytes);

if (functionProvider.expectedErrorMessage() != null) {
verifyThrowingOpcodeTracerCall(debugParameters, functionProvider);
} else {
verifySuccessfulOpcodeTracerCall(debugParameters);
}
assertThat(paramsCaptor.getValue()).isEqualTo(debugParameters);
assertThat(gasCaptor.getValue()).isEqualTo(debugParameters.getGas());
}

@SneakyThrows
protected void verifyThrowingOpcodeTracerCall(
final ContractDebugParameters params, final ContractFunctionProviderRecord function) {
final var actual = contractDebugService.processOpcodeCall(params, OPTIONS);
assertThat(actual.transactionProcessingResult().isSuccessful()).isFalse();
assertThat(actual.transactionProcessingResult().getOutput()).isEqualTo(Bytes.EMPTY);
assertThat(actual.transactionProcessingResult())
.satisfiesAnyOf(
result -> assertThat(result.getRevertReason())
.isPresent()
.map(BytesDecoder::maybeDecodeSolidityErrorStringToReadableMessage)
.hasValue(function.expectedErrorMessage()),
result -> assertThat(result.getHaltReason())
.isPresent()
.map(ExceptionalHaltReason::getDescription)
.hasValue(function.expectedErrorMessage()));
assertThat(actual.opcodes().size()).isNotZero();
assertThat(toHumanReadableMessage(actual.opcodes().getLast().reason()))
.isEqualTo(function.expectedErrorMessage());
}

protected void verifySuccessfulOpcodeTracerCall(final ContractDebugParameters params) {
final var actual = contractDebugService.processOpcodeCall(params, OPTIONS);
final var expected = new OpcodesProcessingResult(resultCaptor, contextCaptor.getOpcodes());
// Compare transaction processing result
assertThat(actual.transactionProcessingResult())
.usingRecursiveComparison()
.ignoringFields("logs")
.isEqualTo(expected.transactionProcessingResult());
// Compare opcodes with gas tolerance
assertThat(actual.opcodes())
.usingRecursiveComparison()
.withComparatorForFields(gasComparator(), "gas")
.isEqualTo(expected.opcodes());
}
}
Loading

0 comments on commit df09959

Please sign in to comment.