Skip to content

Commit

Permalink
chore: cherry pick #10750 (#10778)
Browse files Browse the repository at this point in the history
Signed-off-by: lukelee-sl <luke.lee@swirldslabs.com>
  • Loading branch information
lukelee-sl authored Jan 4, 2024
1 parent 6daccae commit ce3ecc5
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Hedera Hashgraph, LLC
* Copyright (C) 2020-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -239,12 +239,8 @@ private ResponseCodeEnum validateSemantics(final TransactionBody transactionBody
if (op.getGas() > properties.maxGasPerSec()) {
return MAX_GAS_LIMIT_EXCEEDED;
}
// Do some sanity checking in advance to ensure that the target is a valid contract
// Missing entity num is a valid target for lazy create. Tokens are also valid targets
final var target = targetOf(op);
if (!target.equals(EntityNum.MISSING_NUM)
&& !entityAccess.isTokenAccount(target.toId().asEvmAddress())
&& op.getAmount() == 0) {
if (possiblySanityCheckOp(op, target)) {
try {
final var receiver = accountStore.loadContract(target.toId());
validateTrue(receiver != null && receiver.isSmartContract(), INVALID_CONTRACT_ID);
Expand All @@ -255,6 +251,23 @@ private ResponseCodeEnum validateSemantics(final TransactionBody transactionBody
return OK;
}

// Determine if the operation should be sanity checked.
private boolean possiblySanityCheckOp(final ContractCallTransactionBody op, final EntityNum target) {
// Tokens are valid targets
if (entityAccess.isTokenAccount(target.toId().asEvmAddress())) {
return false;
}

// Check for possible lazy create
if (((target.equals(EntityNum.MISSING_NUM)
&& isOfEvmAddressSize(op.getContractID().getEvmAddress()))
|| op.getAmount() > 0)) {
return false;
}

return true;
}

@Override
public void preFetch(final TxnAccessor accessor) {
final var op = accessor.getTxn().getContractCall();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Hedera Hashgraph, LLC
* Copyright (C) 2020-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -92,6 +92,8 @@ class ContractCallTransitionLogicTest {
ContractID.newBuilder().setContractNum(9_999L).build();
private final ByteString alias = ByteStringUtils.wrapUnsafely(
new byte[] {48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 50});
private final ContractID missing =
ContractID.newBuilder().setContractNum(0L).setEvmAddress(alias).build();
private long gas = 21_000;
private long sent = 21_000L;
private static final long maxGas = 666_666L;
Expand Down Expand Up @@ -157,6 +159,7 @@ class ContractCallTransitionLogicTest {
private final Account senderAccount = new Account(new Id(0, 0, 1002));
private final Account relayerAccount = new Account(new Id(0, 0, 1003));
private final Account contractAccount = new Account(new Id(0, 0, 1006));
private final Account missingContractAccount = new Account(new Id(0, 0, 0));
private final GasCalculator gasCalculator =
new GasCalculatorHederaV22(properties, usagePricesProvider, hbarCentExchange);
ContractCallTransitionLogic subject;
Expand Down Expand Up @@ -721,6 +724,25 @@ void acceptsOkSyntax() {
assertEquals(OK, subject.semanticCheck().apply(contractCallTxn));
}

@Test
void acceptsOkSyntaxIfTokenAddress() {
givenValidTxnCtx();
given(properties.maxGasPerSec()).willReturn(gas + 1);
given(entityAccess.isTokenAccount(any())).willReturn(true);
// expect:
assertEquals(OK, subject.semanticCheck().apply(contractCallTxn));
}

@Test
void acceptsOkSyntaxIfPossibleLazyCreate() {
givenMissingContractIDTxnCtx();
given(properties.maxGasPerSec()).willReturn(gas + 1);
given(aliasManager.lookupIdBy(alias)).willReturn(EntityNum.MISSING_NUM);

// expect:
assertEquals(OK, subject.semanticCheck().apply(contractCallTxn));
}

@Test
void failsIfNotSmartContractSyntax() {
givenValidTxnCtxWithNoAmount();
Expand Down Expand Up @@ -793,6 +815,15 @@ private void givenValidTxnCtx() {
contractCallTxn = op.build();
}

private void givenMissingContractIDTxnCtx() {
var op = TransactionBody.newBuilder()
.setContractCall(ContractCallTransactionBody.newBuilder()
.setGas(gas)
.setAmount(sent)
.setContractID(missing));
contractCallTxn = op.build();
}

private void givenValidTxnCtxWithNoAmount() {
var op = TransactionBody.newBuilder()
.setContractCall(ContractCallTransactionBody.newBuilder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Hedera Hashgraph, LLC
* Copyright (C) 2020-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -1575,9 +1575,15 @@ HapiSpec invalidContract() {
final var function = getABIFor(FUNCTION, "getIndirect", CREATE_TRIVIAL);

return defaultHapiSpec("InvalidContract")
.given(withOpContext((spec, ctxLog) -> spec.registry().saveContractId("invalid", asContract("1.1.1"))))
.when()
.then(contractCallWithFunctionAbi("invalid", function).hasKnownStatus(INVALID_CONTRACT_ID));
.given(withOpContext(
(spec, ctxLog) -> spec.registry().saveContractId("invalid", asContract("0.0.100000001"))))
.when(withOpContext((spec, opLog) -> allRunFor(
spec,
ifHapiTest(
contractCallWithFunctionAbi("invalid", function).hasKnownStatus(INVALID_CONTRACT_ID)),
ifNotHapiTest(
contractCallWithFunctionAbi("invalid", function).hasPrecheck(INVALID_CONTRACT_ID)))))
.then();
}

@HapiTest
Expand All @@ -1587,15 +1593,14 @@ HapiSpec invalidContractDoesNotExist() {

return defaultHapiSpec("invalidContractDoesNotExist")
.given()
.when(
withOpContext((spec, opLog) -> allRunFor(
spec,
ifHapiTest(contractCallWithFunctionAbi("0.0.100000001", function)
.hasKnownStatus(INVALID_CONTRACT_ID)
.via("contractDoesNotExist")))),
.when(withOpContext((spec, opLog) -> allRunFor(
spec,
ifHapiTest(contractCallWithFunctionAbi("0.0.100000001", function)
.hasKnownStatus(INVALID_CONTRACT_ID)
.via("contractDoesNotExist")),
ifNotHapiTest(contractCallWithFunctionAbi("0.0.100000001", function)
.hasPrecheck(INVALID_CONTRACT_ID)
.via("contractDoesNotExist")))
.via("contractDoesNotExist")))))
.then(ifNotHapiTest(getTxnRecord("contractDoesNotExist").hasAnswerOnlyPrecheck(RECORD_NOT_FOUND)));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Hedera Hashgraph, LLC
* Copyright (C) 2020-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down

0 comments on commit ce3ecc5

Please sign in to comment.