Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Refactor ContractCallAddressThisTest #9014

Merged
merged 9 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,97 +19,100 @@
import static com.hedera.mirror.common.domain.entity.EntityType.CONTRACT;
import static com.hedera.mirror.common.util.DomainUtils.toEvmAddress;
import static com.hedera.mirror.web3.evm.utils.EvmTokenUtils.entityIdFromEvmAddress;
import static com.hedera.mirror.web3.service.model.CallServiceParameters.CallType.ETH_CALL;
import static com.hedera.mirror.web3.service.model.CallServiceParameters.CallType.ETH_ESTIMATE_GAS;
import static com.hedera.mirror.web3.utils.ContractCallTestUtil.isWithinExpectedGasRange;
import static com.hedera.mirror.web3.utils.ContractCallTestUtil.longValueOf;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import com.hedera.mirror.web3.service.model.ContractExecutionParameters;
import com.hedera.mirror.web3.viewmodel.BlockType;
import com.hedera.node.app.service.evm.store.models.HederaEvmAccount;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;

import com.hedera.mirror.web3.web3j.TestWeb3jServiceCustomDeploy;
import com.hedera.mirror.web3.web3j.generated.TestAddressThis;
import com.hedera.mirror.web3.web3j.generated.TestNestedAddressThis;
import com.hedera.node.app.service.evm.contracts.execution.HederaEvmTransactionProcessingResult;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.Address;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;

class ContractCallAddressThisTest extends ContractCallTestSetup {
class ContractCallAddressThisTest extends AbstractContractCallServiceTest {
@Resource
protected TestWeb3jServiceCustomDeploy testWeb3jServiceCustomDeploy;

@Resource
protected ContractExecutionService contractCallService;

@SpyBean
private ContractExecutionService contractExecutionService;

@BeforeEach
void beforeAll() {
addressThisContractPersist();
@AfterEach
void cleanup() {
testWeb3jService.setEstimateGas(false);
testWeb3jServiceCustomDeploy.cleanupContractRuntime();
}

@Test
void deployAddressThisContract() {
final var serviceParameters = serviceParametersForAddressThis(
Bytes.wrap(functionEncodeDecoder.getContractBytes(ADDRESS_THIS_CONTRACT_INIT_BYTES_PATH)));
final var expectedGasUsed = gasUsedAfterExecution(serviceParameters);

final var contract = testWeb3jService.deploy(TestAddressThis::deploy);
final var serviceParamaters = testWeb3jService.serviceParametersForTopLevelContractCreate(
kselveliev marked this conversation as resolved.
Show resolved Hide resolved
contract.getContractBinary(), ETH_ESTIMATE_GAS, Address.fromHexString(""));
kselveliev marked this conversation as resolved.
Show resolved Hide resolved
final long actualGas = 57764L;
bilyana-gospodinova marked this conversation as resolved.
Show resolved Hide resolved
assertThat(isWithinExpectedGasRange(
longValueOf.applyAsLong(contractCallService.processCall(serviceParameters)), expectedGasUsed))
longValueOf.applyAsLong(contractCallService.processCall(serviceParamaters)), actualGas))
.isTrue();
}

@Test
void addressThisFromFunction() {
final var functionHash =
functionEncodeDecoder.functionHashFor("testAddressThis", ADDRESS_THIS_CONTRACT_ABI_PATH);
final var serviceParameters = serviceParametersForExecution(
functionHash, ADDRESS_THIS_CONTRACT_ADDRESS, ETH_ESTIMATE_GAS, 0L, BlockType.LATEST);

final var expectedGasUsed = gasUsedAfterExecution(serviceParameters);

assertThat(isWithinExpectedGasRange(
longValueOf.applyAsLong(contractCallService.processCall(serviceParameters)), expectedGasUsed))
.isTrue();
final var contract = testWeb3jService.deploy(TestAddressThis::deploy);
final var functionCall = contract.send_testAddressThisFunction();
verifyEthCallAndEstimateGas(functionCall, contract);
}

@Test
void addressThisEthCallWithoutEvmAlias() {
final var addressThisContractAddressWithout0x =
ADDRESS_THIS_CONTRACT_ADDRESS.toString().substring(2);
final var successfulResponse = "0x" + StringUtils.leftPad(addressThisContractAddressWithout0x, 64, '0');
final var functionHash =
functionEncodeDecoder.functionHashFor("getAddressThis", ADDRESS_THIS_CONTRACT_ABI_PATH);
final var serviceParameters = serviceParametersForExecution(
functionHash, ADDRESS_THIS_CONTRACT_ADDRESS, ETH_CALL, 0L, BlockType.LATEST);

assertThat(contractCallService.processCall(serviceParameters)).isEqualTo(successfulResponse);
void addressThisEthCallWithoutEvmAlias() throws Exception {
// Given
final var contract = testWeb3jServiceCustomDeploy.deploy(TestAddressThis::deploy);
addressThisContractPersist(
testWeb3jServiceCustomDeploy.getContractRuntime(),
Address.fromHexString(contract.getContractAddress()));
final List<Bytes> capturedOutputs = new ArrayList<>();
doAnswer(invocation -> {
HederaEvmTransactionProcessingResult result =
(HederaEvmTransactionProcessingResult) invocation.callRealMethod();
capturedOutputs.add(result.getOutput()); // Capture the result
return result;
})
.when(contractExecutionService)
.callContract(any(), any());

// When
final var result = contract.call_getAddressThis().send();

// Then
final var successfulResponse = "0x" + StringUtils.leftPad(result.substring(2), 64, '0');
assertThat(successfulResponse).isEqualTo(capturedOutputs.getFirst().toHexString());
}

@Test
void deployNestedAddressThisContract() {
final var serviceParameters = serviceParametersForAddressThis(
Bytes.wrap(functionEncodeDecoder.getContractBytes(NESTED_ADDRESS_THIS_CONTRACT_BYTES_PATH)));
final var expectedGasUsed = gasUsedAfterExecution(serviceParameters);

final var contract = testWeb3jService.deploy(TestNestedAddressThis::deploy);
final var serviceParamaters = testWeb3jService.serviceParametersForTopLevelContractCreate(
contract.getContractBinary(), ETH_ESTIMATE_GAS, Address.fromHexString(""));
final long actualGas = 95401L;
assertThat(isWithinExpectedGasRange(
longValueOf.applyAsLong(contractCallService.processCall(serviceParameters)), expectedGasUsed))
longValueOf.applyAsLong(contractCallService.processCall(serviceParamaters)), actualGas))
.isTrue();
}

