From c29f2a9942b8992141fbbbe36a5d52116feaf267 Mon Sep 17 00:00:00 2001 From: Bilyana Gospodinova Date: Fri, 22 Nov 2024 11:15:14 +0200 Subject: [PATCH 1/2] Add file id and init bytecode in context Signed-off-by: Bilyana Gospodinova --- .../web3/common/ContractCallContext.java | 26 +++++++++++++++++++ .../web3/state/FileReadableKVState.java | 14 +++++++++- .../web3/state/FileReadableKVStateTest.java | 20 ++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) 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 e409c129ac..e46e454cf2 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 @@ -16,6 +16,9 @@ package com.hedera.mirror.web3.common; +import com.hedera.hapi.node.base.FileID; +import com.hedera.hapi.node.contract.ContractCreateTransactionBody; +import com.hedera.hapi.node.file.FileCreateTransactionBody; import com.hedera.mirror.common.domain.contract.ContractAction; import com.hedera.mirror.common.domain.transaction.RecordFile; import com.hedera.mirror.web3.evm.contracts.execution.traceability.Opcode; @@ -23,6 +26,8 @@ import com.hedera.mirror.web3.evm.contracts.execution.traceability.OpcodeTracerOptions; import com.hedera.mirror.web3.evm.store.CachingStateFrame; import com.hedera.mirror.web3.evm.store.StackedStateFrames; +import com.hedera.mirror.web3.state.FileReadableKVState; +import com.hedera.pbj.runtime.io.buffer.Bytes; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.List; @@ -73,6 +78,25 @@ public class ContractCallContext { @Setter private Optional timestamp = Optional.empty(); + /** + * 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 fields "fileID" and "initBytecode" - to hold temporary these values + * during contract deploy. + */ + @Setter + private Optional fileID = Optional.empty(); + + @Setter + private Optional initBytecode = Optional.empty(); + private ContractCallContext() {} public static ContractCallContext get() { @@ -90,6 +114,8 @@ public static T run(Function function) { public void reset() { recordFile = null; stack = stackBase; + fileID = Optional.empty(); + initBytecode = Optional.empty(); } public int getStackHeight() { 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 3e1f30783a..b353ca3638 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 @@ -56,7 +56,19 @@ public FileReadableKVState(final FileDataRepository fileDataRepository, final En @Override protected File readFromDataSource(@Nonnull FileID key) { final var timestamp = ContractCallContext.getTimestamp(); - final var fileId = toEntityId(key).getId(); + final var fileEntityId = toEntityId(key); + final var fileId = fileEntityId.getId(); + + final var contextFileId = ContractCallContext.get().getFileID(); + // If we are in a contract create case, the fileID and the init bytecode are in the ContractCallContext. + if (contextFileId.isPresent() + && contextFileId.get().equals(key) + && ContractCallContext.get().getInitBytecode().isPresent()) { + return File.newBuilder() + .fileId(key) + .contents(ContractCallContext.get().getInitBytecode().get()) + .build(); + } return timestamp .map(t -> fileDataRepository.getFileAtTimestamp(fileId, t)) diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java index 9787db109e..3b4402deb1 100644 --- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java +++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java @@ -19,7 +19,10 @@ import static com.hedera.services.utils.EntityIdUtils.toEntityId; import static com.hedera.services.utils.EntityIdUtils.toFileId; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.hedera.hapi.node.base.FileID; @@ -67,6 +70,9 @@ class FileReadableKVStateTest { @Mock private EntityRepository entityRepository; + @Mock + private Bytes initBytecode; + @Spy private ContractCallContext contractCallContext; @@ -172,6 +178,20 @@ void readFromDataSourceFileNotFound() { assertThat(result).isNull(); } + @Test + void readFromDataSourceWhenThereIsContext() { + when(ContractCallContext.get().getFileID()).thenReturn(Optional.of(FILE_ID)); + when(ContractCallContext.get().getInitBytecode()).thenReturn(Optional.of(initBytecode)); + + File result = fileReadableKVState.readFromDataSource(FILE_ID); + + assertThat(result) + .isEqualTo( + File.newBuilder().fileId(FILE_ID).contents(initBytecode).build()); + verify(fileDataRepository, times(0)).findById(anyLong()); + verify(fileDataRepository, times(0)).getFileAtTimestamp(anyLong(), anyLong()); + } + @Test void sizeIsAlwaysZero() { assertThat(fileReadableKVState.size()).isZero(); From 398c23a1cbb90cc44db76ee521b1064be6530343 Mon Sep 17 00:00:00 2001 From: Bilyana Gospodinova Date: Wed, 11 Dec 2024 14:31:29 +0200 Subject: [PATCH 2/2] Combine the 2 context parameters into 1 Signed-off-by: Bilyana Gospodinova --- .../mirror/web3/common/ContractCallContext.java | 13 +++++-------- .../mirror/web3/state/FileReadableKVState.java | 11 +++-------- .../mirror/web3/state/FileReadableKVStateTest.java | 4 ++-- 3 files changed, 10 insertions(+), 18 deletions(-) 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 e46e454cf2..a81ef06b6d 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 @@ -16,9 +16,9 @@ package com.hedera.mirror.web3.common; -import com.hedera.hapi.node.base.FileID; import com.hedera.hapi.node.contract.ContractCreateTransactionBody; import com.hedera.hapi.node.file.FileCreateTransactionBody; +import com.hedera.hapi.node.state.file.File; import com.hedera.mirror.common.domain.contract.ContractAction; import com.hedera.mirror.common.domain.transaction.RecordFile; import com.hedera.mirror.web3.evm.contracts.execution.traceability.Opcode; @@ -27,7 +27,6 @@ import com.hedera.mirror.web3.evm.store.CachingStateFrame; import com.hedera.mirror.web3.evm.store.StackedStateFrames; import com.hedera.mirror.web3.state.FileReadableKVState; -import com.hedera.pbj.runtime.io.buffer.Bytes; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.List; @@ -88,14 +87,13 @@ public class ContractCallContext { * 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 fields "fileID" and "initBytecode" - to hold temporary these values + * This is the function of the field "file" to hold temporary the bytecode and the fileId * during contract deploy. */ @Setter - private Optional fileID = Optional.empty(); + private Optional file = Optional.empty(); - @Setter - private Optional initBytecode = Optional.empty(); + ; private ContractCallContext() {} @@ -114,8 +112,7 @@ public static T run(Function function) { public void reset() { recordFile = null; stack = stackBase; - fileID = Optional.empty(); - initBytecode = Optional.empty(); + file = Optional.empty(); } public int getStackHeight() { 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 04c2a67ed5..286a696b64 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 @@ -61,15 +61,10 @@ protected File readFromDataSource(@Nonnull FileID key) { final var fileEntityId = toEntityId(key); final var fileId = fileEntityId.getId(); - final var contextFileId = ContractCallContext.get().getFileID(); + final var contextFile = ContractCallContext.get().getFile(); // If we are in a contract create case, the fileID and the init bytecode are in the ContractCallContext. - if (contextFileId.isPresent() - && contextFileId.get().equals(key) - && ContractCallContext.get().getInitBytecode().isPresent()) { - return File.newBuilder() - .fileId(key) - .contents(ContractCallContext.get().getInitBytecode().get()) - .build(); + if (contextFile.isPresent() && contextFile.get().fileId().equals(key)) { + return contextFile.get(); } return timestamp diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java index 33afd647c5..855fd8d386 100644 --- a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java +++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/FileReadableKVStateTest.java @@ -54,6 +54,7 @@ class FileReadableKVStateTest { private static final long REALM = 1L; private static final long FILE_NUM = 123L; private static final FileID FILE_ID = toFileId(SHARD, REALM, FILE_NUM); + private static final File FILE = File.newBuilder().fileId(FILE_ID).build(); private static final long FILE_ID_LONG = toEntityId(FILE_ID).getId(); private static final long EXPIRATION_TIMESTAMP = 2_000_000_000L; private static final Optional TIMESTAMP = Optional.of(1234L); @@ -180,8 +181,7 @@ void readFromDataSourceFileNotFound() { @Test void readFromDataSourceWhenThereIsContext() { - when(ContractCallContext.get().getFileID()).thenReturn(Optional.of(FILE_ID)); - when(ContractCallContext.get().getInitBytecode()).thenReturn(Optional.of(initBytecode)); + when(ContractCallContext.get().getFile()).thenReturn(Optional.of(FILE)); File result = fileReadableKVState.readFromDataSource(FILE_ID);