diff --git a/docs/configuration.md b/docs/configuration.md
index f57320b415f..180969349b8 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -716,6 +716,7 @@ value, it is recommended to only populate overridden properties in the custom `a
| `hedera.mirror.web3.evm.maxTokensPerAccount` | 1000 | Maximum number token associations per account |
| `hedera.mirror.web3.evm.maxTokenSymbolUtf8Bytes` | 100 | Maximum size in bytes for token symbol |
| `hedera.mirror.web3.evm.minAutoRenewDuration` | 2592000 | Minimum duration for auto-renew account |
+| `hedera.mirror.web3.evm.modularizedServices` | false | Flag that indicates if the hedera.app dependency is used. This is under development. It is recommended to be set to false. |
| `hedera.mirror.web3.evm.network` | TESTNET | Which Hedera network to use. Can be either `MAINNET`, `PREVIEWNET`, `TESTNET` or `OTHER` |
| `hedera.mirror.web3.evm.feesTokenTransferUsageMultiplier` | 380 | Used to calculate token transfer fees |
| `hedera.mirror.web3.evm.trace.enabled` | false | Flag enabling tracer |
diff --git a/hedera-mirror-web3/build.gradle.kts b/hedera-mirror-web3/build.gradle.kts
index ad602bd60e6..90b2f57a986 100644
--- a/hedera-mirror-web3/build.gradle.kts
+++ b/hedera-mirror-web3/build.gradle.kts
@@ -28,13 +28,16 @@ plugins {
id("spring-conventions")
}
+// We need to use this version in order to be able to decode hex data when using the hedera.app
+// dependency
+val headlongVersion = "6.1.1"
val javaxInjectVersion = "1"
dependencies {
implementation(platform("org.springframework.cloud:spring-cloud-dependencies"))
implementation(project(":common"))
implementation("com.bucket4j:bucket4j-core")
- implementation("com.esaulpaugh:headlong")
+ implementation("com.esaulpaugh:headlong:$headlongVersion")
implementation("com.hedera.hashgraph:app") { exclude(group = "io.netty") }
implementation("com.hedera.evm:hedera-evm")
implementation("io.github.mweirauch:micrometer-jvm-extras")
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/common/ContractCallContext.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/common/ContractCallContext.java
index 5b7803e6121..12cc998d168 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/common/ContractCallContext.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/common/ContractCallContext.java
@@ -78,16 +78,14 @@ public class ContractCallContext {
/**
* The TransactionExecutor from the modularized services integration deploys contracts in 2 steps:
- *
- * 1. The initcode is uploaded and saved as a file using a {@link FileCreateTransactionBody}.
- * 2. The returned file id from step 1 is then passed to a {@link ContractCreateTransactionBody}.
- * Each step performs a separate transaction.
- * For step 2 even if we pass the correct file id, since the mirror node data is readonly,
- * the {@link FileReadableKVState} is not able to populate the contract's bytecode from the DB
- * since it was never explicitly persisted in the DB.
- *
- * This is the function of the field "file" to hold temporary the bytecode and the fileId
- * during contract deploy.
+ *
+ * 1. The initcode is uploaded and saved as a file using a {@link FileCreateTransactionBody}. 2. The returned file
+ * id from step 1 is then passed to a {@link ContractCreateTransactionBody}. Each step performs a separate
+ * transaction. For step 2 even if we pass the correct file id, since the mirror node data is readonly, the
+ * {@link FileReadableKVState} is not able to populate the contract's bytecode from the DB since it was never
+ * explicitly persisted in the DB.
+ *
+ * This is the function of the field "file" to hold temporary the bytecode and the fileId during contract deploy.
*/
@Setter
private Optional file = Optional.empty();
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/evm/properties/MirrorNodeEvmProperties.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/evm/properties/MirrorNodeEvmProperties.java
index ef3dfa0dbd4..25d58f13ecf 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/evm/properties/MirrorNodeEvmProperties.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/evm/properties/MirrorNodeEvmProperties.java
@@ -36,6 +36,7 @@
import jakarta.validation.constraints.Positive;
import java.time.Duration;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
@@ -46,6 +47,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
+import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.hibernate.validator.constraints.time.DurationMin;
import org.hyperledger.besu.datatypes.Address;
@@ -176,10 +178,16 @@ public class MirrorNodeEvmProperties implements EvmProperties {
"contracts.maxRefundPercentOfGasLimit",
String.valueOf(maxGasRefundPercentage()));
+ @Getter(lazy = true)
+ private final Map transactionProperties = buildTransactionProperties();
+
@Getter
@Min(1)
private int feesTokenTransferUsageMultiplier = 380;
+ @Getter
+ private boolean modularizedServices;
+
public boolean shouldAutoRenewAccounts() {
return autoRenewTargetTypes.contains(EntityType.ACCOUNT);
}
@@ -314,6 +322,18 @@ public int feesTokenTransferUsageMultiplier() {
return feesTokenTransferUsageMultiplier;
}
+ private Map buildTransactionProperties() {
+ final Map mirrorNodeProperties = new HashMap<>(properties);
+ mirrorNodeProperties.put(
+ "contracts.evm.version",
+ "v"
+ + getSemanticEvmVersion().major() + "."
+ + getSemanticEvmVersion().minor());
+ mirrorNodeProperties.put(
+ "ledger.id", Bytes.wrap(getNetwork().getLedgerId()).toHexString());
+ return mirrorNodeProperties;
+ }
+
@Getter
@RequiredArgsConstructor
public enum HederaNetwork {
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractCallService.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractCallService.java
index 1ce95fd1348..a1481c54992 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractCallService.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractCallService.java
@@ -24,6 +24,7 @@
import com.hedera.mirror.web3.common.ContractCallContext;
import com.hedera.mirror.web3.evm.contracts.execution.MirrorEvmTxProcessor;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
import com.hedera.mirror.web3.evm.store.Store;
import com.hedera.mirror.web3.exception.BlockNumberNotFoundException;
import com.hedera.mirror.web3.exception.MirrorEvmTransactionException;
@@ -44,21 +45,26 @@
public abstract class ContractCallService {
static final String GAS_LIMIT_METRIC = "hedera.mirror.web3.call.gas.limit";
static final String GAS_USED_METRIC = "hedera.mirror.web3.call.gas.used";
+ protected final Store store;
private final MeterProvider gasLimitCounter;
private final MeterProvider gasUsedCounter;
- protected final Store store;
private final MirrorEvmTxProcessor mirrorEvmTxProcessor;
private final RecordFileService recordFileService;
private final ThrottleProperties throttleProperties;
private final Bucket gasLimitBucket;
+ private final MirrorNodeEvmProperties mirrorNodeEvmProperties;
+ private final TransactionExecutionService transactionExecutionService;
+ @SuppressWarnings("java:S107")
protected ContractCallService(
MirrorEvmTxProcessor mirrorEvmTxProcessor,
Bucket gasLimitBucket,
ThrottleProperties throttleProperties,
MeterRegistry meterRegistry,
RecordFileService recordFileService,
- Store store) {
+ Store store,
+ MirrorNodeEvmProperties mirrorNodeEvmProperties,
+ TransactionExecutionService transactionExecutionService) {
this.gasLimitCounter = Counter.builder(GAS_LIMIT_METRIC)
.description("The amount of gas limit sent in the request")
.withRegistry(meterRegistry);
@@ -70,24 +76,26 @@ protected ContractCallService(
this.recordFileService = recordFileService;
this.throttleProperties = throttleProperties;
this.gasLimitBucket = gasLimitBucket;
+ this.mirrorNodeEvmProperties = mirrorNodeEvmProperties;
+ this.transactionExecutionService = transactionExecutionService;
}
/**
* This method is responsible for calling a smart contract function. The method is divided into two main parts:
*
- * 1. If the call is historical, the method retrieves the corresponding record file and initializes
- * the contract call context with the historical state. The method then proceeds to call the contract.
+ * 1. If the call is historical, the method retrieves the corresponding record file and initializes the contract
+ * call context with the historical state. The method then proceeds to call the contract.
*
*
- * 2. If the call is not historical, the method initializes the contract call context with the current state
- * and proceeds to call the contract.
+ * 2. If the call is not historical, the method initializes the contract call context with the current state and
+ * proceeds to call the contract.
*
*
* @param params the call service parameters
- * @param ctx the contract call context
+ * @param ctx the contract call context
* @return {@link HederaEvmTransactionProcessingResult} of the contract call
- * @throws MirrorEvmTransactionException if any pre-checks
- * fail with {@link IllegalStateException} or {@link IllegalArgumentException}
+ * @throws MirrorEvmTransactionException if any pre-checks fail with {@link IllegalStateException} or
+ * {@link IllegalArgumentException}
*/
protected HederaEvmTransactionProcessingResult callContract(CallServiceParameters params, ContractCallContext ctx)
throws MirrorEvmTransactionException {
@@ -106,7 +114,12 @@ protected HederaEvmTransactionProcessingResult doProcessCall(
CallServiceParameters params, long estimatedGas, boolean restoreGasToThrottleBucket)
throws MirrorEvmTransactionException {
try {
- var result = mirrorEvmTxProcessor.execute(params, estimatedGas);
+ HederaEvmTransactionProcessingResult result;
+ if (!mirrorNodeEvmProperties.isModularizedServices()) {
+ result = mirrorEvmTxProcessor.execute(params, estimatedGas);
+ } else {
+ result = transactionExecutionService.execute(params, estimatedGas);
+ }
if (!restoreGasToThrottleBucket) {
return result;
}
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractDebugService.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractDebugService.java
index 21082d13644..e19ead6583f 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractDebugService.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractDebugService.java
@@ -23,6 +23,7 @@
import com.hedera.mirror.web3.evm.contracts.execution.MirrorEvmTxProcessor;
import com.hedera.mirror.web3.evm.contracts.execution.OpcodesProcessingResult;
import com.hedera.mirror.web3.evm.contracts.execution.traceability.OpcodeTracerOptions;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
import com.hedera.mirror.web3.evm.store.Store;
import com.hedera.mirror.web3.exception.MirrorEvmTransactionException;
import com.hedera.mirror.web3.repository.ContractActionRepository;
@@ -43,6 +44,7 @@
public class ContractDebugService extends ContractCallService {
private final ContractActionRepository contractActionRepository;
+ @SuppressWarnings("java:S107")
public ContractDebugService(
ContractActionRepository contractActionRepository,
RecordFileService recordFileService,
@@ -50,8 +52,18 @@ public ContractDebugService(
MirrorEvmTxProcessor mirrorEvmTxProcessor,
Bucket gasLimitBucket,
ThrottleProperties throttleProperties,
- MeterRegistry meterRegistry) {
- super(mirrorEvmTxProcessor, gasLimitBucket, throttleProperties, meterRegistry, recordFileService, store);
+ MeterRegistry meterRegistry,
+ MirrorNodeEvmProperties mirrorNodeEvmProperties,
+ TransactionExecutionService transactionExecutionService) {
+ super(
+ mirrorEvmTxProcessor,
+ gasLimitBucket,
+ throttleProperties,
+ meterRegistry,
+ recordFileService,
+ store,
+ mirrorNodeEvmProperties,
+ transactionExecutionService);
this.contractActionRepository = contractActionRepository;
}
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractExecutionService.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractExecutionService.java
index cbc504e0e01..f4239ede378 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractExecutionService.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/ContractExecutionService.java
@@ -21,6 +21,7 @@
import com.google.common.base.Stopwatch;
import com.hedera.mirror.web3.common.ContractCallContext;
import com.hedera.mirror.web3.evm.contracts.execution.MirrorEvmTxProcessor;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
import com.hedera.mirror.web3.evm.store.Store;
import com.hedera.mirror.web3.service.model.ContractExecutionParameters;
import com.hedera.mirror.web3.service.utils.BinaryGasEstimator;
@@ -38,6 +39,7 @@ public class ContractExecutionService extends ContractCallService {
private final BinaryGasEstimator binaryGasEstimator;
+ @SuppressWarnings("java:S107")
public ContractExecutionService(
MeterRegistry meterRegistry,
BinaryGasEstimator binaryGasEstimator,
@@ -45,8 +47,18 @@ public ContractExecutionService(
MirrorEvmTxProcessor mirrorEvmTxProcessor,
RecordFileService recordFileService,
ThrottleProperties throttleProperties,
- Bucket gasLimitBucket) {
- super(mirrorEvmTxProcessor, gasLimitBucket, throttleProperties, meterRegistry, recordFileService, store);
+ Bucket gasLimitBucket,
+ MirrorNodeEvmProperties mirrorNodeEvmProperties,
+ TransactionExecutionService transactionExecutionService) {
+ super(
+ mirrorEvmTxProcessor,
+ gasLimitBucket,
+ throttleProperties,
+ meterRegistry,
+ recordFileService,
+ store,
+ mirrorNodeEvmProperties,
+ transactionExecutionService);
this.binaryGasEstimator = binaryGasEstimator;
}
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/TransactionExecutionService.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/TransactionExecutionService.java
new file mode 100644
index 00000000000..f750a49fc41
--- /dev/null
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/service/TransactionExecutionService.java
@@ -0,0 +1,265 @@
+/*
+ * 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.state.Utils.isMirror;
+
+import com.hedera.hapi.node.base.AccountID;
+import com.hedera.hapi.node.base.ContractID;
+import com.hedera.hapi.node.base.Duration;
+import com.hedera.hapi.node.base.FileID;
+import com.hedera.hapi.node.base.ResponseCodeEnum;
+import com.hedera.hapi.node.base.Timestamp;
+import com.hedera.hapi.node.base.TransactionID;
+import com.hedera.hapi.node.contract.ContractCallTransactionBody;
+import com.hedera.hapi.node.contract.ContractCreateTransactionBody;
+import com.hedera.hapi.node.contract.ContractFunctionResult;
+import com.hedera.hapi.node.file.FileCreateTransactionBody;
+import com.hedera.hapi.node.state.file.File;
+import com.hedera.hapi.node.state.primitives.ProtoBytes;
+import com.hedera.hapi.node.transaction.TransactionBody;
+import com.hedera.hapi.node.transaction.TransactionRecord;
+import com.hedera.mirror.web3.common.ContractCallContext;
+import com.hedera.mirror.web3.evm.contracts.execution.traceability.OpcodeTracer;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
+import com.hedera.mirror.web3.service.model.CallServiceParameters;
+import com.hedera.mirror.web3.state.AliasesReadableKVState;
+import com.hedera.node.app.config.ConfigProviderImpl;
+import com.hedera.node.app.service.evm.contracts.execution.HederaEvmTransactionProcessingResult;
+import com.hedera.node.app.service.token.TokenService;
+import com.hedera.node.app.workflows.standalone.TransactionExecutor;
+import com.hedera.node.app.workflows.standalone.TransactionExecutors;
+import com.hedera.node.app.workflows.standalone.TransactionExecutors.TracerBinding;
+import com.hedera.node.config.data.EntitiesConfig;
+import com.swirlds.config.api.Configuration;
+import com.swirlds.state.State;
+import jakarta.annotation.Nullable;
+import jakarta.inject.Named;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.CustomLog;
+import org.apache.tuweni.bytes.Bytes;
+import org.hyperledger.besu.datatypes.Address;
+import org.hyperledger.besu.evm.tracing.OperationTracer;
+
+@Named
+@CustomLog
+public class TransactionExecutionService {
+
+ private static final Configuration DEFAULT_CONFIG = new ConfigProviderImpl().getConfiguration();
+ private static final OperationTracer[] EMPTY_OPERATION_TRACER_ARRAY = new OperationTracer[0];
+ private static final AccountID TREASURY_ACCOUNT_ID =
+ AccountID.newBuilder().accountNum(2).build();
+ private static final Duration TRANSACTION_DURATION = new Duration(15);
+ private static final Timestamp TRANSACTION_START = new Timestamp(0, 0);
+ private static final int INITCODE_SIZE_KB = 6 * 1024;
+ private final State mirrorNodeState;
+ private final MirrorNodeEvmProperties mirrorNodeEvmProperties;
+ private final OpcodeTracer opcodeTracer;
+
+ protected TransactionExecutionService(
+ State mirrorNodeState, MirrorNodeEvmProperties mirrorNodeEvmProperties, OpcodeTracer opcodeTracer) {
+ this.mirrorNodeState = mirrorNodeState;
+ this.mirrorNodeEvmProperties = mirrorNodeEvmProperties;
+ this.opcodeTracer = opcodeTracer;
+ }
+
+ public HederaEvmTransactionProcessingResult execute(final CallServiceParameters params, final long estimatedGas) {
+ final var isContractCreate = params.getReceiver().isZero();
+ final var maxLifetime =
+ DEFAULT_CONFIG.getConfigData(EntitiesConfig.class).maxLifetime();
+ var executor =
+ ExecutorFactory.newExecutor(mirrorNodeState, mirrorNodeEvmProperties.getTransactionProperties(), null);
+
+ TransactionBody transactionBody;
+ HederaEvmTransactionProcessingResult result;
+ if (isContractCreate) {
+ if (params.getCallData().size() < INITCODE_SIZE_KB) {
+ transactionBody = buildContractCreateTransactionBodyWithInitBytecode(params, estimatedGas, maxLifetime);
+ } else {
+ // Upload the init bytecode
+ transactionBody = buildFileCreateTransactionBody(params, maxLifetime);
+ var uploadReceipt = executor.execute(transactionBody, Instant.EPOCH);
+ final var fileID = uploadReceipt
+ .getFirst()
+ .transactionRecord()
+ .receiptOrThrow()
+ .fileIDOrThrow();
+ final var file = File.newBuilder()
+ .fileId(fileID)
+ .contents(com.hedera.pbj.runtime.io.buffer.Bytes.wrap(
+ params.getCallData().toFastHex(false).getBytes()))
+ .build();
+ // Set the context variables for the uploaded contract.
+ ContractCallContext.get().setFile(Optional.of(file));
+
+ // Create the contract with the init bytecode
+ transactionBody =
+ buildContractCreateTransactionBodyWithFileID(params, fileID, estimatedGas, maxLifetime);
+ }
+ } else {
+ transactionBody = buildContractCallTransactionBody(params, estimatedGas);
+ }
+
+ var receipt = executor.execute(transactionBody, Instant.EPOCH, getOperationTracers());
+ var transactionRecord = receipt.getFirst().transactionRecord();
+ if (transactionRecord.receiptOrThrow().status() == ResponseCodeEnum.SUCCESS) {
+ result = buildSuccessResult(isContractCreate, transactionRecord, params);
+ } else {
+ result = buildFailedResult(transactionRecord, isContractCreate);
+ }
+ return result;
+ }
+
+ private ContractFunctionResult getTransactionResult(
+ final TransactionRecord transactionRecord, boolean isContractCreate) {
+ return isContractCreate
+ ? transactionRecord.contractCreateResultOrThrow()
+ : transactionRecord.contractCallResultOrThrow();
+ }
+
+ private HederaEvmTransactionProcessingResult buildSuccessResult(
+ final boolean isContractCreate,
+ final TransactionRecord transactionRecord,
+ final CallServiceParameters params) {
+ var result = getTransactionResult(transactionRecord, isContractCreate);
+
+ return HederaEvmTransactionProcessingResult.successful(
+ List.of(),
+ result.gasUsed(),
+ 0L,
+ 0L,
+ Bytes.wrap(result.contractCallResult().toByteArray()),
+ params.getReceiver());
+ }
+
+ private HederaEvmTransactionProcessingResult buildFailedResult(
+ final TransactionRecord transactionRecord, final boolean isContractCreate) {
+ var result = getTransactionResult(transactionRecord, isContractCreate);
+ var status = transactionRecord.receipt().status();
+
+ return HederaEvmTransactionProcessingResult.failed(
+ result.gasUsed(),
+ 0L,
+ 0L,
+ Optional.of(Bytes.wrap(status.protoName().getBytes())),
+ Optional.empty());
+ }
+
+ private TransactionBody.Builder defaultTransactionBodyBuilder(final CallServiceParameters params) {
+ return TransactionBody.newBuilder()
+ .transactionID(TransactionID.newBuilder()
+ .transactionValidStart(TRANSACTION_START)
+ .accountID(getSenderAccountID(params))
+ .build())
+ .nodeAccountID(TREASURY_ACCOUNT_ID) // We don't really need another account here.
+ .transactionValidDuration(TRANSACTION_DURATION);
+ }
+
+ private TransactionBody buildFileCreateTransactionBody(final CallServiceParameters params, long maxLifetime) {
+ return defaultTransactionBodyBuilder(params)
+ .fileCreate(FileCreateTransactionBody.newBuilder()
+ .contents(com.hedera.pbj.runtime.io.buffer.Bytes.wrap(
+ params.getCallData().toArrayUnsafe()))
+ .expirationTime(new Timestamp(maxLifetime, 0))
+ .build())
+ .build();
+ }
+
+ private TransactionBody buildContractCreateTransactionBodyWithInitBytecode(
+ final CallServiceParameters params, long estimatedGas, long maxLifetime) {
+ return defaultTransactionBodyBuilder(params)
+ .contractCreateInstance(ContractCreateTransactionBody.newBuilder()
+ .initcode(com.hedera.pbj.runtime.io.buffer.Bytes.wrap(
+ params.getCallData().toArrayUnsafe()))
+ .gas(estimatedGas)
+ .autoRenewPeriod(new Duration(maxLifetime))
+ .build())
+ .build();
+ }
+
+ private TransactionBody buildContractCreateTransactionBodyWithFileID(
+ final CallServiceParameters params, final FileID fileID, long estimatedGas, long maxLifetime) {
+ return defaultTransactionBodyBuilder(params)
+ .contractCreateInstance(ContractCreateTransactionBody.newBuilder()
+ .fileID(fileID)
+ .gas(estimatedGas)
+ .autoRenewPeriod(new Duration(maxLifetime))
+ .build())
+ .build();
+ }
+
+ private TransactionBody buildContractCallTransactionBody(
+ final CallServiceParameters params, final long estimatedGas) {
+ return defaultTransactionBodyBuilder(params)
+ .contractCall(ContractCallTransactionBody.newBuilder()
+ .contractID(ContractID.newBuilder()
+ .evmAddress(com.hedera.pbj.runtime.io.buffer.Bytes.wrap(
+ params.getReceiver().toArrayUnsafe()))
+ .build())
+ .functionParameters(com.hedera.pbj.runtime.io.buffer.Bytes.wrap(
+ params.getCallData().toArrayUnsafe()))
+ .gas(estimatedGas)
+ .build())
+ .build();
+ }
+
+ private ProtoBytes convertAddressToProtoBytes(final Address address) {
+ return ProtoBytes.newBuilder()
+ .value(com.hedera.pbj.runtime.io.buffer.Bytes.wrap(address.toArrayUnsafe()))
+ .build();
+ }
+
+ private AccountID getSenderAccountID(final CallServiceParameters params) {
+ if (params.getSender().canonicalAddress().isZero() && params.getValue() == 0L) {
+ // Set a default account to keep the sender parameter optional.
+ return TREASURY_ACCOUNT_ID;
+ }
+
+ final var senderAddress = params.getSender().canonicalAddress();
+ if (isMirror(senderAddress)) {
+ return AccountID.newBuilder()
+ .accountNum(senderAddress.trimLeadingZeros().toLong())
+ .build();
+ }
+ return (AccountID) mirrorNodeState
+ .getReadableStates(TokenService.NAME)
+ .get(AliasesReadableKVState.KEY)
+ .get(convertAddressToProtoBytes(senderAddress));
+ }
+
+ private OperationTracer[] getOperationTracers() {
+ return ContractCallContext.get().getOpcodeTracerOptions() != null
+ ? new OperationTracer[] {opcodeTracer}
+ : EMPTY_OPERATION_TRACER_ARRAY;
+ }
+
+ public static class ExecutorFactory {
+
+ private ExecutorFactory() {}
+
+ public static TransactionExecutor newExecutor(
+ State mirrorNodeState,
+ Map properties,
+ @Nullable final TracerBinding customTracerBinding) {
+ return TransactionExecutors.TRANSACTION_EXECUTORS.newExecutor(
+ mirrorNodeState, properties, customTracerBinding);
+ }
+ }
+}
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AbstractReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AbstractReadableKVState.java
new file mode 100644
index 00000000000..fb1a281f9a4
--- /dev/null
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AbstractReadableKVState.java
@@ -0,0 +1,47 @@
+/*
+ * 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.state;
+
+import com.swirlds.state.spi.ReadableKVStateBase;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import jakarta.annotation.Nullable;
+import java.util.Collections;
+import java.util.Iterator;
+
+public abstract class AbstractReadableKVState extends ReadableKVStateBase {
+
+ protected AbstractReadableKVState(@NonNull String stateKey) {
+ super(stateKey);
+ }
+
+ @Nullable
+ @Override
+ public V get(@NonNull K key) {
+ return readFromDataSource(key);
+ }
+
+ @NonNull
+ @Override
+ protected Iterator iterateFromDataSource() {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public long size() {
+ return 0;
+ }
+}
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AccountReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AccountReadableKVState.java
index 80ebbc261a2..dce968fd4e5 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AccountReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AccountReadableKVState.java
@@ -43,11 +43,8 @@
import com.hedera.mirror.web3.utils.Suppliers;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.hedera.services.utils.EntityIdUtils;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -60,8 +57,9 @@
* The object, which is read from DB is converted to the PBJ generated format, so that it can properly be utilized by the hedera app components
* */
@Named
-public class AccountReadableKVState extends ReadableKVStateBase {
+public class AccountReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "ACCOUNTS";
private final AccountBalanceRepository accountBalanceRepository;
private final CommonEntityAccessor commonEntityAccessor;
private final CryptoAllowanceRepository cryptoAllowanceRepository;
@@ -78,7 +76,7 @@ public AccountReadableKVState(
CryptoAllowanceRepository cryptoAllowanceRepository,
TokenAccountRepository tokenAccountRepository,
AccountBalanceRepository accountBalanceRepository) {
- super("ACCOUNTS");
+ super(KEY);
this.accountBalanceRepository = accountBalanceRepository;
this.commonEntityAccessor = commonEntityAccessor;
this.cryptoAllowanceRepository = cryptoAllowanceRepository;
@@ -98,16 +96,6 @@ protected Account readFromDataSource(@Nonnull AccountID key) {
.orElse(null);
}
- @Override
- protected @Nonnull Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
-
private Account accountFromEntity(Entity entity, final Optional timestamp) {
var tokenAccountBalances = getNumberOfAllAndPositiveBalanceTokenAssociations(entity.getId(), timestamp);
byte[] alias = new byte[0];
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AirdropsReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AirdropsReadableKVState.java
index ad2eb4c731e..3fdb6cc904b 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AirdropsReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AirdropsReadableKVState.java
@@ -23,19 +23,17 @@
import com.hedera.hapi.node.state.token.AccountPendingAirdrop;
import com.hedera.mirror.web3.common.ContractCallContext;
import com.hedera.mirror.web3.repository.TokenAirdropRepository;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
-import java.util.Collections;
-import java.util.Iterator;
@Named
-public class AirdropsReadableKVState extends ReadableKVStateBase {
+public class AirdropsReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "PENDING_AIRDROPS";
private final TokenAirdropRepository tokenAirdropRepository;
protected AirdropsReadableKVState(final TokenAirdropRepository tokenAirdropRepository) {
- super("PENDING_AIRDROPS");
+ super(KEY);
this.tokenAirdropRepository = tokenAirdropRepository;
}
@@ -59,17 +57,6 @@ protected AccountPendingAirdrop readFromDataSource(@Nonnull PendingAirdropId key
.orElse(null);
}
- @Nonnull
- @Override
- protected Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
-
private AccountPendingAirdrop mapToAccountPendingAirdrop(final long amount) {
return AccountPendingAirdrop.newBuilder()
.pendingAirdropValue(mapToPendingAirdropValue(amount))
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AliasesReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AliasesReadableKVState.java
index 2cdd5f675c8..ffdfa0ea9ba 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AliasesReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/AliasesReadableKVState.java
@@ -21,19 +21,17 @@
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.state.primitives.ProtoBytes;
import com.hedera.mirror.web3.common.ContractCallContext;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
-import java.util.Collections;
-import java.util.Iterator;
@Named
-public class AliasesReadableKVState extends ReadableKVStateBase {
+public class AliasesReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "ALIASES";
private final CommonEntityAccessor commonEntityAccessor;
protected AliasesReadableKVState(final CommonEntityAccessor commonEntityAccessor) {
- super("ALIASES");
+ super(KEY);
this.commonEntityAccessor = commonEntityAccessor;
}
@@ -44,15 +42,4 @@ protected AccountID readFromDataSource(@Nonnull ProtoBytes alias) {
return entity.map(e -> toAccountId(e.getShard(), e.getRealm(), e.getNum()))
.orElse(null);
}
-
- @Nonnull
- @Override
- protected Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
}
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractBytecodeReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractBytecodeReadableKVState.java
index 43fdc7e0abf..9aa3fca6de2 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractBytecodeReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractBytecodeReadableKVState.java
@@ -26,23 +26,21 @@
import com.hedera.mirror.common.util.DomainUtils;
import com.hedera.mirror.web3.repository.ContractRepository;
import com.hedera.pbj.runtime.io.buffer.Bytes;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.Optional;
@Named
-public class ContractBytecodeReadableKVState extends ReadableKVStateBase {
+public class ContractBytecodeReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "BYTECODE";
private final ContractRepository contractRepository;
private final CommonEntityAccessor commonEntityAccessor;
protected ContractBytecodeReadableKVState(
final ContractRepository contractRepository, CommonEntityAccessor commonEntityAccessor) {
- super("BYTECODE");
+ super(KEY);
this.contractRepository = contractRepository;
this.commonEntityAccessor = commonEntityAccessor;
}
@@ -58,17 +56,6 @@ protected Bytecode readFromDataSource(@Nonnull ContractID contractID) {
.orElse(null);
}
- @Nonnull
- @Override
- protected Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
-
private EntityId toEntityId(@Nonnull final com.hedera.hapi.node.base.ContractID contractID) {
if (contractID.hasContractNum()) {
return entityIdFromContractId(contractID);
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractStorageReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractStorageReadableKVState.java
index f09403bf848..6260db24149 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractStorageReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/ContractStorageReadableKVState.java
@@ -22,19 +22,17 @@
import com.hedera.mirror.web3.repository.ContractStateRepository;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.hedera.services.utils.EntityIdUtils;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
-import java.util.Collections;
-import java.util.Iterator;
@Named
-public class ContractStorageReadableKVState extends ReadableKVStateBase {
+public class ContractStorageReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "STORAGE";
private final ContractStateRepository contractStateRepository;
protected ContractStorageReadableKVState(final ContractStateRepository contractStateRepository) {
- super("STORAGE");
+ super(KEY);
this.contractStateRepository = contractStateRepository;
}
@@ -54,15 +52,4 @@ protected SlotValue readFromDataSource(@Nonnull SlotKey slotKey) {
.map(byteArr -> new SlotValue(Bytes.wrap(byteArr), Bytes.EMPTY, Bytes.EMPTY))
.orElse(null);
}
-
- @Nonnull
- @Override
- protected Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
}
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/FileReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/FileReadableKVState.java
index 119b430d34d..64e7d59331c 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/FileReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/FileReadableKVState.java
@@ -29,12 +29,9 @@
import com.hedera.mirror.web3.repository.FileDataRepository;
import com.hedera.mirror.web3.utils.Suppliers;
import com.hedera.pbj.runtime.io.buffer.Bytes;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
import java.time.Instant;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.Optional;
import java.util.function.Supplier;
@@ -44,13 +41,14 @@
* properly be utilized by the hedera app components
*/
@Named
-public class FileReadableKVState extends ReadableKVStateBase {
+public class FileReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "FILES";
private final FileDataRepository fileDataRepository;
private final EntityRepository entityRepository;
public FileReadableKVState(final FileDataRepository fileDataRepository, final EntityRepository entityRepository) {
- super("FILES");
+ super(KEY);
this.fileDataRepository = fileDataRepository;
this.entityRepository = entityRepository;
}
@@ -74,17 +72,6 @@ protected File readFromDataSource(@Nonnull FileID key) {
.orElse(null);
}
- @Nonnull
- @Override
- protected Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
-
private File mapToFile(final FileData fileData, final FileID key, final Optional timestamp) {
return File.newBuilder()
.contents(Bytes.wrap(fileData.getFileData()))
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java
index 8154e63d5f3..208b26ebf08 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java
@@ -16,7 +16,6 @@
package com.hedera.mirror.web3.state;
-import static com.hedera.node.app.util.FileUtilities.createFileID;
import static com.hedera.node.app.workflows.handle.metric.UnavailableMetrics.UNAVAILABLE_METRICS;
import static com.hedera.node.app.workflows.standalone.TransactionExecutors.DEFAULT_NODE_INFO;
import static com.swirlds.state.StateChangeListener.StateType.MAP;
@@ -25,13 +24,11 @@
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
-import com.hedera.hapi.node.base.FileID;
import com.hedera.hapi.node.base.Key;
-import com.hedera.hapi.node.base.KeyList;
import com.hedera.hapi.node.base.SignatureMap;
-import com.hedera.hapi.node.state.file.File;
import com.hedera.hapi.node.transaction.ThrottleDefinitions;
import com.hedera.mirror.web3.common.ContractCallContext;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
import com.hedera.mirror.web3.state.core.ListReadableQueueState;
import com.hedera.mirror.web3.state.core.ListWritableQueueState;
import com.hedera.mirror.web3.state.core.MapReadableKVState;
@@ -44,9 +41,8 @@
import com.hedera.node.app.ids.EntityIdService;
import com.hedera.node.app.records.BlockRecordService;
import com.hedera.node.app.service.contract.impl.ContractServiceImpl;
-import com.hedera.node.app.service.file.FileService;
import com.hedera.node.app.service.file.impl.FileServiceImpl;
-import com.hedera.node.app.service.file.impl.schemas.V0490FileSchema;
+import com.hedera.node.app.service.schedule.impl.ScheduleServiceImpl;
import com.hedera.node.app.service.token.impl.TokenServiceImpl;
import com.hedera.node.app.services.AppContextImpl;
import com.hedera.node.app.services.ServiceMigrator;
@@ -59,15 +55,11 @@
import com.hedera.node.app.throttle.ThrottleAccumulator;
import com.hedera.node.app.version.ServicesSoftwareVersion;
import com.hedera.node.app.workflows.handle.metric.UnavailableMetrics;
-import com.hedera.node.config.data.FilesConfig;
import com.hedera.node.config.data.VersionConfig;
-import com.hedera.pbj.runtime.io.buffer.Bytes;
-import com.swirlds.config.api.Configuration;
import com.swirlds.state.State;
import com.swirlds.state.StateChangeListener;
import com.swirlds.state.lifecycle.StartupNetworks;
import com.swirlds.state.lifecycle.info.NetworkInfo;
-import com.swirlds.state.spi.CommittableWritableStates;
import com.swirlds.state.spi.EmptyWritableStates;
import com.swirlds.state.spi.KVChangeListener;
import com.swirlds.state.spi.QueueChangeListener;
@@ -115,8 +107,15 @@ public class MirrorNodeState implements State {
private final NetworkInfo networkInfo;
private final StartupNetworks startupNetworks;
+ private final MirrorNodeEvmProperties mirrorNodeEvmProperties;
+
@PostConstruct
private void init() {
+ if (!mirrorNodeEvmProperties.isModularizedServices()) {
+ // If the flag is not enabled, we don't need to make any further initialization.
+ return;
+ }
+
ContractCallContext.run(ctx -> {
registerServices(servicesRegistry);
final var bootstrapConfig = new BootstrapConfigProviderImpl().getConfiguration();
@@ -131,20 +130,6 @@ private void init() {
networkInfo,
UnavailableMetrics.UNAVAILABLE_METRICS,
startupNetworks);
-
- final var fileServiceStates = this.getWritableStates(FileService.NAME);
- final var files = fileServiceStates.get(V0490FileSchema.BLOBS_KEY);
- genesisContentProviders(bootstrapConfig).forEach((fileNum, provider) -> {
- final var fileId = createFileID(fileNum, bootstrapConfig);
- files.put(
- fileId,
- File.newBuilder()
- .fileId(fileId)
- .keys(KeyList.DEFAULT)
- .contents(provider.apply(bootstrapConfig))
- .build());
- });
- ((CommittableWritableStates) fileServiceStates).commit();
return ctx;
});
}
@@ -392,19 +377,11 @@ private void registerServices(ServicesRegistry servicesRegistry) {
new BlockRecordService(),
new FeeService(),
new CongestionThrottleService(),
- new RecordCacheService())
+ new RecordCacheService(),
+ new ScheduleServiceImpl())
.forEach(servicesRegistry::register);
}
- private Map> genesisContentProviders(
- final com.swirlds.config.api.Configuration config) {
- final var genesisSchema = new V0490FileSchema();
- final var filesConfig = config.getConfigData(FilesConfig.class);
- return Map.of(
- filesConfig.feeSchedules(), genesisSchema::genesisFeeSchedules,
- filesConfig.exchangeRates(), genesisSchema::genesisExchangeRates);
- }
-
private SignatureVerifier signatureVerifier() {
return new SignatureVerifier() {
@Override
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/NftReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/NftReadableKVState.java
index cdd7c3ddb7e..2ccc153708b 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/NftReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/NftReadableKVState.java
@@ -26,11 +26,8 @@
import com.hedera.mirror.web3.repository.NftRepository;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.hedera.services.utils.EntityIdUtils;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
-import java.util.Collections;
-import java.util.Iterator;
/**
* This class serves as a repository layer between hedera app services read only state and the Postgres database in
@@ -38,12 +35,13 @@
* utilized by the hedera app components
*/
@Named
-public class NftReadableKVState extends ReadableKVStateBase {
+public class NftReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "NFTS";
private final NftRepository nftRepository;
public NftReadableKVState(@Nonnull NftRepository nftRepository) {
- super("NFTS");
+ super(KEY);
this.nftRepository = nftRepository;
}
@@ -62,17 +60,6 @@ protected Nft readFromDataSource(@Nonnull final NftID key) {
.orElse(null);
}
- @Nonnull
- @Override
- protected Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
-
private Nft mapToNft(final com.hedera.mirror.common.domain.token.Nft nft, final TokenID tokenID) {
return Nft.newBuilder()
.metadata(Bytes.wrap(nft.getMetadata()))
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenReadableKVState.java
index 9e4cb41812c..72787f7f630 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenReadableKVState.java
@@ -45,12 +45,10 @@
import com.hedera.pbj.runtime.OneOf;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.hedera.services.utils.EntityIdUtils;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -58,7 +56,9 @@
import org.springframework.util.CollectionUtils;
@Named
-public class TokenReadableKVState extends ReadableKVStateBase {
+public class TokenReadableKVState extends AbstractReadableKVState {
+
+ public static final String KEY = "TOKENS";
private final CommonEntityAccessor commonEntityAccessor;
private final CustomFeeRepository customFeeRepository;
private final TokenRepository tokenRepository;
@@ -71,7 +71,7 @@ protected TokenReadableKVState(
final TokenRepository tokenRepository,
final EntityRepository entityRepository,
final NftRepository nftRepository) {
- super("TOKENS");
+ super(KEY);
this.commonEntityAccessor = commonEntityAccessor;
this.customFeeRepository = customFeeRepository;
this.tokenRepository = tokenRepository;
@@ -99,16 +99,6 @@ protected Token readFromDataSource(@Nonnull TokenID key) {
return tokenFromEntities(entity, token, timestamp);
}
- @Override
- protected @Nonnull Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
-
private Token tokenFromEntities(
final Entity entity,
final com.hedera.mirror.common.domain.token.Token token,
diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenRelationshipReadableKVState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenRelationshipReadableKVState.java
index aa7857f5ed1..44e1a37e9e3 100644
--- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenRelationshipReadableKVState.java
+++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/TokenRelationshipReadableKVState.java
@@ -33,17 +33,15 @@
import com.hedera.mirror.web3.repository.TokenBalanceRepository;
import com.hedera.mirror.web3.repository.TokenRepository;
import com.hedera.mirror.web3.utils.Suppliers;
-import com.swirlds.state.spi.ReadableKVStateBase;
import jakarta.annotation.Nonnull;
import jakarta.inject.Named;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.Optional;
import java.util.function.Supplier;
@Named
-public class TokenRelationshipReadableKVState extends ReadableKVStateBase {
+public class TokenRelationshipReadableKVState extends AbstractReadableKVState {
+ public static final String KEY = "TOKEN_RELS";
private final NftRepository nftRepository;
private final TokenAccountRepository tokenAccountRepository;
private final TokenBalanceRepository tokenBalanceRepository;
@@ -54,7 +52,7 @@ protected TokenRelationshipReadableKVState(
final TokenAccountRepository tokenAccountRepository,
final TokenBalanceRepository tokenBalanceRepository,
final TokenRepository tokenRepository) {
- super("TOKEN_RELS");
+ super(KEY);
this.nftRepository = nftRepository;
this.tokenAccountRepository = tokenAccountRepository;
this.tokenBalanceRepository = tokenBalanceRepository;
@@ -79,17 +77,6 @@ protected TokenRelation readFromDataSource(@Nonnull EntityIDPair key) {
.orElse(null);
}
- @Nonnull
- @Override
- protected Iterator iterateFromDataSource() {
- return Collections.emptyIterator();
- }
-
- @Override
- public long size() {
- return 0;
- }
-
private Optional findTokenAccount(
final TokenID tokenID, final AccountID accountID, final Optional timestamp) {
AbstractTokenAccount.Id id = new AbstractTokenAccount.Id();
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/RecordFileRepositoryTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/RecordFileRepositoryTest.java
index 37b45c00e97..5c60323986a 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/RecordFileRepositoryTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/RecordFileRepositoryTest.java
@@ -40,10 +40,11 @@ void findLatestIndex() {
@Test
void findEarliest() {
- var earliest = domainBuilder.recordFile().persist();
+ final var genesisRecordFile =
+ domainBuilder.recordFile().customize(f -> f.index(0L)).persist();
domainBuilder.recordFile().persist();
- assertThat(recordFileRepository.findEarliest()).get().isEqualTo(earliest);
+ assertThat(recordFileRepository.findEarliest()).get().isEqualTo(genesisRecordFile);
}
@Test
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/TokenBalanceRepositoryTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/TokenBalanceRepositoryTest.java
index c93cf29158f..3f65803ba11 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/TokenBalanceRepositoryTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/repository/TokenBalanceRepositoryTest.java
@@ -158,17 +158,11 @@ void findHistoricalBalanceIfTokenBalanceIsMissing() {
// Test case: account_balance and token_balance entry BEFORE token transfers is missing
// usually the account_balance/token_balance gets persisted ~8 mins after the account creation
- var accountId = new AccountBalance.Id(domainBuilder.timestamp(), TREASURY_ENTITY_ID);
- // persist account
- domainBuilder
- .entity()
- .customize(a -> a.id(accountId.getAccountId().getId()))
- .persist();
// not persisted
var tokenBalance1 = domainBuilder
.tokenBalance()
- .customize(tb -> tb.id(new TokenBalance.Id(
- domainBuilder.timestamp(), accountId.getAccountId(), domainBuilder.entityId())))
+ .customize(tb -> tb.id(
+ new TokenBalance.Id(domainBuilder.timestamp(), TREASURY_ENTITY_ID, domainBuilder.entityId())))
.get();
long consensusTimestamp = tokenBalance1.getId().getConsensusTimestamp();
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceOpcodeTracerTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceOpcodeTracerTest.java
index 3951dfc91ce..39e265d02e5 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceOpcodeTracerTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceOpcodeTracerTest.java
@@ -48,6 +48,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.springframework.boot.test.mock.mockito.SpyBean;
import org.web3j.tx.Contract;
abstract class AbstractContractCallServiceOpcodeTracerTest extends AbstractContractCallServiceHistoricalTest {
@@ -55,6 +56,9 @@ abstract class AbstractContractCallServiceOpcodeTracerTest extends AbstractContr
@Resource
protected ContractDebugService contractDebugService;
+ @SpyBean
+ protected TransactionExecutionService transactionExecutionService;
+
@Captor
private ArgumentCaptor paramsCaptor;
@@ -69,15 +73,27 @@ abstract class AbstractContractCallServiceOpcodeTracerTest extends AbstractContr
@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());
+ if (!mirrorNodeEvmProperties.isModularizedServices()) {
+ doAnswer(invocation -> {
+ final var transactionProcessingResult =
+ (HederaEvmTransactionProcessingResult) invocation.callRealMethod();
+ resultCaptor = transactionProcessingResult;
+ contextCaptor = ContractCallContext.get();
+ return transactionProcessingResult;
+ })
+ .when(processor)
+ .execute(paramsCaptor.capture(), gasCaptor.capture());
+ } else {
+ doAnswer(invocation -> {
+ final var transactionProcessingResult =
+ (HederaEvmTransactionProcessingResult) invocation.callRealMethod();
+ resultCaptor = transactionProcessingResult;
+ contextCaptor = ContractCallContext.get();
+ return transactionProcessingResult;
+ })
+ .when(transactionExecutionService)
+ .execute(paramsCaptor.capture(), gasCaptor.capture());
+ }
}
protected void verifyOpcodeTracerCall(
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceTest.java
index c97ee6ad582..6548a181a7a 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/AbstractContractCallServiceTest.java
@@ -29,9 +29,11 @@
import com.hedera.mirror.common.domain.entity.EntityType;
import com.hedera.mirror.common.domain.token.TokenFreezeStatusEnum;
import com.hedera.mirror.common.domain.token.TokenKycStatusEnum;
+import com.hedera.mirror.common.domain.transaction.RecordFile;
import com.hedera.mirror.web3.Web3IntegrationTest;
import com.hedera.mirror.web3.common.ContractCallContext;
import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
+import com.hedera.mirror.web3.evm.utils.EvmTokenUtils;
import com.hedera.mirror.web3.service.model.CallServiceParameters.CallType;
import com.hedera.mirror.web3.service.model.ContractDebugParameters;
import com.hedera.mirror.web3.service.model.ContractExecutionParameters;
@@ -42,7 +44,11 @@
import com.hedera.node.app.service.evm.store.models.HederaEvmAccount;
import com.hedera.services.store.models.Id;
import com.hedera.services.utils.EntityIdUtils;
+import com.hederahashgraph.api.proto.java.ExchangeRate;
+import com.hederahashgraph.api.proto.java.ExchangeRateSet;
import com.hederahashgraph.api.proto.java.Key;
+import com.hederahashgraph.api.proto.java.TimestampSeconds;
+import com.swirlds.state.State;
import jakarta.annotation.Resource;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -56,7 +62,6 @@
import org.web3j.tx.Contract;
@Import(Web3jTestConfiguration.class)
-@SuppressWarnings("unchecked")
public abstract class AbstractContractCallServiceTest extends Web3IntegrationTest {
@Resource
@@ -65,6 +70,27 @@ public abstract class AbstractContractCallServiceTest extends Web3IntegrationTes
@Resource
protected MirrorNodeEvmProperties mirrorNodeEvmProperties;
+ @Resource
+ protected State state;
+
+ protected RecordFile genesisRecordFile;
+
+ protected static final String TREASURY_ADDRESS = EvmTokenUtils.toAddress(2).toHexString();
+
+ protected static final byte[] EXCHANGE_RATES_SET = ExchangeRateSet.newBuilder()
+ .setCurrentRate(ExchangeRate.newBuilder()
+ .setCentEquiv(12)
+ .setHbarEquiv(1)
+ .setExpirationTime(TimestampSeconds.newBuilder().setSeconds(4102444800L))
+ .build())
+ .setNextRate(ExchangeRate.newBuilder()
+ .setCentEquiv(15)
+ .setHbarEquiv(1)
+ .setExpirationTime(TimestampSeconds.newBuilder().setSeconds(4102444800L))
+ .build())
+ .build()
+ .toByteArray();
+
public static Key getKeyWithDelegatableContractId(final Contract contract) {
final var contractAddress = Address.fromHexString(contract.getContractAddress());
@@ -82,8 +108,18 @@ public static Key getKeyWithContractId(final Contract contract) {
}
@BeforeEach
- final void setup() {
- domainBuilder.recordFile().persist();
+ protected void setup() {
+ genesisRecordFile =
+ domainBuilder.recordFile().customize(f -> f.index(0L)).persist();
+ domainBuilder
+ .entity()
+ .customize(e -> e.id(2L).num(2L).balance(5000000000000000000L))
+ .persist();
+ domainBuilder.entity().customize(e -> e.id(98L).num(98L)).persist();
+ domainBuilder
+ .fileData()
+ .customize(f -> f.entityId(EntityId.of(112L)).fileData(EXCHANGE_RATES_SET))
+ .persist();
testWeb3jService.reset();
}
@@ -190,7 +226,15 @@ protected Entity tokenEntityPersist() {
protected Entity accountEntityPersist() {
return domainBuilder
.entity()
- .customize(e -> e.type(EntityType.ACCOUNT).deleted(false).balance(1_000_000_000_000L))
+ .customize(e ->
+ e.type(EntityType.ACCOUNT).evmAddress(null).alias(null).balance(1_000_000_000_000L))
+ .persist();
+ }
+
+ protected Entity accountEntityWithEvmAddressPersist() {
+ return domainBuilder
+ .entity()
+ .customize(e -> e.type(EntityType.ACCOUNT).balance(1_000_000_000_000L))
.persist();
}
@@ -210,7 +254,7 @@ protected void tokenAccountPersist(final Entity token, final Long accountId) {
}
protected String getAddressFromEntity(Entity entity) {
- return EntityIdUtils.asHexedEvmAddress(new Id(entity.getShard(), entity.getRealm(), entity.getNum()));
+ return EvmTokenUtils.toAddress(entity.toEntityId()).toHexString();
}
protected String getAliasFromEntity(Entity entity) {
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallAddressThisTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallAddressThisTest.java
index 09bc96ecb8e..4196d34ee7a 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallAddressThisTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallAddressThisTest.java
@@ -143,7 +143,7 @@ void contractDeployWithValue() throws Exception {
final var request = new ContractCallRequest();
request.setBlock(BlockType.LATEST);
request.setData(contract.getContractBinary());
- request.setFrom(Address.ZERO.toHexString());
+ request.setFrom(TREASURY_ADDRESS);
request.setValue(1000);
// When
contractCall(request)
@@ -158,11 +158,11 @@ void contractDeployWithValue() throws Exception {
@Test
void deployNestedAddressThisContract() {
final var contract = testWeb3jService.deploy(TestNestedAddressThis::deploy);
- final var serviceParamaters = testWeb3jService.serviceParametersForTopLevelContractCreate(
+ final var serviceParameters = testWeb3jService.serviceParametersForTopLevelContractCreate(
contract.getContractBinary(), ETH_ESTIMATE_GAS, Address.ZERO);
final long actualGas = 95401L;
assertThat(isWithinExpectedGasRange(
- longValueOf.applyAsLong(contractCallService.processCall(serviceParamaters)), actualGas))
+ longValueOf.applyAsLong(contractCallService.processCall(serviceParameters)), actualGas))
.isTrue();
}
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallEvmCodesTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallEvmCodesTest.java
index 46be59836dd..76c713034f3 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallEvmCodesTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallEvmCodesTest.java
@@ -166,31 +166,29 @@ void getBlockPrevrandao() throws Exception {
@Test
void getBlockHashReturnsCorrectHash() throws Exception {
testWeb3jService.setUseContractCallDeploy(true);
+ domainBuilder.recordFile().persist(); // Set a latest block - needed for the block hash operation
final var contract = testWeb3jService.deploy(EvmCodes::deploy);
- final var recordFileForBlockHash =
- domainBuilder.recordFile().customize(r -> r.index(0L)).persist();
- var result = contract.call_getBlockHash(BigInteger.valueOf(recordFileForBlockHash.getIndex()))
+ var result = contract.call_getBlockHash(BigInteger.valueOf(genesisRecordFile.getIndex()))
.send();
- var expectedResult = ByteString.fromHex(recordFileForBlockHash.getHash().substring(0, 64))
- .toByteArray();
+ var expectedResult =
+ ByteString.fromHex(genesisRecordFile.getHash().substring(0, 64)).toByteArray();
assertThat(result).isEqualTo(expectedResult);
}
@Test
void getGenesisBlockHashReturnsCorrectBlock() throws Exception {
testWeb3jService.setUseContractCallDeploy(true);
+ domainBuilder.recordFile().persist(); // Set a latest block - needed for the block hash operation
final var contract = testWeb3jService.deploy(EvmCodes::deploy);
- final var genesisRecordFileForBlockHash =
- domainBuilder.recordFile().customize(f -> f.index(0L)).persist();
var result = contract.call_getBlockHash(BigInteger.ZERO).send();
- var expectedResult = ByteString.fromHex(
- genesisRecordFileForBlockHash.getHash().substring(0, 64))
- .toByteArray();
+ var expectedResult =
+ ByteString.fromHex(genesisRecordFile.getHash().substring(0, 64)).toByteArray();
assertThat(result).isEqualTo(expectedResult);
}
@Test
void getLatestBlockHashIsNotEmpty() throws Exception {
+ domainBuilder.recordFile().persist();
final var contract = testWeb3jService.deploy(EvmCodes::deploy);
var result = contract.call_getLatestBlockHash().send();
var expectedResult = ByteString.fromHex((EMPTY_BLOCK_HASH)).toByteArray();
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallNativePrecompileTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallNativePrecompileTest.java
index 0532f3c7495..198bdd3efbc 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallNativePrecompileTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallNativePrecompileTest.java
@@ -39,8 +39,7 @@ class ContractCallNativePrecompileTest extends Web3IntegrationTest {
@BeforeEach
void setup() {
- // Persist needed entities
- domainBuilder.recordFile().customize(f -> f.index(0L)).persist();
+ domainBuilder.recordFile().persist();
}
@Test
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileModificationTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileModificationTest.java
index 8c133a1a12a..9fac5f25a85 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileModificationTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileModificationTest.java
@@ -80,8 +80,8 @@ class ContractCallServicePrecompileModificationTest extends AbstractContractCall
void transferFrom() throws Exception {
// Given
final var owner = accountEntityPersist();
- final var spender = accountEntityPersist();
- final var recipient = accountEntityPersist();
+ final var spender = accountEntityWithEvmAddressPersist();
+ final var recipient = accountEntityWithEvmAddressPersist();
final var tokenEntity =
domainBuilder.entity().customize(e -> e.type(EntityType.TOKEN)).persist();
@@ -269,7 +269,7 @@ void associateTokenHRC() throws Exception {
@ValueSource(booleans = {true, false})
void dissociateToken(final Boolean single) throws Exception {
// Given
- final var associatedAccount = accountEntityPersist();
+ final var associatedAccount = accountEntityWithEvmAddressPersist();
final var tokenEntity =
domainBuilder.entity().customize(e -> e.type(EntityType.TOKEN)).persist();
@@ -438,7 +438,7 @@ void burnNFT() throws Exception {
@Test
void wipeFungibleToken() throws Exception {
// Given
- final var owner = accountEntityPersist();
+ final var owner = accountEntityWithEvmAddressPersist();
final var tokenEntity = tokenEntityPersist();
domainBuilder
@@ -462,7 +462,7 @@ void wipeFungibleToken() throws Exception {
@Test
void wipeNFT() throws Exception {
// Given
- final var owner = accountEntityPersist();
+ final var owner = accountEntityWithEvmAddressPersist();
final var tokenEntity = tokenEntityPersist();
domainBuilder
@@ -490,7 +490,7 @@ void wipeNFT() throws Exception {
@Test
void grantTokenKyc() throws Exception {
// Given
- final var accountWithoutGrant = accountEntityPersist();
+ final var accountWithoutGrant = accountEntityWithEvmAddressPersist();
final var tokenEntity = tokenEntityPersist();
domainBuilder
@@ -514,7 +514,7 @@ void grantTokenKyc() throws Exception {
@Test
void revokeTokenKyc() throws Exception {
// Given
- final var accountWithGrant = accountEntityPersist();
+ final var accountWithGrant = accountEntityWithEvmAddressPersist();
final var tokenEntity = tokenEntityPersist();
domainBuilder
@@ -557,7 +557,7 @@ void deleteToken() throws Exception {
@Test
void freezeToken() throws Exception {
// Given
- final var accountWithoutFreeze = accountEntityPersist();
+ final var accountWithoutFreeze = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
tokenAccountPersist(tokenEntity, accountWithoutFreeze);
@@ -575,7 +575,7 @@ void freezeToken() throws Exception {
@Test
void unfreezeToken() throws Exception {
// Given
- final var accountWithFreeze = accountEntityPersist();
+ final var accountWithFreeze = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
@@ -686,7 +686,7 @@ void createFungibleTokenWithCustomFees() throws Exception {
var value = 10000L * 100_000_000L;
final var tokenForDenomination = persistFungibleToken();
- final var feeCollector = accountEntityPersist();
+ final var feeCollector = accountEntityWithEvmAddressPersist();
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
@@ -766,7 +766,7 @@ void createNonFungibleTokenWithCustomFees() throws Exception {
var value = 10000L * 100_000_000L;
final var tokenForDenomination = persistFungibleToken();
- final var feeCollector = accountEntityPersist();
+ final var feeCollector = accountEntityWithEvmAddressPersist();
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
@@ -811,8 +811,8 @@ void createNonFungibleTokenWithCustomFees() throws Exception {
@Test
void create2ContractAndTransferFromIt() throws Exception {
// Given
- final var sponsor = accountEntityPersist();
- final var receiver = accountEntityPersist();
+ final var sponsor = accountEntityWithEvmAddressPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
final var token = persistFungibleToken();
tokenAccountPersist(token, sponsor);
@@ -847,7 +847,7 @@ void notExistingPrecompileCallFails() {
void createFungibleTokenWithInheritKeysCall() throws Exception {
// Given
final var value = 10000 * 100_000_000L;
- final var sender = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
// When
@@ -1049,9 +1049,9 @@ void transferToken(final String type) throws Exception {
// Given
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
final var tokenEntity = persistFungibleToken();
- final var sender = accountEntityPersist();
- final var receiver = accountEntityPersist();
- final var payer = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
+ final var payer = accountEntityWithEvmAddressPersist();
tokenAccountPersist(tokenEntity, payer);
tokenAccountPersist(tokenEntity, sender);
@@ -1085,7 +1085,7 @@ void transferToken(final String type) throws Exception {
void transferNft(final String type) throws Exception {
// Given
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
- final var sender = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
final var tokenEntity = tokenEntityPersist();
domainBuilder
.token()
@@ -1095,8 +1095,8 @@ void transferNft(final String type) throws Exception {
.nft()
.customize(n -> n.tokenId(tokenEntity.getId()).serialNumber(1L).accountId(sender.toEntityId()))
.persist();
- final var receiver = accountEntityPersist();
- final var payer = accountEntityPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
+ final var payer = accountEntityWithEvmAddressPersist();
tokenAccountPersist(tokenEntity, payer);
tokenAccountPersist(tokenEntity, sender);
@@ -1130,7 +1130,7 @@ void transferNft(final String type) throws Exception {
void transferFromNft() throws Exception {
// Given
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
- final var sender = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
final var tokenEntity = tokenEntityPersist();
domainBuilder
.token()
@@ -1140,8 +1140,8 @@ void transferFromNft() throws Exception {
.nft()
.customize(n -> n.tokenId(tokenEntity.getId()).serialNumber(1L).accountId(sender.toEntityId()))
.persist();
- final var receiver = accountEntityPersist();
- final var payer = accountEntityPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
+ final var payer = accountEntityWithEvmAddressPersist();
tokenAccountPersist(tokenEntity, payer);
tokenAccountPersist(tokenEntity, sender);
@@ -1169,9 +1169,9 @@ void transferFromNft() throws Exception {
void cryptoTransferHbars() throws Exception {
// Given
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
- final var sender = accountEntityPersist();
- final var receiver = accountEntityPersist();
- final var payer = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
+ final var payer = accountEntityWithEvmAddressPersist();
// When
testWeb3jService.setSender(getAliasFromEntity(payer));
@@ -1192,10 +1192,10 @@ void cryptoTransferHbars() throws Exception {
void cryptoTransferToken() throws Exception {
// Given
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
- final var sender = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
- final var receiver = accountEntityPersist();
- final var payer = accountEntityPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
+ final var payer = accountEntityWithEvmAddressPersist();
tokenAccountPersist(tokenEntity, payer);
tokenAccountPersist(tokenEntity, sender);
@@ -1224,10 +1224,10 @@ void cryptoTransferToken() throws Exception {
void cryptoTransferHbarsAndToken() throws Exception {
// Given
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
- final var sender = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
- final var receiver = accountEntityPersist();
- final var payer = accountEntityPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
+ final var payer = accountEntityWithEvmAddressPersist();
tokenAccountPersist(tokenEntity, payer);
tokenAccountPersist(tokenEntity, sender);
@@ -1259,7 +1259,7 @@ void cryptoTransferHbarsAndToken() throws Exception {
void cryptoTransferNft() throws Exception {
// Given
final var contract = testWeb3jService.deploy(ModificationPrecompileTestContract::deploy);
- final var sender = accountEntityPersist();
+ final var sender = accountEntityWithEvmAddressPersist();
final var tokenEntity = tokenEntityPersist();
domainBuilder
.token()
@@ -1269,8 +1269,8 @@ void cryptoTransferNft() throws Exception {
.nft()
.customize(n -> n.tokenId(tokenEntity.getId()).serialNumber(1L).accountId(sender.toEntityId()))
.persist();
- final var receiver = accountEntityPersist();
- final var payer = accountEntityPersist();
+ final var receiver = accountEntityWithEvmAddressPersist();
+ final var payer = accountEntityWithEvmAddressPersist();
tokenAccountPersist(tokenEntity, payer);
tokenAccountPersist(tokenEntity, sender);
@@ -1318,7 +1318,7 @@ void updateTokenInfo() throws Exception {
@Test
void updateTokenExpiry() throws Exception {
// Given
- final var treasuryAccount = accountEntityPersist();
+ final var treasuryAccount = accountEntityWithEvmAddressPersist();
final var tokenWithAutoRenewPair =
persistTokenWithAutoRenewAndTreasuryAccounts(TokenTypeEnum.FUNGIBLE_COMMON, treasuryAccount);
@@ -1392,7 +1392,7 @@ private void verifyEthCallAndEstimateGas(
private HederaToken populateHederaToken(
final String contractAddress, final TokenTypeEnum tokenType, final EntityId treasuryAccountId) {
- final var autoRenewAccount = accountEntityPersist();
+ final var autoRenewAccount = accountEntityWithEvmAddressPersist();
final var tokenEntity = domainBuilder
.entity()
.customize(e -> e.type(EntityType.TOKEN).autoRenewAccountId(autoRenewAccount.getId()))
@@ -1423,7 +1423,7 @@ private HederaToken populateHederaToken(
private Pair persistTokenWithAutoRenewAndTreasuryAccounts(
final TokenTypeEnum tokenType, final Entity treasuryAccount) {
- final var autoRenewAccount = accountEntityPersist();
+ final var autoRenewAccount = accountEntityWithEvmAddressPersist();
final var tokenToUpdateEntity = domainBuilder
.entity()
.customize(e -> e.type(EntityType.TOKEN).autoRenewAccountId(autoRenewAccount.getId()))
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileReadonlyTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileReadonlyTest.java
index 22fa320650b..a4fe12b15d5 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileReadonlyTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServicePrecompileReadonlyTest.java
@@ -492,7 +492,7 @@ void getTokenKey(final TokenTypeEnum tokenType, final KeyValueType keyValueType,
@Test
void getCustomFeesForTokenWithFixedFee() throws Exception {
// Given
- final var collectorAccount = accountEntityPersist();
+ final var collectorAccount = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
final var fixedFee = com.hedera.mirror.common.domain.token.FixedFee.builder()
.amount(100L)
@@ -530,7 +530,7 @@ void getCustomFeesForTokenWithFixedFee() throws Exception {
@Test
void getCustomFeesForTokenWithFractionalFee() throws Exception {
// Given
- final var collectorAccount = accountEntityPersist();
+ final var collectorAccount = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
final var fractionalFee = FractionalFee.builder()
.collectorAccountId(collectorAccount.toEntityId())
@@ -572,7 +572,7 @@ void getCustomFeesForTokenWithFractionalFee() throws Exception {
@Test
void getCustomFeesForTokenWithRoyaltyFee() throws Exception {
// Given
- final var collectorAccount = accountEntityPersist();
+ final var collectorAccount = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
final var royaltyFee = RoyaltyFee.builder()
.collectorAccountId(collectorAccount.toEntityId())
@@ -618,7 +618,7 @@ void getExpiryForToken() throws Exception {
// Given
final var expiryPeriod = 9999999999999L;
final var autoRenewExpiry = 100000000L;
- final var autoRenewAccount = accountEntityPersist();
+ final var autoRenewAccount = accountEntityWithEvmAddressPersist();
final var tokenEntity = domainBuilder
.entity()
.customize(e -> e.type(EntityType.TOKEN)
@@ -653,8 +653,8 @@ void getExpiryForToken() throws Exception {
void getAllowanceForToken() throws Exception {
// Given
final var amountGranted = 50L;
- final var owner = accountEntityPersist();
- final var spender = accountEntityPersist();
+ final var owner = accountEntityWithEvmAddressPersist();
+ final var spender = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistFungibleToken();
domainBuilder
@@ -681,8 +681,8 @@ void getAllowanceForToken() throws Exception {
@Test
void isApprovedForAllNFT() throws Exception {
// Given
- final var owner = accountEntityPersist();
- final var spender = accountEntityPersist();
+ final var owner = accountEntityWithEvmAddressPersist();
+ final var spender = accountEntityWithEvmAddressPersist();
final var tokenEntity = persistNft();
domainBuilder
@@ -708,8 +708,8 @@ void isApprovedForAllNFT() throws Exception {
@Test
void getFungibleTokenInfo() throws Exception {
// Given
- final var treasury = accountEntityPersist();
- final var feeCollector = accountEntityPersist();
+ final var treasury = accountEntityWithEvmAddressPersist();
+ final var feeCollector = accountEntityWithEvmAddressPersist();
final var tokenEntity =
domainBuilder.entity().customize(e -> e.type(EntityType.TOKEN)).persist();
final var token = domainBuilder
@@ -775,8 +775,8 @@ void getFungibleTokenInfo() throws Exception {
void getNonFungibleTokenInfo() throws Exception {
// Given
final var owner = accountEntityPersist();
- final var treasury = accountEntityPersist();
- final var feeCollector = accountEntityPersist();
+ final var treasury = accountEntityWithEvmAddressPersist();
+ final var feeCollector = accountEntityWithEvmAddressPersist();
final var tokenEntity =
domainBuilder.entity().customize(e -> e.type(EntityType.TOKEN)).persist();
final var token = domainBuilder
@@ -852,8 +852,8 @@ void getNonFungibleTokenInfo() throws Exception {
@EnumSource(TokenTypeEnum.class)
void getTokenInfo(final TokenTypeEnum tokenType) throws Exception {
// Given
- final var treasury = accountEntityPersist();
- final var feeCollector = accountEntityPersist();
+ final var treasury = accountEntityWithEvmAddressPersist();
+ final var feeCollector = accountEntityWithEvmAddressPersist();
final var tokenEntity =
domainBuilder.entity().customize(e -> e.type(EntityType.TOKEN)).persist();
final var token = domainBuilder
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServiceTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServiceTest.java
index 2f6edd1780f..60e7442bfc2 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServiceTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/ContractCallServiceTest.java
@@ -52,6 +52,7 @@
import com.hedera.mirror.web3.service.model.CallServiceParameters.CallType;
import com.hedera.mirror.web3.service.model.ContractExecutionParameters;
import com.hedera.mirror.web3.service.utils.BinaryGasEstimator;
+import com.hedera.mirror.web3.state.MirrorNodeState;
import com.hedera.mirror.web3.throttle.ThrottleProperties;
import com.hedera.mirror.web3.viewmodel.BlockType;
import com.hedera.mirror.web3.web3j.generated.ERCTestContract;
@@ -61,10 +62,14 @@
import com.hedera.services.store.models.Id;
import com.hedera.services.utils.EntityIdUtils;
import io.github.bucket4j.Bucket;
+import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
+import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Stream;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.Address;
@@ -101,6 +106,9 @@ class ContractCallServiceTest extends AbstractContractCallServiceTest {
@Autowired
private ThrottleProperties throttleProperties;
+ @Autowired
+ private TransactionExecutionService transactionExecutionService;
+
@Resource
private ContractExecutionService contractExecutionService;
@@ -130,6 +138,13 @@ private static Stream ercPrecompileCallTypeArgumentsProvider() {
.flatMap(callType -> gasLimits.stream().map(gasLimit -> Arguments.of(callType, gasLimit)));
}
+ private static String toHexWith64LeadingZeros(final Long value) {
+ final String result;
+ final var paddedHexString = String.format("%064x", value);
+ result = "0x" + paddedHexString;
+ return result;
+ }
+
@Test
void callWithoutDataToAddressWithNoBytecodeReturnsEmptyResult() {
// Given
@@ -163,6 +178,38 @@ void pureCall() throws Exception {
assertGasUsedIsPositive(gasUsedBeforeExecution, ETH_CALL);
}
+ // This test will be removed in the future. Needed only for test coverage right now.
+ @Test
+ void pureCallModularizedServices() throws Exception {
+ // Given
+ final var modularizedServicesFlag = mirrorNodeEvmProperties.isModularizedServices();
+ mirrorNodeEvmProperties.setModularizedServices(true);
+ Method postConstructMethod = Arrays.stream(MirrorNodeState.class.getDeclaredMethods())
+ .filter(method -> method.isAnnotationPresent(PostConstruct.class))
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("@PostConstruct method not found"));
+
+ postConstructMethod.setAccessible(true); // Make the method accessible
+ postConstructMethod.invoke(state);
+
+ final var backupProperties = mirrorNodeEvmProperties.getProperties();
+ final Map propertiesMap = new HashMap<>();
+ propertiesMap.put("contracts.maxRefundPercentOfGasLimit", "100");
+ propertiesMap.put("contracts.maxGasPerSec", "15000000");
+ mirrorNodeEvmProperties.setProperties(propertiesMap);
+
+ final var contract = testWeb3jService.deploy(EthCall::deploy);
+ meterRegistry.clear(); // Clear it as the contract deploy increases the gas limit metric
+
+ // When
+ contract.call_multiplySimpleNumbers().send();
+
+ // Then
+ // Restore changed property values.
+ mirrorNodeEvmProperties.setModularizedServices(modularizedServicesFlag);
+ mirrorNodeEvmProperties.setProperties(backupProperties);
+ }
+
@ParameterizedTest
@MethodSource("provideBlockTypes")
void pureCallWithBlock(BlockType blockType) throws Exception {
@@ -715,7 +762,9 @@ void ercPrecompileExceptionalHaltReturnsExpectedGasToBucket(final CallType callT
mirrorEvmTxProcessor,
recordFileService,
throttleProperties,
- gasLimitBucket);
+ gasLimitBucket,
+ mirrorNodeEvmProperties,
+ transactionExecutionService);
// When
try {
@@ -748,7 +797,9 @@ void ercPrecompileContractRevertReturnsExpectedGasToBucket(final CallType callTy
mirrorEvmTxProcessor,
recordFileService,
throttleProperties,
- gasLimitBucket);
+ gasLimitBucket,
+ mirrorNodeEvmProperties,
+ transactionExecutionService);
// When
try {
@@ -782,7 +833,9 @@ void ercPrecompileSuccessReturnsExpectedGasToBucket(final CallType callType, fin
mirrorEvmTxProcessor,
recordFileService,
throttleProperties,
- gasLimitBucket);
+ gasLimitBucket,
+ mirrorNodeEvmProperties,
+ transactionExecutionService);
// When
try {
@@ -977,11 +1030,4 @@ void transferToNonExistingContract() {
assertGasLimit(serviceParameters);
}
}
-
- private static String toHexWith64LeadingZeros(final Long value) {
- final String result;
- final var paddedHexString = String.format("%064x", value);
- result = "0x" + paddedHexString;
- return result;
- }
}
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/RecordFileServiceTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/RecordFileServiceTest.java
index d890672b5fb..65d990bee93 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/RecordFileServiceTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/RecordFileServiceTest.java
@@ -42,11 +42,11 @@ void testFindByTimestamp() {
@Test
void testFindByBlockTypeEarliest() {
- var recordFileEarliest =
+ final var genesisRecordFile =
domainBuilder.recordFile().customize(f -> f.index(0L)).persist();
domainBuilder.recordFile().customize(f -> f.index(1L)).persist();
domainBuilder.recordFile().customize(f -> f.index(2L)).persist();
- assertThat(recordFileService.findByBlockType(BlockType.EARLIEST)).contains(recordFileEarliest);
+ assertThat(recordFileService.findByBlockType(BlockType.EARLIEST)).contains(genesisRecordFile);
}
@Test
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/TransactionExecutionServiceTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/TransactionExecutionServiceTest.java
new file mode 100644
index 00000000000..ae03196f74b
--- /dev/null
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/service/TransactionExecutionServiceTest.java
@@ -0,0 +1,274 @@
+/*
+ * 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.state.Utils.isMirror;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+import com.hedera.hapi.node.base.ResponseCodeEnum;
+import com.hedera.hapi.node.contract.ContractFunctionResult;
+import com.hedera.hapi.node.transaction.TransactionBody;
+import com.hedera.hapi.node.transaction.TransactionReceipt;
+import com.hedera.hapi.node.transaction.TransactionRecord;
+import com.hedera.mirror.web3.common.ContractCallContext;
+import com.hedera.mirror.web3.evm.contracts.execution.traceability.OpcodeTracer;
+import com.hedera.mirror.web3.evm.contracts.execution.traceability.OpcodeTracerOptions;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
+import com.hedera.mirror.web3.service.TransactionExecutionService.ExecutorFactory;
+import com.hedera.mirror.web3.service.model.CallServiceParameters;
+import com.hedera.mirror.web3.service.model.CallServiceParameters.CallType;
+import com.hedera.mirror.web3.service.model.ContractExecutionParameters;
+import com.hedera.mirror.web3.state.AliasesReadableKVState;
+import com.hedera.mirror.web3.viewmodel.BlockType;
+import com.hedera.mirror.web3.web3j.generated.NestedCalls;
+import com.hedera.node.app.service.evm.store.models.HederaEvmAccount;
+import com.hedera.node.app.state.SingleTransactionRecord;
+import com.hedera.node.app.workflows.standalone.TransactionExecutor;
+import com.hedera.pbj.runtime.io.buffer.Bytes;
+import com.swirlds.state.State;
+import com.swirlds.state.spi.ReadableKVState;
+import com.swirlds.state.spi.ReadableStates;
+import java.time.Instant;
+import java.util.List;
+import java.util.stream.Stream;
+import org.hyperledger.besu.datatypes.Address;
+import org.hyperledger.besu.evm.tracing.OperationTracer;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class TransactionExecutionServiceTest {
+ private static final Long DEFAULT_GAS = 50000L;
+
+ @Mock
+ private State mirrorNodeState;
+
+ @Mock
+ private MirrorNodeEvmProperties mirrorNodeEvmProperties;
+
+ @Mock
+ private OpcodeTracer opcodeTracer;
+
+ @Mock
+ private TransactionExecutor transactionExecutor;
+
+ @Mock
+ private ContractCallContext contractCallContext;
+
+ private TransactionExecutionService transactionExecutionService;
+
+ private static Stream provideCallData() {
+ return Stream.of(
+ Arguments.of(org.apache.tuweni.bytes.Bytes.EMPTY),
+ Arguments.of(org.apache.tuweni.bytes.Bytes.fromHexString(NestedCalls.BINARY)));
+ }
+
+ @BeforeEach
+ void setUp() {
+ transactionExecutionService =
+ new TransactionExecutionService(mirrorNodeState, mirrorNodeEvmProperties, opcodeTracer);
+ }
+
+ @ParameterizedTest
+ @ValueSource(
+ strings = {
+ "0x0000000000000000000000000000000000000000",
+ "0x1234",
+ "0x627306090abab3a6e1400e9345bc60c78a8bef57"
+ })
+ void testExecuteContractCallSuccess(String senderAddressHex) {
+ // Given
+ try (MockedStatic executorFactoryMock = mockStatic(ExecutorFactory.class);
+ MockedStatic contractCallContextMock = mockStatic(ContractCallContext.class)) {
+
+ // Set up mock behaviors for ExecutorFactory
+ executorFactoryMock
+ .when(() -> ExecutorFactory.newExecutor(any(), any(), any()))
+ .thenReturn(transactionExecutor);
+
+ // Set up mock behaviors for ContractCallContext
+ contractCallContextMock.when(ContractCallContext::get).thenReturn(contractCallContext);
+ when(contractCallContext.getOpcodeTracerOptions()).thenReturn(new OpcodeTracerOptions());
+
+ // Mock the SingleTransactionRecord and TransactionRecord
+ SingleTransactionRecord singleTransactionRecord = mock(SingleTransactionRecord.class);
+ TransactionRecord transactionRecord = mock(TransactionRecord.class);
+ TransactionReceipt transactionReceipt = mock(TransactionReceipt.class);
+
+ // Simulate SUCCESS status in the receipt
+ when(transactionReceipt.status()).thenReturn(ResponseCodeEnum.SUCCESS);
+ when(transactionRecord.receiptOrThrow()).thenReturn(transactionReceipt);
+ when(singleTransactionRecord.transactionRecord()).thenReturn(transactionRecord);
+
+ ContractFunctionResult contractFunctionResult = mock(ContractFunctionResult.class);
+ when(contractFunctionResult.gasUsed()).thenReturn(DEFAULT_GAS);
+ when(contractFunctionResult.contractCallResult()).thenReturn(Bytes.EMPTY);
+
+ // Mock the transactionRecord to return the contract call result
+ when(transactionRecord.contractCallResultOrThrow()).thenReturn(contractFunctionResult);
+
+ final var senderAddress = Address.fromHexString(senderAddressHex);
+ if (!isMirror(senderAddress)) {
+ final var readableStates = mock(ReadableStates.class);
+ when(mirrorNodeState.getReadableStates(any())).thenReturn(readableStates);
+
+ final var aliasesReadableKVState = mock(ReadableKVState.class);
+ when(readableStates.get(AliasesReadableKVState.KEY)).thenReturn(aliasesReadableKVState);
+ }
+
+ // Mock the executor to return a List with the mocked SingleTransactionRecord
+ when(transactionExecutor.execute(
+ any(TransactionBody.class), any(Instant.class), any(OperationTracer[].class)))
+ .thenReturn(List.of(singleTransactionRecord));
+
+ CallServiceParameters callServiceParameters =
+ buildServiceParams(false, org.apache.tuweni.bytes.Bytes.EMPTY, senderAddress);
+
+ // When
+ var result = transactionExecutionService.execute(callServiceParameters, DEFAULT_GAS);
+
+ // Then
+ assertThat(result).isNotNull();
+ assertThat(result.getGasUsed()).isEqualTo(DEFAULT_GAS);
+ assertThat(result.getRevertReason()).isNotPresent();
+ }
+ }
+
+ @Test
+ void testExecuteContractCallFailure() {
+ // Given
+ try (MockedStatic executorFactoryMock = mockStatic(ExecutorFactory.class);
+ MockedStatic contractCallContextMock = mockStatic(ContractCallContext.class)) {
+
+ // Set up mock behaviors for ExecutorFactory
+ executorFactoryMock
+ .when(() -> ExecutorFactory.newExecutor(any(), any(), any()))
+ .thenReturn(transactionExecutor);
+
+ // Set up mock behaviors for ContractCallContext
+ contractCallContextMock.when(ContractCallContext::get).thenReturn(contractCallContext);
+
+ // Mock the SingleTransactionRecord and TransactionRecord
+ SingleTransactionRecord singleTransactionRecord = mock(SingleTransactionRecord.class);
+ TransactionRecord transactionRecord = mock(TransactionRecord.class);
+ TransactionReceipt transactionReceipt = mock(TransactionReceipt.class);
+
+ // Simulate CONTRACT_REVERT_EXECUTED status in the receipt
+ when(transactionReceipt.status()).thenReturn(ResponseCodeEnum.CONTRACT_REVERT_EXECUTED);
+ when(transactionRecord.receiptOrThrow()).thenReturn(transactionReceipt);
+ when(transactionRecord.receipt()).thenReturn(transactionReceipt);
+ when(singleTransactionRecord.transactionRecord()).thenReturn(transactionRecord);
+
+ ContractFunctionResult contractFunctionResult = mock(ContractFunctionResult.class);
+ when(transactionRecord.contractCallResultOrThrow()).thenReturn(contractFunctionResult);
+ when(contractFunctionResult.gasUsed()).thenReturn(DEFAULT_GAS);
+
+ // Mock the executor to return a List with the mocked SingleTransactionRecord
+ when(transactionExecutor.execute(
+ any(TransactionBody.class), any(Instant.class), any(OperationTracer[].class)))
+ .thenReturn(List.of(singleTransactionRecord));
+
+ CallServiceParameters callServiceParameters =
+ buildServiceParams(false, org.apache.tuweni.bytes.Bytes.EMPTY, Address.ZERO);
+
+ // When
+ var result = transactionExecutionService.execute(callServiceParameters, DEFAULT_GAS);
+
+ // Then
+ assertThat(result).isNotNull();
+ assertThat(result.getGasUsed()).isEqualTo(DEFAULT_GAS);
+ assertThat(result.getRevertReason()).isPresent();
+ }
+ }
+
+ // NestedCalls.BINARY
+ @ParameterizedTest
+ @MethodSource("provideCallData")
+ void testExecuteContractCreateSuccess(org.apache.tuweni.bytes.Bytes callData) {
+ // Given
+ try (MockedStatic executorFactoryMock = mockStatic(ExecutorFactory.class);
+ MockedStatic contractCallContextMock = mockStatic(ContractCallContext.class)) {
+
+ // Set up mock behaviors for ExecutorFactory
+ executorFactoryMock
+ .when(() -> ExecutorFactory.newExecutor(any(), any(), any()))
+ .thenReturn(transactionExecutor);
+
+ // Set up mock behaviors for ContractCallContext
+ contractCallContextMock.when(ContractCallContext::get).thenReturn(contractCallContext);
+ when(contractCallContext.getOpcodeTracerOptions()).thenReturn(new OpcodeTracerOptions());
+
+ // Mock the SingleTransactionRecord and TransactionRecord
+ SingleTransactionRecord singleTransactionRecord = mock(SingleTransactionRecord.class);
+ TransactionRecord transactionRecord = mock(TransactionRecord.class);
+ TransactionReceipt transactionReceipt = mock(TransactionReceipt.class);
+
+ when(transactionReceipt.status()).thenReturn(ResponseCodeEnum.SUCCESS);
+ when(transactionRecord.receiptOrThrow()).thenReturn(transactionReceipt);
+ when(singleTransactionRecord.transactionRecord()).thenReturn(transactionRecord);
+
+ ContractFunctionResult contractFunctionResult = mock(ContractFunctionResult.class);
+ when(contractFunctionResult.gasUsed()).thenReturn(DEFAULT_GAS);
+ when(contractFunctionResult.contractCallResult()).thenReturn(Bytes.EMPTY);
+
+ // Mock the transactionRecord to return the contract call result
+ when(transactionRecord.contractCreateResultOrThrow()).thenReturn(contractFunctionResult);
+
+ // Mock the executor to return a List with the mocked SingleTransactionRecord
+ when(transactionExecutor.execute(
+ any(TransactionBody.class), any(Instant.class), any(OperationTracer[].class)))
+ .thenReturn(List.of(singleTransactionRecord));
+
+ CallServiceParameters callServiceParameters = buildServiceParams(true, callData, Address.ZERO);
+
+ // When
+ var result = transactionExecutionService.execute(callServiceParameters, DEFAULT_GAS);
+
+ // Then
+ assertThat(result).isNotNull();
+ assertThat(result.getGasUsed()).isEqualTo(DEFAULT_GAS);
+ assertThat(result.getRevertReason()).isNotPresent();
+ }
+ }
+
+ private CallServiceParameters buildServiceParams(
+ boolean isContractCreate, org.apache.tuweni.bytes.Bytes callData, final Address senderAddress) {
+ return ContractExecutionParameters.builder()
+ .block(BlockType.LATEST)
+ .callData(callData)
+ .callType(CallType.ETH_CALL)
+ .gas(DEFAULT_GAS)
+ .isEstimate(false)
+ .isStatic(true)
+ .receiver(isContractCreate ? Address.ZERO : Address.fromHexString("0x1234"))
+ .sender(new HederaEvmAccount(senderAddress))
+ .value(0)
+ .build();
+ }
+}
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateIntegrationTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateIntegrationTest.java
index 365bad99d84..5a3298566da 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateIntegrationTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateIntegrationTest.java
@@ -19,6 +19,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import com.hedera.mirror.web3.Web3IntegrationTest;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
import com.hedera.node.app.fees.FeeService;
import com.hedera.node.app.ids.EntityIdService;
import com.hedera.node.app.records.BlockRecordService;
@@ -46,9 +47,14 @@ public class MirrorNodeStateIntegrationTest extends Web3IntegrationTest {
private final MirrorNodeState mirrorNodeState;
private final ServicesRegistry servicesRegistry;
+ private final MirrorNodeEvmProperties mirrorNodeEvmProperties;
@Test
void verifyMirrorNodeStateHasRegisteredServices() {
+ if (!mirrorNodeEvmProperties.isModularizedServices()) {
+ return;
+ }
+
Set> expectedServices = new HashSet<>(List.of(
EntityIdService.class,
TokenServiceImpl.class,
@@ -72,6 +78,10 @@ void verifyMirrorNodeStateHasRegisteredServices() {
@Test
void verifyServicesHaveAssignedDataSources() {
+ if (!mirrorNodeEvmProperties.isModularizedServices()) {
+ return;
+ }
+
final var states = mirrorNodeState.getStates();
// BlockRecordService
@@ -81,7 +91,7 @@ void verifyServicesHaveAssignedDataSources() {
verifyServiceDataSources(states, BlockRecordService.NAME, blockRecordServiceDataSources);
// FileService
- Map> fileServiceDataSources = Map.of("FILES", Map.class);
+ Map> fileServiceDataSources = Map.of(FileReadableKVState.KEY, Map.class);
verifyServiceDataSources(states, FileService.NAME, fileServiceDataSources);
// CongestionThrottleService
@@ -96,8 +106,8 @@ void verifyServicesHaveAssignedDataSources() {
// ContractService
Map> contractServiceDataSources = Map.of(
- "BYTECODE", Map.class,
- "STORAGE", Map.class);
+ ContractBytecodeReadableKVState.KEY, Map.class,
+ ContractStorageReadableKVState.KEY, Map.class);
verifyServiceDataSources(states, ContractService.NAME, contractServiceDataSources);
// RecordCacheService
@@ -110,13 +120,20 @@ void verifyServicesHaveAssignedDataSources() {
// TokenService
Map> tokenServiceDataSources = Map.of(
- "ACCOUNTS", Map.class,
- "PENDING_AIRDROPS", Map.class,
- "ALIASES", Map.class,
- "NFTS", Map.class,
- "TOKENS", Map.class,
- "TOKEN_RELS", Map.class,
- "STAKING_NETWORK_REWARDS", AtomicReference.class);
+ AccountReadableKVState.KEY,
+ Map.class,
+ "PENDING_AIRDROPS",
+ Map.class,
+ AliasesReadableKVState.KEY,
+ Map.class,
+ NftReadableKVState.KEY,
+ Map.class,
+ TokenReadableKVState.KEY,
+ Map.class,
+ TokenRelationshipReadableKVState.KEY,
+ Map.class,
+ "STAKING_NETWORK_REWARDS",
+ AtomicReference.class);
verifyServiceDataSources(states, TokenService.NAME, tokenServiceDataSources);
}
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateTest.java
index 64056dc4f2f..cce6fbf9186 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateTest.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.hedera.mirror.web3.evm.properties.MirrorNodeEvmProperties;
import com.hedera.mirror.web3.state.core.MapReadableStates;
import com.hedera.mirror.web3.state.core.MapWritableKVState;
import com.hedera.mirror.web3.state.core.MapWritableStates;
@@ -92,6 +93,9 @@ class MirrorNodeStateTest {
@Mock
private ServiceMigrator serviceMigrator;
+ @Mock
+ private MirrorNodeEvmProperties mirrorNodeEvmProperties;
+
@Mock
private NetworkInfo networkInfo;
@@ -121,91 +125,104 @@ void setup() {
@Test
void testAddService() {
- when(fileReadableKVState.getStateKey()).thenReturn("FILES");
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
-
- assertThat(mirrorNodeState.getReadableStates("NEW").contains("FILES")).isFalse();
- final var newState =
- mirrorNodeState.addService("NEW", new HashMap<>(Map.of("FILES", Map.of("FILES", fileReadableKVState))));
- assertThat(newState.getReadableStates("NEW").contains("FILES")).isTrue();
+ when(fileReadableKVState.getStateKey()).thenReturn(FileReadableKVState.KEY);
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
+
+ assertThat(mirrorNodeState.getReadableStates("NEW").contains(FileReadableKVState.KEY))
+ .isFalse();
+ final var newState = mirrorNodeState.addService(
+ "NEW",
+ new HashMap<>(Map.of(FileReadableKVState.KEY, Map.of(FileReadableKVState.KEY, fileReadableKVState))));
+ assertThat(newState.getReadableStates("NEW").contains(FileReadableKVState.KEY))
+ .isTrue();
}
@Test
void testRemoveService() {
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
final var testStates = new HashMap<>(Map.of(
- "BYTECODE", Map.of("BYTECODE", contractBytecodeReadableKVState),
- "STORAGE", Map.of("STORAGE", contractStorageReadableKVState)));
+ ContractBytecodeReadableKVState.KEY,
+ Map.of(ContractBytecodeReadableKVState.KEY, contractBytecodeReadableKVState),
+ ContractStorageReadableKVState.KEY,
+ Map.of(ContractStorageReadableKVState.KEY, contractStorageReadableKVState)));
final var newState = mirrorNodeState.addService("NEW", testStates);
- assertThat(newState.getReadableStates("NEW").contains("BYTECODE")).isTrue();
- assertThat(newState.getReadableStates("NEW").contains("STORAGE")).isTrue();
- newState.removeServiceState("NEW", "BYTECODE");
- assertThat(newState.getReadableStates("NEW").contains("BYTECODE")).isFalse();
- assertThat(newState.getReadableStates("NEW").contains("STORAGE")).isTrue();
+ assertThat(newState.getReadableStates("NEW").contains(ContractBytecodeReadableKVState.KEY))
+ .isTrue();
+ assertThat(newState.getReadableStates("NEW").contains(ContractStorageReadableKVState.KEY))
+ .isTrue();
+ newState.removeServiceState("NEW", ContractBytecodeReadableKVState.KEY);
+ assertThat(newState.getReadableStates("NEW").contains(ContractBytecodeReadableKVState.KEY))
+ .isFalse();
+ assertThat(newState.getReadableStates("NEW").contains(ContractStorageReadableKVState.KEY))
+ .isTrue();
}
@Test
void testGetReadableStatesForFileService() {
- when(fileReadableKVState.getStateKey()).thenReturn("FILES");
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
+ when(fileReadableKVState.getStateKey()).thenReturn(FileReadableKVState.KEY);
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
final var readableStates = mirrorNodeState.getReadableStates(FileService.NAME);
- assertThat(readableStates).isEqualTo(new MapReadableStates(Map.of("FILES", fileReadableKVState)));
+ assertThat(readableStates)
+ .isEqualTo(new MapReadableStates(Map.of(FileReadableKVState.KEY, fileReadableKVState)));
}
@Test
void testGetReadableStatesForContractService() {
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
final var readableStates = mirrorNodeState.getReadableStates(ContractService.NAME);
assertThat(readableStates)
.isEqualTo(new MapReadableStates(Map.of(
- "BYTECODE", contractBytecodeReadableKVState, "STORAGE", contractStorageReadableKVState)));
+ ContractBytecodeReadableKVState.KEY,
+ contractBytecodeReadableKVState,
+ ContractStorageReadableKVState.KEY,
+ contractStorageReadableKVState)));
}
@Test
void testGetReadableStatesForTokenService() {
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(nftReadableKVState.getStateKey()).thenReturn("NFTS");
- when(tokenReadableKVState.getStateKey()).thenReturn("TOKENS");
- when(tokenRelationshipReadableKVState.getStateKey()).thenReturn("TOKEN_RELS");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
- when(fileReadableKVState.getStateKey()).thenReturn("FILES");
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(nftReadableKVState.getStateKey()).thenReturn(NftReadableKVState.KEY);
+ when(tokenReadableKVState.getStateKey()).thenReturn(TokenReadableKVState.KEY);
+ when(tokenRelationshipReadableKVState.getStateKey()).thenReturn(TokenRelationshipReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
+ when(fileReadableKVState.getStateKey()).thenReturn(FileReadableKVState.KEY);
final var readableStates = mirrorNodeState.getReadableStates(TokenService.NAME);
assertThat(readableStates)
.isEqualTo(new MapReadableStates(Map.of(
- "ACCOUNTS",
+ AccountReadableKVState.KEY,
accountReadableKVState,
- "PENDING_AIRDROPS",
+ AirdropsReadableKVState.KEY,
airdropsReadableKVState,
- "ALIASES",
+ AliasesReadableKVState.KEY,
aliasesReadableKVState,
- "NFTS",
+ NftReadableKVState.KEY,
nftReadableKVState,
- "TOKENS",
+ TokenReadableKVState.KEY,
tokenReadableKVState,
- "TOKEN_RELS",
+ TokenRelationshipReadableKVState.KEY,
tokenRelationshipReadableKVState)));
}
@@ -235,85 +252,99 @@ void testGetReadableStatesWithQueue() {
@Test
void testGetWritableStatesForFileService() {
- when(fileReadableKVState.getStateKey()).thenReturn("FILES");
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
+ when(fileReadableKVState.getStateKey()).thenReturn(FileReadableKVState.KEY);
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
final var writableStates = mirrorNodeState.getWritableStates(FileService.NAME);
final var readableStates = mirrorNodeState.getReadableStates(FileService.NAME);
assertThat(writableStates)
- .isEqualTo(new MapWritableStates(
- Map.of("FILES", new MapWritableKVState<>("FILES", readableStates.get("FILES")))));
+ .isEqualTo(new MapWritableStates(Map.of(
+ FileReadableKVState.KEY,
+ new MapWritableKVState<>(
+ FileReadableKVState.KEY, readableStates.get(FileReadableKVState.KEY)))));
}
@Test
void testGetWritableStatesForFileServiceWithListeners() {
when(listener.stateTypes()).thenReturn(Set.of(StateType.MAP));
- when(fileReadableKVState.getStateKey()).thenReturn("FILES");
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
+ when(fileReadableKVState.getStateKey()).thenReturn(FileReadableKVState.KEY);
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
mirrorNodeState.registerCommitListener(listener);
final var writableStates = mirrorNodeState.getWritableStates(FileService.NAME);
final var readableStates = mirrorNodeState.getReadableStates(FileService.NAME);
assertThat(writableStates)
- .isEqualTo(new MapWritableStates(
- Map.of("FILES", new MapWritableKVState<>("FILES", readableStates.get("FILES")))));
+ .isEqualTo(new MapWritableStates(Map.of(
+ FileReadableKVState.KEY,
+ new MapWritableKVState<>(
+ FileReadableKVState.KEY, readableStates.get(FileReadableKVState.KEY)))));
}
@Test
void testGetWritableStatesForContractService() {
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
final var writableStates = mirrorNodeState.getWritableStates(ContractService.NAME);
final var readableStates = mirrorNodeState.getReadableStates(ContractService.NAME);
assertThat(writableStates)
.isEqualTo(new MapWritableStates(Map.of(
- "BYTECODE",
- new MapWritableKVState<>("BYTECODE", readableStates.get("BYTECODE")),
- "STORAGE",
- new MapWritableKVState<>("STORAGE", readableStates.get("STORAGE")))));
+ ContractBytecodeReadableKVState.KEY,
+ new MapWritableKVState<>(
+ ContractBytecodeReadableKVState.KEY,
+ readableStates.get(ContractBytecodeReadableKVState.KEY)),
+ ContractStorageReadableKVState.KEY,
+ new MapWritableKVState<>(
+ ContractStorageReadableKVState.KEY,
+ readableStates.get(ContractStorageReadableKVState.KEY)))));
}
@Test
void testGetWritableStatesForTokenService() {
- when(accountReadableKVState.getStateKey()).thenReturn("ACCOUNTS");
- when(airdropsReadableKVState.getStateKey()).thenReturn("PENDING_AIRDROPS");
- when(aliasesReadableKVState.getStateKey()).thenReturn("ALIASES");
- when(nftReadableKVState.getStateKey()).thenReturn("NFTS");
- when(tokenReadableKVState.getStateKey()).thenReturn("TOKENS");
- when(tokenRelationshipReadableKVState.getStateKey()).thenReturn("TOKEN_RELS");
- when(contractBytecodeReadableKVState.getStateKey()).thenReturn("BYTECODE");
- when(contractStorageReadableKVState.getStateKey()).thenReturn("STORAGE");
- when(fileReadableKVState.getStateKey()).thenReturn("FILES");
+ when(accountReadableKVState.getStateKey()).thenReturn(AccountReadableKVState.KEY);
+ when(airdropsReadableKVState.getStateKey()).thenReturn(AirdropsReadableKVState.KEY);
+ when(aliasesReadableKVState.getStateKey()).thenReturn(AliasesReadableKVState.KEY);
+ when(nftReadableKVState.getStateKey()).thenReturn(NftReadableKVState.KEY);
+ when(tokenReadableKVState.getStateKey()).thenReturn(TokenReadableKVState.KEY);
+ when(tokenRelationshipReadableKVState.getStateKey()).thenReturn(TokenRelationshipReadableKVState.KEY);
+ when(contractBytecodeReadableKVState.getStateKey()).thenReturn(ContractBytecodeReadableKVState.KEY);
+ when(contractStorageReadableKVState.getStateKey()).thenReturn(ContractStorageReadableKVState.KEY);
+ when(fileReadableKVState.getStateKey()).thenReturn(FileReadableKVState.KEY);
final var writableStates = mirrorNodeState.getWritableStates(TokenService.NAME);
final var readableStates = mirrorNodeState.getReadableStates(TokenService.NAME);
assertThat(writableStates)
.isEqualTo(new MapWritableStates(Map.of(
- "ACCOUNTS",
- new MapWritableKVState<>("ACCOUNTS", readableStates.get("ACCOUNTS")),
- "PENDING_AIRDROPS",
- new MapWritableKVState<>("PENDING_AIRDROPS", readableStates.get("PENDING_AIRDROPS")),
- "ALIASES",
- new MapWritableKVState<>("ALIASES", readableStates.get("ALIASES")),
- "NFTS",
- new MapWritableKVState<>("NFTS", readableStates.get("NFTS")),
- "TOKENS",
- new MapWritableKVState<>("TOKENS", readableStates.get("TOKENS")),
- "TOKEN_RELS",
- new MapWritableKVState<>("TOKEN_RELS", readableStates.get("TOKEN_RELS")))));
+ AccountReadableKVState.KEY,
+ new MapWritableKVState<>(
+ AccountReadableKVState.KEY, readableStates.get(AccountReadableKVState.KEY)),
+ AirdropsReadableKVState.KEY,
+ new MapWritableKVState<>(
+ AirdropsReadableKVState.KEY, readableStates.get(AirdropsReadableKVState.KEY)),
+ AliasesReadableKVState.KEY,
+ new MapWritableKVState<>(
+ AliasesReadableKVState.KEY, readableStates.get(AliasesReadableKVState.KEY)),
+ NftReadableKVState.KEY,
+ new MapWritableKVState<>(NftReadableKVState.KEY, readableStates.get(NftReadableKVState.KEY)),
+ TokenReadableKVState.KEY,
+ new MapWritableKVState<>(
+ TokenReadableKVState.KEY, readableStates.get(TokenReadableKVState.KEY)),
+ TokenRelationshipReadableKVState.KEY,
+ new MapWritableKVState<>(
+ TokenRelationshipReadableKVState.KEY,
+ readableStates.get(TokenRelationshipReadableKVState.KEY)))));
}
@Test
@@ -425,17 +456,21 @@ void testHashCode() {
}
private MirrorNodeState initStateAfterMigration() {
- final Map fileStateData = new HashMap<>(Map.of("FILES", Map.of("FILES", fileReadableKVState)));
+ final Map fileStateData =
+ new HashMap<>(Map.of(FileReadableKVState.KEY, Map.of(FileReadableKVState.KEY, fileReadableKVState)));
final Map contractStateData = new HashMap<>(Map.of(
- "BYTECODE", Map.of("BYTECODE", contractBytecodeReadableKVState),
- "STORAGE", Map.of("STORAGE", contractStorageReadableKVState)));
+ ContractBytecodeReadableKVState.KEY,
+ Map.of(ContractBytecodeReadableKVState.KEY, contractBytecodeReadableKVState),
+ ContractStorageReadableKVState.KEY,
+ Map.of(ContractStorageReadableKVState.KEY, contractStorageReadableKVState)));
final Map tokenStateData = new HashMap<>(Map.of(
- "ACCOUNTS", Map.of("ACCOUNTS", accountReadableKVState),
- "PENDING_AIRDROPS", Map.of("PENDING_AIRDROPS", airdropsReadableKVState),
- "ALIASES", Map.of("ALIASES", aliasesReadableKVState),
- "NFTS", Map.of("NFTS", nftReadableKVState),
- "TOKENS", Map.of("TOKENS", tokenReadableKVState),
- "TOKEN_RELS", Map.of("TOKEN_RELS", tokenRelationshipReadableKVState)));
+ AccountReadableKVState.KEY, Map.of(AccountReadableKVState.KEY, accountReadableKVState),
+ AirdropsReadableKVState.KEY, Map.of(AirdropsReadableKVState.KEY, airdropsReadableKVState),
+ AliasesReadableKVState.KEY, Map.of(AliasesReadableKVState.KEY, aliasesReadableKVState),
+ NftReadableKVState.KEY, Map.of(NftReadableKVState.KEY, nftReadableKVState),
+ TokenReadableKVState.KEY, Map.of(TokenReadableKVState.KEY, tokenReadableKVState),
+ TokenRelationshipReadableKVState.KEY,
+ Map.of(TokenRelationshipReadableKVState.KEY, tokenRelationshipReadableKVState)));
// Add service using the mock data source
return buildStateObject()
@@ -445,6 +480,12 @@ private MirrorNodeState initStateAfterMigration() {
}
private MirrorNodeState buildStateObject() {
- return new MirrorNodeState(readableKVStates, servicesRegistry, serviceMigrator, networkInfo, startupNetworks);
+ return new MirrorNodeState(
+ readableKVStates,
+ servicesRegistry,
+ serviceMigrator,
+ networkInfo,
+ startupNetworks,
+ mirrorNodeEvmProperties);
}
}
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapReadableKVStateTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapReadableKVStateTest.java
index abddf9c2b5c..146aa88f7dd 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapReadableKVStateTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapReadableKVStateTest.java
@@ -20,6 +20,8 @@
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.state.token.Account;
+import com.hedera.mirror.web3.state.AccountReadableKVState;
+import com.hedera.mirror.web3.state.AliasesReadableKVState;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -43,7 +45,7 @@ class MapReadableKVStateTest {
@BeforeEach
void setup() {
accountMap = Map.of(accountID, account);
- mapReadableKVState = new MapReadableKVState<>("ACCOUNTS", accountMap);
+ mapReadableKVState = new MapReadableKVState<>(AccountReadableKVState.KEY, accountMap);
}
@Test
@@ -70,7 +72,7 @@ void testSize() {
final var accountID1 = AccountID.newBuilder().accountNum(1L).build();
final var accountID2 = AccountID.newBuilder().accountNum(2L).build();
final var mapReadableKVStateBigger = new MapReadableKVState<>(
- "ACCOUNTS",
+ AccountReadableKVState.KEY,
Map.of(
accountID1,
Account.newBuilder().accountId(accountID1).build(),
@@ -96,26 +98,27 @@ void testEqualsWithNull() {
@Test
void testEqualsSameValues() {
- MapReadableKVState other = new MapReadableKVState<>("ACCOUNTS", accountMap);
+ MapReadableKVState other = new MapReadableKVState<>(AccountReadableKVState.KEY, accountMap);
assertThat(mapReadableKVState).isEqualTo(other);
}
@Test
void testEqualsDifferentKeys() {
- MapReadableKVState other = new MapReadableKVState<>("ALIASES", accountMap);
+ MapReadableKVState other = new MapReadableKVState<>(AliasesReadableKVState.KEY, accountMap);
assertThat(mapReadableKVState).isNotEqualTo(other);
}
@Test
void testEqualsDifferentValues() {
final var accountMapOther = Map.of(AccountID.newBuilder().accountNum(3L).build(), account);
- MapReadableKVState other = new MapReadableKVState<>("ACCOUNTS", accountMapOther);
+ MapReadableKVState other =
+ new MapReadableKVState<>(AccountReadableKVState.KEY, accountMapOther);
assertThat(mapReadableKVState).isNotEqualTo(other);
}
@Test
void testHashCode() {
- MapReadableKVState other = new MapReadableKVState<>("ACCOUNTS", accountMap);
+ MapReadableKVState other = new MapReadableKVState<>(AccountReadableKVState.KEY, accountMap);
assertThat(mapReadableKVState).hasSameHashCodeAs(other);
}
}
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapWritableKVStateTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapWritableKVStateTest.java
index 731091c2fa5..6f70c81e89a 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapWritableKVStateTest.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/core/MapWritableKVStateTest.java
@@ -22,6 +22,8 @@
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.state.token.Account;
+import com.hedera.mirror.web3.state.AccountReadableKVState;
+import com.hedera.mirror.web3.state.AliasesReadableKVState;
import com.swirlds.state.spi.ReadableKVState;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
@@ -47,7 +49,7 @@ class MapWritableKVStateTest {
@BeforeEach
void setup() {
- mapWritableKVState = new MapWritableKVState<>("ACCOUNTS", readableKVState);
+ mapWritableKVState = new MapWritableKVState<>(AccountReadableKVState.KEY, readableKVState);
}
@Test
@@ -113,21 +115,24 @@ void testEqualsWithNull() {
@Test
void testEqualsDifferentKeys() {
- MapWritableKVState other = new MapWritableKVState<>("ALIASES", readableKVState);
+ MapWritableKVState other =
+ new MapWritableKVState<>(AliasesReadableKVState.KEY, readableKVState);
assertThat(mapWritableKVState).isNotEqualTo(other);
}
@Test
void testEqualsDifferentValues() {
final var readableKVStateMock = mock(ReadableKVState.class);
- MapWritableKVState other = new MapWritableKVState<>("ACCOUNTS", readableKVStateMock);
+ MapWritableKVState other =
+ new MapWritableKVState<>(AccountReadableKVState.KEY, readableKVStateMock);
other.put(accountID, account);
assertThat(mapWritableKVState).isNotEqualTo(other);
}
@Test
void testHashCode() {
- MapWritableKVState other = new MapWritableKVState<>("ACCOUNTS", readableKVState);
+ MapWritableKVState other =
+ new MapWritableKVState<>(AccountReadableKVState.KEY, readableKVState);
assertThat(mapWritableKVState).hasSameHashCodeAs(other);
}
}
diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/web3j/TestWeb3jService.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/web3j/TestWeb3jService.java
index ca144549f75..9a105b46224 100644
--- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/web3j/TestWeb3jService.java
+++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/web3j/TestWeb3jService.java
@@ -113,7 +113,6 @@ public void reset() {
this.isEstimateGas = false;
this.contractRuntime = null;
this.persistContract = true;
- this.sender = Address.ZERO;
this.value = 0L;
this.sender = Address.fromHexString("");
this.blockType = BlockType.LATEST;