private ContractExecutionParameters serviceParametersForAddressThis(final Bytes callData) {
final var sender = new HederaEvmAccount(SENDER_ADDRESS);
return ContractExecutionParameters.builder()
.sender(sender)
.value(0L)
.receiver(Address.ZERO)
.callData(callData)
.callType(ETH_ESTIMATE_GAS)
.block(BlockType.LATEST)
.gas(15_000_000L)
.isStatic(false)
.isEstimate(true)
.build();
}

private void addressThisContractPersist() {
final var addressThisContractBytes = functionEncodeDecoder.getContractBytes(ADDRESS_THIS_CONTRACT_BYTES_PATH);
final var addressThisContractEntityId = entityIdFromEvmAddress(ADDRESS_THIS_CONTRACT_ADDRESS);
private void addressThisContractPersist(byte[] runtimeBytecode, Address contractAddress) {
final var addressThisContractEntityId = entityIdFromEvmAddress(contractAddress);
final var addressThisEvmAddress = toEvmAddress(addressThisContractEntityId);

domainBuilder
.entity()
.customize(e -> e.id(addressThisContractEntityId.getId())
Expand All @@ -118,12 +121,10 @@ private void addressThisContractPersist() {
.type(CONTRACT)
.balance(1500L))
.persist();

domainBuilder
.contract()
.customize(c -> c.id(addressThisContractEntityId.getId()).runtimeBytecode(addressThisContractBytes))
.customize(c -> c.id(addressThisContractEntityId.getId()).runtimeBytecode(runtimeBytecode))
kselveliev marked this conversation as resolved.
Show resolved Hide resolved
.persist();

domainBuilder
.contractState()
.customize(c -> c.contractId(addressThisContractEntityId.getId())
Expand All @@ -132,10 +133,6 @@ private void addressThisContractPersist() {
.value(Bytes.fromHexString("0x4746573740000000000000000000000000000000000000000000000000000000")
.toArrayUnsafe()))
.persist();

domainBuilder
.recordFile()
.customize(f -> f.bytes(addressThisContractBytes))
.persist();
domainBuilder.recordFile().customize(f -> f.bytes(runtimeBytecode)).persist();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,6 @@ public class ContractCallTestSetup extends Web3IntegrationTest {
@Value("classpath:contracts/TestContractAddress/TestAddressThisInit.bin")
protected Path ADDRESS_THIS_CONTRACT_INIT_BYTES_PATH;

@Value("classpath:contracts/TestContractAddress/TestAddressThis.json")
protected Path ADDRESS_THIS_CONTRACT_ABI_PATH;

@Value("classpath:contracts/TestContractAddress/TestNestedAddressThis.bin")
protected Path NESTED_ADDRESS_THIS_CONTRACT_BYTES_PATH;

@Value("classpath:contracts/InternalCaller/InternalCaller.bin")
protected Path INTERNAL_CALLER_CONTRACT_BYTES_PATH;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,14 @@
public class TestWeb3jService implements Web3jService {
private static final long DEFAULT_TRANSACTION_VALUE = 10L;
private static final String MOCK_KEY = "0x4e3c5c727f3f4b8f8e8a8fe7e032cf78b8693a2b711e682da1d3a26a6a3b58b6";

private final ContractExecutionService contractExecutionService;
protected final DomainBuilder domainBuilder;
protected final ContractExecutionService contractExecutionService;
private final ContractGasProvider contractGasProvider;
private final Credentials credentials;
private final DomainBuilder domainBuilder;
private final Web3j web3j;
private Address sender = Address.fromHexString("");
protected Address sender = Address.fromHexString("");
protected String transactionResult;
private boolean isEstimateGas = false;
private String transactionResult;
private String estimatedGas;
private long value = 0L;

Expand Down Expand Up @@ -130,26 +129,13 @@ public <T extends Response> T send(Request request, Class<T> responseType) {
};
}

private EthSendTransaction call(List<?> params, Request request) {
var rawTransaction = TransactionDecoder.decode(params.get(0).toString());
var transactionHash = generateTransactionHashHexEncoded(rawTransaction, credentials);
final var to = rawTransaction.getTo();

if (to.equals(HEX_PREFIX)) {
return sendTopLevelContractCreate(rawTransaction, transactionHash, request);
}

return sendEthCall(rawTransaction, transactionHash, request);
}

private EthSendTransaction sendTopLevelContractCreate(
protected EthSendTransaction sendTopLevelContractCreate(
RawTransaction rawTransaction, String transactionHash, Request request) {
final var res = new EthSendTransaction();
var serviceParameters = serviceParametersForTopLevelContractCreate(rawTransaction.getData(), ETH_CALL, sender);
final var mirrorNodeResult = contractExecutionService.processCall(serviceParameters);

try {
final var contractInstance = this.deployInternal(mirrorNodeResult);
final var contractInstance = deployInternal(mirrorNodeResult);
res.setResult(transactionHash);
res.setRawResponse(contractInstance.toHexString());
res.setId(request.getId());
Expand All @@ -162,6 +148,18 @@ private EthSendTransaction sendTopLevelContractCreate(
return res;
}

private EthSendTransaction call(List<?> params, Request request) {
var rawTransaction = TransactionDecoder.decode(params.get(0).toString());
kselveliev marked this conversation as resolved.
Show resolved Hide resolved
var transactionHash = generateTransactionHashHexEncoded(rawTransaction, credentials);
final var to = rawTransaction.getTo();

if (to.equals(HEX_PREFIX)) {
return sendTopLevelContractCreate(rawTransaction, transactionHash, request);
}

return sendEthCall(rawTransaction, transactionHash, request);
}

private EthSendTransaction sendEthCall(RawTransaction rawTransaction, String transactionHash, Request request) {
final var res = new EthSendTransaction();
var serviceParameters = serviceParametersForExecutionSingle(
Expand Down Expand Up @@ -272,7 +270,7 @@ protected ContractExecutionParameters serviceParametersForExecutionSingle(
.build();
}

protected ContractExecutionParameters serviceParametersForTopLevelContractCreate(
public ContractExecutionParameters serviceParametersForTopLevelContractCreate(
final String contractInitCode, final CallServiceParameters.CallType callType, final Address senderAddress) {
final var senderEvmAccount = new HederaEvmAccount(senderAddress);

Expand All @@ -293,7 +291,6 @@ public Address deployInternal(String binary) {
final var id = domainBuilder.id();
final var contractAddress = toAddress(EntityId.of(id));
contractPersist(binary, id);

return contractAddress;
}

Expand Down Expand Up @@ -348,5 +345,11 @@ TestWeb3jService testWeb3jService(
ContractExecutionService contractExecutionService, DomainBuilder domainBuilder) {
return new TestWeb3jService(contractExecutionService, domainBuilder);
}

@Bean
TestWeb3jServiceCustomDeploy testWeb3jServiceCustomDeploy(
ContractExecutionService contractExecutionService, DomainBuilder domainBuilder) {
return new TestWeb3jServiceCustomDeploy(contractExecutionService, domainBuilder);
}
steven-sheehy marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading