Skip to content

Commit

Permalink
feat: Implement lazyCreationCostInGas method (#10337)
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 committed Dec 7, 2023
1 parent 7610d39 commit f070221
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,17 @@ private void runHtsCallAndExpect(
context.exchangeRateInfo().activeRate(Instant.now()),
context.resourcePricesFor(HederaFunctionality.CONTRACT_CALL, SubType.DEFAULT),
context.resourcePricesFor(HederaFunctionality.CONTRACT_CALL, SubType.DEFAULT));
final var systemContractGasCalculator = new SystemContractGasCalculator(
tinybarValues,
new CanonicalDispatchPrices(new AssetsLoader()),
(body, payerId) -> context.dispatchComputeFees(body, payerId).totalFee());
final var enhancement = new HederaWorldUpdater.Enhancement(
new HandleHederaOperations(
component.config().getConfigData(LedgerConfig.class),
component.config().getConfigData(ContractsConfig.class),
context,
tinybarValues,
systemContractGasCalculator,
component.config().getConfigData(HederaConfig.class)),
new HandleHederaNativeOperations(context),
new HandleSystemContractOperations(context));
Expand All @@ -290,10 +295,6 @@ private void runHtsCallAndExpect(
given(frame.getSenderAddress()).willReturn(sender);
final Deque<MessageFrame> stack = new ArrayDeque<>();
given(initialFrame.getContextVariable(CONFIG_CONTEXT_VARIABLE)).willReturn(component.config());
final var systemContractGasCalculator = new SystemContractGasCalculator(
tinybarValues,
new CanonicalDispatchPrices(new AssetsLoader()),
(body, payerId) -> context.dispatchComputeFees(body, payerId).totalFee());
given(initialFrame.getContextVariable(SYSTEM_CONTRACT_GAS_CALCULATOR_CONTEXT_VARIABLE))
.willReturn(systemContractGasCalculator);
stack.push(initialFrame);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@

import static com.hedera.hapi.node.base.ResponseCodeEnum.OK;
import static com.hedera.hapi.node.base.ResponseCodeEnum.SUCCESS;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.tuweniToPbjBytes;
import static com.hedera.node.app.service.contract.impl.utils.SynthTxnUtils.*;
import static com.hedera.node.app.service.mono.txns.crypto.AbstractAutoCreationLogic.LAZY_MEMO;
import static com.hedera.node.app.service.mono.txns.crypto.AbstractAutoCreationLogic.THREE_MONTHS_IN_SECONDS;
import static com.hedera.node.app.spi.key.KeyUtils.IMMUTABILITY_SENTINEL_KEY;
import static com.hedera.node.app.spi.workflows.record.ExternalizedRecordCustomizer.SUPPRESSING_EXTERNALIZED_RECORD_CUSTOMIZER;
import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.*;
import com.hedera.hapi.node.contract.ContractCreateTransactionBody;
import com.hedera.hapi.node.contract.ContractFunctionResult;
import com.hedera.hapi.node.token.CryptoCreateTransactionBody;
import com.hedera.hapi.node.token.CryptoUpdateTransactionBody;
import com.hedera.hapi.node.transaction.SignedTransaction;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.service.contract.impl.annotations.TransactionScope;
import com.hedera.node.app.service.contract.impl.exec.gas.DispatchType;
import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator;
import com.hedera.node.app.service.contract.impl.exec.gas.TinybarValues;
import com.hedera.node.app.service.contract.impl.records.ContractCreateRecordBuilder;
import com.hedera.node.app.service.contract.impl.state.ContractStateStore;
Expand All @@ -50,6 +57,7 @@
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import org.hyperledger.besu.datatypes.Address;

/**
* A fully mutable {@link HederaOperations} implementation based on a {@link HandleContext}.
Expand All @@ -59,10 +67,24 @@ public class HandleHederaOperations implements HederaOperations {
public static final Bytes ZERO_ENTROPY = Bytes.fromHex(
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");

private static final CryptoUpdateTransactionBody.Builder UPDATE_TXN_BODY_BUILDER =
CryptoUpdateTransactionBody.newBuilder()
.key(Key.newBuilder().ecdsaSecp256k1(Bytes.EMPTY).build());

private static final CryptoCreateTransactionBody.Builder CREATE_TXN_BODY_BUILDER =
CryptoCreateTransactionBody.newBuilder()
.initialBalance(0)
.maxAutomaticTokenAssociations(0)
.autoRenewPeriod(Duration.newBuilder().seconds(THREE_MONTHS_IN_SECONDS))
.key(IMMUTABILITY_SENTINEL_KEY)
.memo(LAZY_MEMO);

private final TinybarValues tinybarValues;
private final LedgerConfig ledgerConfig;
private final ContractsConfig contractsConfig;
private final HederaConfig hederaConfig;
private final SystemContractGasCalculator gasCalculator;

private final HandleContext context;

@Inject
Expand All @@ -71,12 +93,14 @@ public HandleHederaOperations(
@NonNull final ContractsConfig contractsConfig,
@NonNull final HandleContext context,
@NonNull final TinybarValues tinybarValues,
@NonNull final SystemContractGasCalculator gasCalculator,
@NonNull final HederaConfig hederaConfig) {
this.ledgerConfig = requireNonNull(ledgerConfig);
this.contractsConfig = requireNonNull(contractsConfig);
this.context = requireNonNull(context);
this.tinybarValues = requireNonNull(tinybarValues);
this.hederaConfig = requireNonNull(hederaConfig);
this.gasCalculator = requireNonNull(gasCalculator);
}

/**
Expand Down Expand Up @@ -154,9 +178,25 @@ public long contractCreationLimit() {
* {@inheritDoc}
*/
@Override
public long lazyCreationCostInGas() {
// TODO - implement correctly
return 1L;
public long lazyCreationCostInGas(@NonNull final Address recipient) {
final var payerId = context.payer();
// Calculate gas for a CryptoCreateTransactionBody with an alias address
final var createFee = gasCalculator.gasRequirement(
TransactionBody.newBuilder()
.cryptoCreateAccount(CREATE_TXN_BODY_BUILDER.alias(tuweniToPbjBytes(recipient)))
.build(),
DispatchType.CRYPTO_CREATE,
payerId);

// Calculate gas for an update TransactionBody
final var updateFee = gasCalculator.gasRequirement(
TransactionBody.newBuilder()
.cryptoUpdateAccount(UPDATE_TXN_BODY_BUILDER)
.build(),
DispatchType.CRYPTO_UPDATE,
payerId);

return createFee + updateFee;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.List;
import org.hyperledger.besu.datatypes.Address;

/**
* Provides the Hedera operations that only a {@link ProxyWorldUpdater} needs (but not a {@link DispatchingEvmFrameState}.
Expand Down Expand Up @@ -109,10 +110,11 @@ public interface HederaOperations {

/**
* Returns the lazy creation cost within this scope.
* @param recipient the recipient contract address
*
* @return the lazy creation cost in gas
*/
long lazyCreationCostInGas();
long lazyCreationCostInGas(@NonNull final Address recipient);

/**
* Returns the gas price in tinybars within this scope.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
import org.hyperledger.besu.datatypes.Address;

/**
* TODO - a read-only {@link HederaOperations} implementation based on a {@link QueryContext}.
Expand Down Expand Up @@ -128,7 +129,7 @@ public long contractCreationLimit() {
* @throws UnsupportedOperationException always
*/
@Override
public long lazyCreationCostInGas() {
public long lazyCreationCostInGas(@NonNull final Address recipient) {
throw new UnsupportedOperationException("Queries cannot get lazy creation cost");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public Optional<ExceptionalHaltReason> tryTransfer(
@Override
public Optional<ExceptionalHaltReason> tryLazyCreation(
@NonNull final Address recipient, @NonNull final MessageFrame frame) {
final var gasCost = enhancement.operations().lazyCreationCostInGas();
final var gasCost = enhancement.operations().lazyCreationCostInGas(recipient);
if (gasCost > frame.getRemainingGas()) {
return Optional.of(INSUFFICIENT_GAS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import com.hedera.hapi.node.token.TokenCreateTransactionBody;
import com.hedera.hapi.node.transaction.SignedTransaction;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.service.contract.impl.exec.gas.DispatchType;
import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator;
import com.hedera.node.app.service.contract.impl.exec.gas.TinybarValues;
import com.hedera.node.app.service.contract.impl.exec.scope.HandleHederaOperations;
import com.hedera.node.app.service.contract.impl.exec.scope.HederaOperations;
Expand Down Expand Up @@ -93,12 +95,20 @@ class HandleHederaOperationsTest {
@Mock
private FeeCalculator feeCalculator;

@Mock
private SystemContractGasCalculator gasCalculator;

private HandleHederaOperations subject;

@BeforeEach
void setUp() {
subject = new HandleHederaOperations(
DEFAULT_LEDGER_CONFIG, DEFAULT_CONTRACTS_CONFIG, context, tinybarValues, DEFAULT_HEDERA_CONFIG);
DEFAULT_LEDGER_CONFIG,
DEFAULT_CONTRACTS_CONFIG,
context,
tinybarValues,
gasCalculator,
DEFAULT_HEDERA_CONFIG);
}

@Test
Expand Down Expand Up @@ -194,8 +204,13 @@ void commitIsNoopUntilSavepointExposesIt() {
}

@Test
void lazyCreationCostInGasHardcoded() {
assertEquals(1L, subject.lazyCreationCostInGas());
void lazyCreationCostInGasTest() {
given(context.payer()).willReturn(A_NEW_ACCOUNT_ID);
given(gasCalculator.gasRequirement(any(), eq(DispatchType.CRYPTO_CREATE), eq(A_NEW_ACCOUNT_ID)))
.willReturn(6L);
given(gasCalculator.gasRequirement(any(), eq(DispatchType.CRYPTO_UPDATE), eq(A_NEW_ACCOUNT_ID)))
.willReturn(5L);
assertEquals(11L, subject.lazyCreationCostInGas(NON_SYSTEM_LONG_ZERO_ADDRESS));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ void delegatesTransfer() {
@Test
void abortsLazyCreationIfRemainingGasInsufficient() {
final var pretendCost = 1_234L;
given(hederaOperations.lazyCreationCostInGas()).willReturn(pretendCost);
given(hederaOperations.lazyCreationCostInGas(SOME_EVM_ADDRESS)).willReturn(pretendCost);
given(frame.getRemainingGas()).willReturn(pretendCost - 1);
final var maybeHaltReason = subject.tryLazyCreation(SOME_EVM_ADDRESS, frame);
assertTrue(maybeHaltReason.isPresent());
Expand All @@ -447,7 +447,7 @@ void abortsLazyCreationIfRemainingGasInsufficient() {
@Test
void delegatesLazyCreationAndDecrementsGasCostOnSuccess() {
final var pretendCost = 1_234L;
given(hederaOperations.lazyCreationCostInGas()).willReturn(pretendCost);
given(hederaOperations.lazyCreationCostInGas(SOME_EVM_ADDRESS)).willReturn(pretendCost);
given(frame.getRemainingGas()).willReturn(pretendCost * 2);
given(evmFrameState.tryLazyCreation(SOME_EVM_ADDRESS)).willReturn(Optional.empty());
final var maybeHaltReason = subject.tryLazyCreation(SOME_EVM_ADDRESS, frame);
Expand All @@ -459,7 +459,7 @@ void delegatesLazyCreationAndDecrementsGasCostOnSuccess() {
void doesntBothDecrementingGasOnLazyCreationFailureSinceAboutToHalt() {
final var pretendCost = 1_234L;
final var haltReason = Optional.<ExceptionalHaltReason>of(FAILURE_DURING_LAZY_ACCOUNT_CREATION);
given(hederaOperations.lazyCreationCostInGas()).willReturn(pretendCost);
given(hederaOperations.lazyCreationCostInGas(SOME_EVM_ADDRESS)).willReturn(pretendCost);
given(frame.getRemainingGas()).willReturn(pretendCost * 2);
given(evmFrameState.tryLazyCreation(SOME_EVM_ADDRESS)).willReturn(haltReason);
final var maybeHaltReason = subject.tryLazyCreation(SOME_EVM_ADDRESS, frame);
Expand Down

0 comments on commit f070221

Please sign in to comment.