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

Fix createContractWithStakingFields() #8986

Merged
merged 6 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -17,6 +17,7 @@
package com.hedera.node.app.service.mono.txns.contract;

import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateFalse;
import static com.hedera.node.app.service.mono.config.HederaNumbers.NUM_RESERVED_SYSTEM_ENTITIES;
import static com.hedera.node.app.service.mono.ledger.accounts.ContractCustomizer.fromHapiCreation;
import static com.hedera.node.app.service.mono.ledger.accounts.HederaAccountCustomizer.hasStakedId;
import static com.hedera.node.app.service.mono.state.EntityCreator.EMPTY_MEMO;
Expand All @@ -28,6 +29,7 @@
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_NEGATIVE_GAS;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_NEGATIVE_VALUE;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_FILE_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_RENEWAL_PERIOD;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_STAKING_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.MAX_GAS_LIMIT_EXCEEDED;
Expand Down Expand Up @@ -350,6 +352,7 @@ Bytes prepareCodeWithConstructorArguments(final ContractCreateTransactionBody op
return Bytes.wrap(ByteStringUtils.unwrapUnsafelyIfPossible(op.getInitcode()));
} else {
final var bytecodeSrc = op.getFileID();
validateFalse(bytecodeSrc.getFileNum() <= NUM_RESERVED_SYSTEM_ENTITIES, INVALID_FILE_ID);
final byte[] bytecode;
try {
bytecode = hfs.cat(bytecodeSrc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
import static com.hedera.node.app.hapi.utils.ByteStringUtils.wrapUnsafely;
import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateFalse;
import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateTrue;
import static com.hedera.node.app.service.mono.config.HederaNumbers.NUM_RESERVED_SYSTEM_ENTITIES;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.ETHEREUM_NONCE;
import static com.hedera.node.app.service.mono.utils.EntityNum.MISSING_NUM;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ACCOUNT_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ETHEREUM_TRANSACTION;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_FILE_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.NEGATIVE_ALLOWANCE_AMOUNT;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.OK;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.WRONG_CHAIN_ID;
Expand Down Expand Up @@ -129,7 +131,11 @@ public void doStateTransition() {

@Override
public ResponseCodeEnum validateSemantics(final TxnAccessor accessor) {
if (accessor.getTxn().getEthereumTransaction().getMaxGasAllowance() < 0) {
final var ethTx = accessor.getTxn().getEthereumTransaction();
if (ethTx.hasCallData() && ethTx.getCallData().getFileNum() <= NUM_RESERVED_SYSTEM_ENTITIES) {
return INVALID_FILE_ID;
}
if (ethTx.getMaxGasAllowance() < 0) {
return NEGATIVE_ALLOWANCE_AMOUNT;
}
final var ethTxData = spanMapAccessor.getEthTxDataMeta(accessor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,26 @@ void rejectsEmptyBytecodeFile() {
assertEquals("CONTRACT_FILE_EMPTY", exception.getMessage());
}

@Test
void rejectsSystemFileInitcode() {
givenValidTxnCtx();
final var fileId = FileID.newBuilder().setFileNum(159).build();
given(accessor.getTxn())
.willReturn(contractCreateTxn.toBuilder()
.setContractCreateInstance(contractCreateTxn.getContractCreateInstance().toBuilder()
.setFileID(fileId))
.build());
given(txnCtx.activePayer()).willReturn(ourAccount());
given(txnCtx.accessor()).willReturn(accessor);
given(accountStore.loadAccount(senderAccount.getId())).willReturn(senderAccount);

// when:
Exception exception = assertThrows(InvalidTransactionException.class, () -> subject.doStateTransition());

// then:
assertEquals("INVALID_FILE_ID", exception.getMessage());
}

@Test
void rejectSerializationFailed() {
Key key = Key.getDefaultInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ETHEREUM_TRANSACTION;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_FILE_ID;
import static com.hedera.node.app.hapi.utils.ethereum.EthTxData.populateEthTxData;
import static com.hedera.node.app.service.contract.impl.exec.processors.ProcessorModule.NUM_SYSTEM_ACCOUNTS;
import static com.hedera.node.app.service.contract.impl.hevm.HydratedEthTxData.failureFrom;
import static com.hedera.node.app.service.contract.impl.hevm.HydratedEthTxData.successFrom;

Expand Down Expand Up @@ -60,7 +61,11 @@ public HydratedEthTxData tryToHydrate(
return failureFrom(INVALID_ETHEREUM_TRANSACTION);
}
if (requiresHydration(body, ethTxData)) {
final var maybeCallDataFile = fileStore.getFileLeaf(body.callDataOrThrow());
final var callDataFileId = body.callDataOrThrow();
if (callDataFileId.fileNum() <= NUM_SYSTEM_ACCOUNTS) {
return failureFrom(INVALID_FILE_ID);
}
final var maybeCallDataFile = fileStore.getFileLeaf(callDataFileId);
if (maybeCallDataFile == null) {
return failureFrom(INVALID_FILE_ID);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT;
import static com.hedera.hapi.node.base.ResponseCodeEnum.SERIALIZATION_FAILED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.WRONG_CHAIN_ID;
import static com.hedera.node.app.service.contract.impl.exec.processors.ProcessorModule.NUM_SYSTEM_ACCOUNTS;
import static com.hedera.node.app.service.contract.impl.hevm.HederaEvmTransaction.NOT_APPLICABLE;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asChainIdBytes;
import static com.hedera.node.app.service.contract.impl.utils.SynthTxnUtils.synthEthTxCreation;
Expand Down Expand Up @@ -241,6 +242,7 @@ private void assertValidCall(@NonNull final ContractCallTransactionBody body) {
}

private void assertValidCreation(@NonNull final ContractCreateTransactionBody body) {
validateTrue(body.fileIDOrElse(FileID.DEFAULT).fileNum() > NUM_SYSTEM_ACCOUNTS, INVALID_FILE_ID);
final var autoRenewPeriod = body.autoRenewPeriodOrElse(Duration.DEFAULT).seconds();
validateTrue(autoRenewPeriod >= 1, INVALID_RENEWAL_PERIOD);
attributeValidator.validateAutoRenewPeriod(autoRenewPeriod);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.SERIALIZATION_FAILED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.WRONG_CHAIN_ID;
import static com.hedera.node.app.hapi.utils.ethereum.EthTxData.WEIBARS_TO_TINYBARS;
import static com.hedera.node.app.service.contract.impl.exec.processors.ProcessorModule.NUM_SYSTEM_ACCOUNTS;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.AN_ED25519_KEY;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.AUTO_ASSOCIATING_CONTRACTS_CONFIG;
import static com.hedera.node.app.service.contract.impl.test.TestHelpers.AUTO_ASSOCIATING_LEDGER_CONFIG;
Expand Down Expand Up @@ -74,6 +75,7 @@
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.Key;
import com.hedera.hapi.node.base.KeyList;
import com.hedera.hapi.node.base.ResponseCodeEnum;
Expand Down Expand Up @@ -211,6 +213,13 @@ void fromHapiCallUsesCallParamsWhenSet() {
assertNull(transaction.hapiCreation());
}

@Test
void fromHapiCreationFailsOnSystemInitcode() {
assertCreateFailsWith(
INVALID_FILE_ID,
b -> b.fileID(FileID.newBuilder().fileNum(NUM_SYSTEM_ACCOUNTS).build()));
}

@Test
void fromHapiCreationFailsOnInvalidRenewalPeriod() {
assertCreateFailsWith(INVALID_RENEWAL_PERIOD, b -> b.autoRenewPeriod(Duration.DEFAULT));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verifyNoInteractions;

import com.hedera.hapi.node.base.FileID;
import com.hedera.hapi.node.contract.EthereumTransactionBody;
import com.hedera.hapi.node.state.file.File;
import com.hedera.node.app.service.contract.impl.exec.processors.ProcessorModule;
import com.hedera.node.app.service.contract.impl.infra.EthereumCallDataHydration;
import com.hedera.node.app.service.file.ReadableFileStore;
import com.hedera.pbj.runtime.io.buffer.Bytes;
Expand Down Expand Up @@ -77,6 +79,18 @@ void doesNoHydrationIfCallDataAlreadyAvailable() {
verifyNoInteractions(fileStore);
}

@Test
void failsWithInvalidFileIdOnSystemInitcodeId() {
final var ethTxn = EthereumTransactionBody.newBuilder()
.ethereumData(ETH_WITH_TO_ADDRESS)
.callData(FileID.newBuilder().fileNum(ProcessorModule.NUM_SYSTEM_ACCOUNTS))
.build();
final var result = subject.tryToHydrate(ethTxn, fileStore);
assertFalse(result.isAvailable());
assertEquals(INVALID_FILE_ID, result.status());
verifyNoInteractions(fileStore);
}

@Test
void failsWithInvalidFileIdOnMissingCallDataFile() {
final var ethTxn = EthereumTransactionBody.newBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import javax.inject.Inject;
Expand Down Expand Up @@ -100,7 +101,10 @@ public Map<AccountID, Long> applyStakingRewards(final FinalizeContext context) {
* @param writableStore The store to write to for updated values
*/
public void adjustStakedToMeForAccountStakees(@NonNull final WritableAccountStore writableStore) {
final var modifiedAccounts = writableStore.modifiedAccountsInState();
// If there is a FROM_ACCOUNT_ or _TO_ACCOUNT stake change scenario, the set of modified
// accounts returned by writableStore.modifiedAccountsInState(); so we only iterate through
mhess-swl marked this conversation as resolved.
Show resolved Hide resolved
// the original modified accounts here
final var modifiedAccounts = new ArrayList<>(writableStore.modifiedAccountsInState());

for (final var id : modifiedAccounts) {
final var originalAccount = writableStore.getOriginalValue(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@
import com.hedera.services.bdd.spec.transactions.token.HapiTokenWipe;
import com.hedera.services.bdd.spec.transactions.token.TokenMovement;
import com.hedera.services.bdd.spec.transactions.util.HapiUtilPrng;
import com.hederahashgraph.api.proto.java.ContractCreateTransactionBody;
import com.hederahashgraph.api.proto.java.CryptoTransferTransactionBody;
import com.hederahashgraph.api.proto.java.EthereumTransactionBody;
import com.hederahashgraph.api.proto.java.ResponseCodeEnum;
import com.hederahashgraph.api.proto.java.TokenType;
import com.hederahashgraph.api.proto.java.TopicID;
Expand Down Expand Up @@ -464,6 +466,30 @@ public static HapiContractCreate contractCreate(final String contractName, final
}
}

/**
* Constructs a {@link HapiContractCreate} by letting the client code explicitly customize the {@link ContractCreateTransactionBody}.
*
* @param contractName the name the contract should register in this {@link HapiSpec}
* @param spec the spec to use to customize the {@link ContractCreateTransactionBody}
* @return a {@link HapiContractCreate} that can be used to create a contract
*/
public static HapiContractCreate explicitContractCreate(
final String contractName, final BiConsumer<HapiSpec, ContractCreateTransactionBody.Builder> spec) {
return new HapiContractCreate(contractName, spec);
}

/**
* Constructs a {@link HapiEthereumContractCreate} by letting the client code explicitly customize the {@link EthereumTransactionBody}.
*
* @param contractName the name the contract should register in this {@link HapiSpec}
* @param spec the spec to use to customize the {@link EthereumTransactionBody}
* @return a {@link HapiEthereumContractCreate} that can be used to create a contract
*/
public static HapiEthereumContractCreate explicitEthereumTransaction(
final String contractName, final BiConsumer<HapiSpec, EthereumTransactionBody.Builder> spec) {
return new HapiEthereumContractCreate(contractName, spec);
}

public static HapiContractCreate createDefaultContract(final String name) {
return new HapiContractCreate(name);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,15 @@
import com.hederahashgraph.api.proto.java.TransactionBody;
import com.hederahashgraph.api.proto.java.TransactionResponse;
import com.swirlds.common.utility.CommonUtils;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.LongConsumer;
Expand All @@ -66,10 +71,20 @@ public HapiContractCreate(String contract, String abi, Object... args) {
super(contract, abi, args);
}

public HapiContractCreate(
@NonNull final String contract,
@NonNull final BiConsumer<HapiSpec, ContractCreateTransactionBody.Builder> spec) {
super(contract);
this.spec = spec;
}

private Optional<String> autoRenewAccount = Optional.empty();
private Optional<Integer> maxAutomaticTokenAssociations = Optional.empty();
private Optional<ByteString> inlineInitcode = Optional.empty();

@Nullable
private BiConsumer<HapiSpec, ContractCreateTransactionBody.Builder> spec;

public HapiContractCreate exposingNumTo(LongConsumer obs) {
newNumObserver = Optional.of(obs);
return this;
Expand Down Expand Up @@ -265,6 +280,15 @@ protected void updateStateOf(HapiSpec spec) throws Throwable {

@Override
protected Consumer<TransactionBody.Builder> opBodyDef(HapiSpec spec) throws Throwable {
if (this.spec != null) {
return b -> {
try {
b.setContractCreateInstance(explicitContractCreate(spec));
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e);
}
};
}
if (!omitAdminKey && !useDeprecatedAdminKey) {
generateAdminKey(spec);
}
Expand Down Expand Up @@ -318,6 +342,14 @@ protected Consumer<TransactionBody.Builder> opBodyDef(HapiSpec spec) throws Thro
return b -> b.setContractCreateInstance(opBody);
}

private ContractCreateTransactionBody explicitContractCreate(HapiSpec spec)
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
return spec.txns()
.<ContractCreateTransactionBody, ContractCreateTransactionBody.Builder>body(
ContractCreateTransactionBody.class,
b -> Objects.requireNonNull(this.spec).accept(spec, b));
}

@Override
protected long feeFor(HapiSpec spec, Transaction txn, int numPayerSigs) throws Throwable {
return spec.fees()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,14 @@
import com.hederahashgraph.api.proto.java.Transaction;
import com.hederahashgraph.api.proto.java.TransactionBody;
import com.hederahashgraph.api.proto.java.TransactionResponse;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.LongConsumer;
Expand All @@ -60,6 +65,9 @@ public class HapiEthereumContractCreate extends HapiBaseContractCreate<HapiEther
private Optional<Long> maxGasAllowance = Optional.of(ONE_HUNDRED_HBARS);
private String privateKeyRef = SECP_256K1_SOURCE_KEY;

@Nullable
private BiConsumer<HapiSpec, EthereumTransactionBody.Builder> spec;

public HapiEthereumContractCreate exposingNumTo(LongConsumer obs) {
newNumObserver = Optional.of(obs);
return this;
Expand All @@ -81,8 +89,10 @@ public HapiEthereumContractCreate(String contract) {
super.omitAdminKey = true;
}

public HapiEthereumContractCreate(String contract, String abi, Object... args) {
super(contract, abi, args);
public HapiEthereumContractCreate(
@NonNull final String contract, @NonNull final BiConsumer<HapiSpec, EthereumTransactionBody.Builder> spec) {
super(contract);
this.spec = spec;
this.payer = Optional.of(RELAYER);
super.omitAdminKey = true;
}
Expand Down Expand Up @@ -175,6 +185,15 @@ public HapiEthereumContractCreate gasLimit(long gasLimit) {

@Override
protected Consumer<TransactionBody.Builder> opBodyDef(HapiSpec spec) throws Throwable {
if (this.spec != null) {
return b -> {
try {
b.setEthereumTransaction(explicitEthereumTransaction(spec));
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e);
}
};
}
bytecodeFileFn.ifPresent(stringSupplier -> bytecodeFile = Optional.of(stringSupplier.get()));
if (bytecodeFile.isEmpty()) {
setBytecodeToDefaultContract(spec);
Expand Down Expand Up @@ -216,7 +235,6 @@ protected Consumer<TransactionBody.Builder> opBodyDef(HapiSpec spec) throws Thro
final var senderAddress = ByteString.copyFrom(extractedSignatures.address());
spec.registry().saveBytes(ETH_SENDER_ADDRESS, senderAddress);

System.out.println("Size = " + callData.length + " vs " + MAX_CALL_DATA_SIZE);
if (fileContents.toByteArray().length > MAX_CALL_DATA_SIZE) {
ethFileID = Optional.of(TxnUtils.asFileId(bytecodeFile.get(), spec));
signedEthTxData = signedEthTxData.replaceCallData(new byte[] {});
Expand Down Expand Up @@ -251,4 +269,12 @@ protected long feeFor(HapiSpec spec, Transaction txn, int numPayerSigs) throws T
protected Function<Transaction, TransactionResponse> callToUse(HapiSpec spec) {
return spec.clients().getScSvcStub(targetNodeFor(spec), useTls)::createContract;
}

private EthereumTransactionBody explicitEthereumTransaction(HapiSpec spec)
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
return spec.txns()
.<EthereumTransactionBody, EthereumTransactionBody.Builder>body(
EthereumTransactionBody.class,
b -> Objects.requireNonNull(this.spec).accept(spec, b));
}
}
Loading