Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add file id and init bytecode in ContractCallContext #9799

Open
wants to merge 4 commits into
base: 09791-init-mirror-node-state-and-subcomponents
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@

package com.hedera.mirror.web3.common;

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;
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.store.CachingStateFrame;
import com.hedera.mirror.web3.evm.store.StackedStateFrames;
import com.hedera.mirror.web3.state.FileReadableKVState;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.List;
Expand Down Expand Up @@ -73,6 +77,24 @@ public class ContractCallContext {
@Setter
private Optional<Long> 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 field "file" to hold temporary the bytecode and the fileId
* during contract deploy.
*/
@Setter
private Optional<File> file = Optional.empty();

;

private ContractCallContext() {}

public static ContractCallContext get() {
Expand All @@ -90,6 +112,7 @@ public static <T> T run(Function<ContractCallContext, T> function) {
public void reset() {
recordFile = null;
stack = stackBase;
file = Optional.empty();
}

public int getStackHeight() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,15 @@ 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 contextFile = ContractCallContext.get().getFile();
// If we are in a contract create case, the fileID and the init bytecode are in the ContractCallContext.
if (contextFile.isPresent() && contextFile.get().fileId().equals(key)) {
return contextFile.get();
}

return timestamp
.map(t -> fileDataRepository.getFileAtTimestamp(fileId, t))
.orElseGet(() -> fileDataRepository.getFileAtTimestamp(fileId, getCurrentTimestamp()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
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;
Expand Down Expand Up @@ -52,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<Long> TIMESTAMP = Optional.of(1234L);
Expand All @@ -68,6 +71,9 @@ class FileReadableKVStateTest {
@Mock
private EntityRepository entityRepository;

@Mock
private Bytes initBytecode;

@Spy
private ContractCallContext contractCallContext;

Expand Down Expand Up @@ -173,6 +179,19 @@ void readFromDataSourceFileNotFound() {
assertThat(result).isNull();
}

@Test
void readFromDataSourceWhenThereIsContext() {
when(ContractCallContext.get().getFile()).thenReturn(Optional.of(FILE));

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();
Expand Down