Skip to content

Commit

Permalink
HIP-584 Historical: Add integration tests for ERC and HTS precompiles…
Browse files Browse the repository at this point in the history
… - part 1 (#7431)

This PR adds some of the integration tests needed after extending the DB queries and accessors. It is focused on the HTS and ERC precompiles for EVM v0.34.

---------

Signed-off-by: Bilyana Gospodinova <[email protected]>
Signed-off-by: Kristiyan Selveliev <[email protected]>
Co-authored-by: Kristiyan Selveliev <[email protected]>
  • Loading branch information
bilyana-gospodinova and kselveliev authored Jan 4, 2024
1 parent 1b65f4a commit 1f59828
Show file tree
Hide file tree
Showing 7 changed files with 1,072 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
* Copyright (C) 2023-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.
Expand Down Expand Up @@ -43,7 +43,7 @@ void chainId() {
final var serviceParameters = serviceParametersForEvmCodes(functionHash);

assertThat(contractCallService.processCall(serviceParameters))
.isEqualTo(properties.chainIdBytes32().toHexString());
.isEqualTo(mirrorNodeEvmProperties.chainIdBytes32().toHexString());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
* Copyright (C) 2023-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.
Expand All @@ -19,17 +19,22 @@
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 org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;

import com.hedera.mirror.web3.exception.BlockNumberOutOfRangeException;
import com.hedera.mirror.web3.viewmodel.BlockType;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.Address;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

class ContractCallServiceERCTokenTest extends ContractCallTestSetup {

Expand All @@ -38,6 +43,15 @@ private static Stream<Arguments> ercContractFunctionArgumentsProvider() {
.flatMap(ercFunction -> Stream.of(Arguments.of(ercFunction, true), Arguments.of(ercFunction, false)));
}

private static Stream<Arguments> ercContractFunctionArgumentsProviderHistoricalReadOnly() {
List<BlockType> blockNumbers = List.of(BlockType.of(String.valueOf(EVM_V_34_BLOCK)));

return Arrays.stream(ErcContractReadOnlyFunctionsHistorical.values())
.flatMap(ercFunction -> Stream.concat(
blockNumbers.stream().map(blockNumber -> Arguments.of(ercFunction, true, blockNumber)),
blockNumbers.stream().map(blockNumber -> Arguments.of(ercFunction, false, blockNumber))));
}

public static final String REDIRECT_SUFFIX = "Redirect";
public static final String NON_STATIC_SUFFIX = "NonStatic";

Expand All @@ -56,6 +70,56 @@ void ercReadOnlyPrecompileOperationsTest(final ErcContractReadOnlyFunctions ercF
assertThat(contractCallService.processCall(serviceParameters)).isEqualTo(successfulResponse);
}

@ParameterizedTest
@MethodSource("ercContractFunctionArgumentsProviderHistoricalReadOnly")
void ercReadOnlyPrecompileHistoricalOperationsTest(
final ErcContractReadOnlyFunctionsHistorical ercFunction,
final boolean isStatic,
final BlockType blockNumber) {
final var functionName = ercFunction.getName(isStatic);
final var functionHash =
functionEncodeDecoder.functionHashFor(functionName, ERC_ABI_PATH, ercFunction.functionParameters);
final var serviceParameters =
serviceParametersForExecution(functionHash, ERC_CONTRACT_ADDRESS, ETH_CALL, 0L, blockNumber);

final var successfulResponse = functionEncodeDecoder.encodedResultFor(
ercFunction.name, ERC_ABI_PATH, ercFunction.expectedResultFields);
final var emptyResponse = Bytes.EMPTY.toHexString();

// Before the block the data did not exist yet
if (blockNumber.number() < EVM_V_34_BLOCK) {
assertThat(contractCallService.processCall(serviceParameters)).isEqualTo(emptyResponse);
} else {
assertThat(contractCallService.processCall(serviceParameters)).isEqualTo(successfulResponse);
}
}

@ParameterizedTest
@ValueSource(longs = {51, Long.MAX_VALUE - 1})
void ercReadOnlyPrecompileHistoricalNotExistingBlockTest(final long blockNumber) {
final var functionHash = functionEncodeDecoder.functionHashFor(
"isApprovedForAll",
ERC_ABI_PATH,
NFT_ADDRESS_HISTORICAL,
SENDER_ADDRESS_HISTORICAL,
SPENDER_ADDRESS_HISTORICAL);
final var serviceParameters = serviceParametersForExecution(
functionHash, ERC_CONTRACT_ADDRESS, ETH_CALL, 0L, BlockType.of(String.valueOf(blockNumber)));
final var latestBlockNumber = recordFileRepository.findLatestIndex().orElse(Long.MAX_VALUE);
final var emptyResponse = Bytes.EMPTY.toHexString();

// Block number (Long.MAX_VALUE - 1) does not exist in the DB and is after the
// latest block available in the DB => returning error
if (blockNumber > latestBlockNumber) {
assertThatThrownBy(() -> contractCallService.processCall(serviceParameters))
.isInstanceOf(BlockNumberOutOfRangeException.class);
} else if (blockNumber == 51) {
// Block number 51 = (EVM_V_34_BLOCK + 1) does not exist in the DB but is before the latest
// block available in the DB => returning empty response
assertThat(contractCallService.processCall(serviceParameters)).isEqualTo(emptyResponse);
}
}

@ParameterizedTest
@MethodSource("ercContractFunctionArgumentsProvider")
void supportedErcReadOnlyPrecompileOperationsTest(
Expand Down Expand Up @@ -160,6 +224,53 @@ public String getName(final boolean isStatic) {
}
}

@RequiredArgsConstructor
public enum ErcContractReadOnlyFunctionsHistorical {
GET_APPROVED_EMPTY_SPENDER(
"getApproved", new Object[] {NFT_ADDRESS_HISTORICAL, 2L}, new Address[] {Address.ZERO}),
IS_APPROVE_FOR_ALL(
"isApprovedForAll",
new Address[] {NFT_ADDRESS_HISTORICAL, SENDER_ADDRESS_HISTORICAL, SPENDER_ADDRESS_HISTORICAL},
new Boolean[] {true}),
IS_APPROVE_FOR_ALL_WITH_ALIAS(
"isApprovedForAll",
new Address[] {NFT_ADDRESS_HISTORICAL, SENDER_ALIAS_HISTORICAL, SPENDER_ALIAS_HISTORICAL},
new Boolean[] {true}),
ALLOWANCE_OF(
"allowance",
new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL, SENDER_ADDRESS_HISTORICAL, SPENDER_ADDRESS_HISTORICAL
},
new Long[] {13L}),
ALLOWANCE_OF_WITH_ALIAS(
"allowance",
new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL, SENDER_ALIAS_HISTORICAL, SPENDER_ALIAS_HISTORICAL},
new Long[] {13L}),
GET_APPROVED(
"getApproved", new Object[] {NFT_ADDRESS_HISTORICAL, 1L}, new Address[] {SPENDER_ALIAS_HISTORICAL}),
ERC_DECIMALS("decimals", new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL}, new Integer[] {12}),
TOTAL_SUPPLY("totalSupply", new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL}, new Long[] {12345L}),
ERC_SYMBOL("symbol", new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL}, new String[] {"HBAR"}),
BALANCE_OF(
"balanceOf",
new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL, SENDER_ADDRESS_HISTORICAL},
new Long[] {12L}),
BALANCE_OF_WITH_ALIAS(
"balanceOf", new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL, SENDER_ALIAS_HISTORICAL}, new Long[] {12L
}),
ERC_NAME("name", new Address[] {FUNGIBLE_TOKEN_ADDRESS_HISTORICAL}, new String[] {"Hbars"}),
OWNER_OF("getOwnerOf", new Object[] {NFT_ADDRESS_HISTORICAL, 1L}, new Address[] {OWNER_ADDRESS_HISTORICAL}),
EMPTY_OWNER_OF("getOwnerOf", new Object[] {NFT_ADDRESS_HISTORICAL, 2L}, new Address[] {Address.ZERO}),
TOKEN_URI("tokenURI", new Object[] {NFT_ADDRESS_HISTORICAL, 1L}, new String[] {"NFT_METADATA_URI"});

private final String name;
private final Object[] functionParameters;
private final Object[] expectedResultFields;

public String getName(final boolean isStatic) {
return isStatic ? name : name + NON_STATIC_SUFFIX;
}
}

@RequiredArgsConstructor
public enum ErcContractModificationFunctions {
APPROVE("approve", new Object[] {FUNGIBLE_TOKEN_ADDRESS, SPENDER_ALIAS, 2L}),
Expand All @@ -174,6 +285,7 @@ public enum ErcContractModificationFunctions {
"transferFrom", new Object[] {TREASURY_TOKEN_ADDRESS, SENDER_ALIAS, SPENDER_ALIAS, 2L}),
TRANSFER_FROM_NFT_WITH_ALIAS(
"transferFromNFT", new Object[] {NFT_TRANSFER_ADDRESS, OWNER_ADDRESS, SPENDER_ALIAS, 1L});

private final String name;
private final Object[] functionParameters;
}
Expand Down
Loading

0 comments on commit 1f59828

Please sign in to comment.