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

feat: Implement lazyCreationCostInGas method #10337

Merged
merged 3 commits into from
Dec 7, 2023
Merged
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 @@ -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
Loading