Skip to content
Draft
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 @@ -204,4 +204,11 @@ message CryptoCreateTransactionBody {
* Details of hooks to add immediately after creating this account.
*/
repeated com.hedera.hapi.node.hooks.HookCreationDetails hook_creation_details = 19;

/**
* The delegation contract address for the account.
* If this field is set, a call to the account's address within a smart contract will
* result in the code of the authorized contract being executed.
*/
bytes delegation_address = 20;
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,11 @@ message CryptoUpdateTransactionBody {
* The hooks to create for the account.
*/
repeated com.hedera.hapi.node.hooks.HookCreationDetails hook_creation_details = 20;

/**
* The delegated contract address for the account.
* If this field is set, a call to the account's address within a smart contract will
* result in the code of the authorized contract being executed.
*/
bytes delegation_address = 21;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.services;

import com.hedera.node.app.service.contract.ContractService;
import com.hedera.node.app.service.contract.impl.ContractServiceImpl;
import com.hedera.node.app.service.file.impl.FileServiceInjectionModule;
import com.hedera.node.app.service.schedule.ScheduleService;
import com.hedera.node.app.service.schedule.impl.ScheduleServiceImpl;
Expand All @@ -19,4 +21,7 @@
public interface ServicesInjectionModule {
@Binds
ScheduleService bindScheduleService(ScheduleServiceImpl impl);

@Binds
ContractService bindContractService(ContractServiceImpl impl);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.hedera.node.app.fees.ExchangeRateManager;
import com.hedera.node.app.fees.FeeManager;
import com.hedera.node.app.records.BlockRecordService;
import com.hedera.node.app.service.contract.ContractService;
import com.hedera.node.app.service.contract.ContractServiceApi;
import com.hedera.node.app.service.file.ReadableFileStore;
import com.hedera.node.app.service.file.impl.FileServiceImpl;
import com.hedera.node.app.service.schedule.ScheduleService;
Expand Down Expand Up @@ -75,13 +77,17 @@ static Supplier<FeeCharging> provideBaseFeeCharging(@NonNull final AppContext ap

@Provides
@Singleton
static Map<Class<?>, ServiceApiProvider<?>> provideApiProviders(@NonNull final ScheduleService scheduleService) {
static Map<Class<?>, ServiceApiProvider<?>> provideApiProviders(
@NonNull final ScheduleService scheduleService, @NonNull final ContractService contractService) {
requireNonNull(scheduleService);
requireNonNull(contractService);
return Map.of(
TokenServiceApi.class,
TOKEN_SERVICE_API_PROVIDER,
ScheduleServiceApi.class,
scheduleService.apiProvider());
scheduleService.apiProvider(),
ContractServiceApi.class,
contractService.apiProvider());
}

@Binds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.hedera.node.app.fees.ResourcePriceCalculatorImpl;
import com.hedera.node.app.info.NodeInfoImpl;
import com.hedera.node.app.records.impl.BlockRecordInfoImpl;
import com.hedera.node.app.service.contract.ContractServiceApi;
import com.hedera.node.app.service.contract.impl.ContractServiceImpl;
import com.hedera.node.app.service.entityid.EntityIdService;
import com.hedera.node.app.service.entityid.impl.EntityNumGeneratorImpl;
import com.hedera.node.app.service.entityid.impl.WritableEntityIdStoreImpl;
Expand Down Expand Up @@ -107,7 +109,8 @@ public StandaloneDispatchFactory(
@NonNull final TransactionDispatcher transactionDispatcher,
@NonNull final NetworkUtilizationManager networkUtilizationManager,
@NonNull final TransactionChecker transactionChecker,
@NonNull final ScheduleServiceImpl scheduleService) {
@NonNull final ScheduleServiceImpl scheduleService,
@NonNull final ContractServiceImpl contractService) {
this.feeManager = requireNonNull(feeManager);
this.appFeeCharging = requireNonNull(appFeeCharging);
this.authorizer = requireNonNull(authorizer);
Expand All @@ -127,7 +130,9 @@ public StandaloneDispatchFactory(
TokenServiceApi.class,
TOKEN_SERVICE_API_PROVIDER,
ScheduleServiceApi.class,
scheduleService.apiProvider());
scheduleService.apiProvider(),
ContractServiceApi.class,
contractService.apiProvider());
}

/**
Expand Down
2 changes: 1 addition & 1 deletion hedera-node/hedera-app/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
requires transitive com.hedera.node.app.service.addressbook.impl;
requires transitive com.hedera.node.app.service.consensus.impl;
requires transitive com.hedera.node.app.service.contract.impl;
requires transitive com.hedera.node.app.service.contract;
requires transitive com.hedera.node.app.service.entityid;
requires transitive com.hedera.node.app.service.file.impl;
requires transitive com.hedera.node.app.service.network.admin.impl;
Expand Down Expand Up @@ -44,7 +45,6 @@
requires com.hedera.node.app.hapi.fees;
requires com.hedera.node.app.service.addressbook;
requires com.hedera.node.app.service.consensus;
requires com.hedera.node.app.service.contract;
requires com.hedera.node.app.service.entityid.impl;
requires com.hedera.node.app.service.file;
requires com.hedera.node.app.service.network.admin;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.service.contract.impl;

import static java.util.Objects.requireNonNull;
import static org.hyperledger.besu.evm.worldstate.CodeDelegationHelper.CODE_DELEGATION_PREFIX;

import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.ContractID;
import com.hedera.node.app.service.contract.ContractService;
import com.hedera.node.app.service.contract.ContractServiceApi;
import com.hedera.node.app.service.contract.impl.state.WritableContractStateStore;
import com.hedera.node.app.service.entityid.WritableEntityCounters;
import com.hedera.node.app.spi.api.ServiceApiProvider;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.swirlds.config.api.Configuration;
import com.swirlds.state.spi.WritableStates;
import edu.umd.cs.findbugs.annotations.NonNull;
import javax.inject.Singleton;

@Singleton
public class ContractServiceApiProvider implements ServiceApiProvider<ContractServiceApi> {

@Override
public String serviceName() {
return ContractService.NAME;

Check warning on line 25 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java#L25

Added line #L25 was not covered by tests
}

@Override
public ContractServiceApi newInstance(
@NonNull final Configuration configuration,
@NonNull final WritableStates writableStates,
@NonNull final WritableEntityCounters entityCounters) {
requireNonNull(configuration);
requireNonNull(writableStates);
requireNonNull(entityCounters);
return new ContractServiceApiImpl(new WritableContractStateStore(writableStates, entityCounters));

Check warning on line 36 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java#L33-L36

Added lines #L33 - L36 were not covered by tests
}

/**
* Default implementation of the {@link ContractServiceApi} interface.
*/
public class ContractServiceApiImpl implements ContractServiceApi {
private final WritableContractStateStore contractStateStore;

public ContractServiceApiImpl(@NonNull final WritableContractStateStore contractStateStore) {
this.contractStateStore = requireNonNull(contractStateStore);
}

Check warning on line 47 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java#L45-L47

Added lines #L45 - L47 were not covered by tests

@Override
public void setAccountDelegationTarget(@NonNull final AccountID accountID, @NonNull final Bytes bytecode) {
requireNonNull(accountID);
requireNonNull(bytecode);
final var contractID = ContractID.newBuilder()
.shardNum(accountID.shardNum())
.realmNum(accountID.realmNum())
.contractNum(accountID.accountNumOrThrow())
.build();

Check warning on line 57 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java#L51-L57

Added lines #L51 - L57 were not covered by tests
if (bytecode.equals(Bytes.EMPTY)) {
// Remove the delegation if the bytecode is empty
contractStateStore.removeBytecode(contractID);

Check warning on line 60 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java#L60

Added line #L60 was not covered by tests
} else {
final var delegationIndicator = Bytes.merge(Bytes.wrap(CODE_DELEGATION_PREFIX.toArray()), bytecode);
contractStateStore.putBytecode(

Check warning on line 63 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java#L62-L63

Added lines #L62 - L63 were not covered by tests
contractID, new com.hedera.hapi.node.state.contract.Bytecode(delegationIndicator));
}
}

Check warning on line 66 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceApiProvider.java#L66

Added line #L66 was not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.service.contract.impl;

import com.hedera.node.app.service.contract.ContractServiceApi;
import com.hedera.node.app.service.contract.impl.annotations.CustomOps;
import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics;
import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategies;
Expand All @@ -12,6 +13,7 @@
import com.hedera.node.app.service.contract.impl.handlers.ContractHandlers;
import com.hedera.node.app.service.contract.impl.nativelibverification.NativeLibVerifier;
import com.hedera.node.app.service.entityid.EntityIdFactory;
import com.hedera.node.app.spi.api.ServiceApiProvider;
import com.hedera.node.app.spi.signatures.SignatureVerifier;
import dagger.BindsInstance;
import dagger.Component;
Expand Down Expand Up @@ -46,6 +48,7 @@ interface Factory {
* @param systemContractMethodRegistry registry of all system contract methods
* @param customOps any additional custom operations to use when constructing the EVM
* @param entityIdFactory a factory for creating entity IDs
* @param nativeLibVerifier the native library verifier
* @return the contract service component
*/
ContractServiceComponent create(
Expand All @@ -57,7 +60,8 @@ ContractServiceComponent create(
@BindsInstance SystemContractMethodRegistry systemContractMethodRegistry,
@BindsInstance @CustomOps Set<Operation> customOps,
@BindsInstance EntityIdFactory entityIdFactory,
@BindsInstance NativeLibVerifier nativeLibVerifier);
@BindsInstance NativeLibVerifier nativeLibVerifier,
@BindsInstance ServiceApiProvider<ContractServiceApi> contractServiceApiProvider);
}

/**
Expand All @@ -80,6 +84,11 @@ ContractServiceComponent create(
*/
SystemContractMethodRegistry systemContractMethodRegistry();

/**
* Provides the {@link ContractServiceApi} provider.
*/
ServiceApiProvider<ContractServiceApi> contractServiceApiProvider();

@Named("HasTranslators")
Provider<List<CallTranslator<HasCallAttempt>>> hasCallTranslators();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static java.util.Objects.requireNonNull;

import com.hedera.node.app.service.contract.ContractService;
import com.hedera.node.app.service.contract.ContractServiceApi;
import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics;
import com.hedera.node.app.service.contract.impl.exec.scope.DefaultVerificationStrategies;
import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategies;
Expand All @@ -15,6 +16,7 @@
import com.hedera.node.app.service.contract.impl.schemas.V0490ContractSchema;
import com.hedera.node.app.service.contract.impl.schemas.V065ContractSchema;
import com.hedera.node.app.spi.AppContext;
import com.hedera.node.app.spi.api.ServiceApiProvider;
import com.hedera.node.config.data.ContractsConfig;
import com.swirlds.metrics.api.Metrics;
import com.swirlds.state.lifecycle.SchemaRegistry;
Expand Down Expand Up @@ -67,6 +69,7 @@ public ContractServiceImpl(
final var systemContractMethodRegistry = new SystemContractMethodRegistry();
final var contractMetrics = new ContractMetrics(metrics, contractsConfigSupplier, systemContractMethodRegistry);
final var nativeLibVerifier = new NativeLibVerifier(contractsConfigSupplier);
final var contractServiceApiProvider = new ContractServiceApiProvider();

this.component = DaggerContractServiceComponent.factory()
.create(
Expand All @@ -80,7 +83,8 @@ public ContractServiceImpl(
systemContractMethodRegistry,
customOps,
appContext.idFactory(),
nativeLibVerifier);
nativeLibVerifier,
contractServiceApiProvider);
}

@Override
Expand Down Expand Up @@ -123,4 +127,9 @@ public NativeLibVerifier nativeLibVerifier() {
allCallTranslators.addAll(component.htsCallTranslators().get());
return allCallTranslators;
}

@Override
public ServiceApiProvider<ContractServiceApi> apiProvider() {
return component.contractServiceApiProvider();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.hedera.node.app.hapi.utils.ethereum.CodeDelegation;
import com.hedera.node.app.hapi.utils.ethereum.EthTxSigs;
import com.hedera.node.app.service.contract.impl.hevm.HederaEvmTransaction;
import com.hedera.node.app.service.contract.impl.state.ProxyWorldUpdater;
import java.math.BigInteger;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
Expand Down Expand Up @@ -111,6 +112,7 @@
if (codeDelegation.nonce() != 0) {
return;
}
((ProxyWorldUpdater) worldUpdater).setupTopLevelLazyCreate(authorizerAddress);

Check warning on line 115 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationProcessor.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationProcessor.java#L115

Added line #L115 was not covered by tests
authority = worldUpdater.createAccount(authorizerAddress);
} else {
authority = maybeAuthorityAccount.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
}

public void incrementAlreadyExistingDelegators() {
alreadyExistingDelegators += 1;
this.alreadyExistingDelegators += 1;
}

public Set<Address> accessedDelegatorAddresses() {
return accessedDelegatorAddresses;
}

public long alreadyExistingDelegators() {
return alreadyExistingDelegators;

Check warning on line 28 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationResult.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationResult.java#L28

Added line #L28 was not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,23 @@ public interface HederaGasCalculator extends GasCalculator {
*/
GasCharges transactionGasRequirements(
@NonNull final Bytes payload, final boolean isContractCreate, final long baselineCost);

/**
* Calculate gas requirements of the transaction.
* This method mirrors {{@link GasCalculator#transactionIntrinsicGasCost(Transaction, long)},
* but does not require a full Transaction object and uses
* {@link GasCalculator#transactionFloorCost(Bytes, long)} for `minimumGasUsed` calculation.
* This overloaded also takes into account the size of the authorization list for type 4 transactions.
*
* @param payload the payload of the transaction
* @param isContractCreate is this call a 'contract creation'
* @param baselineCost the gas used by access lists and code delegation authorizations
* @param authorizationListSize the number of entries in the authorization list for type 4 transactions
* @return The gas requirements of the transaction
*/
GasCharges transactionGasRequirements(
@NonNull final Bytes payload,
final boolean isContractCreate,
final long baselineCost,
final long authorizationListSize);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class HederaGasCalculatorImpl extends PragueGasCalculator implements Hede
private static final int LOG_CONTRACT_ID_SIZE = 24;
private static final int LOG_TOPIC_SIZE = 32;
private static final int LOG_BLOOM_SIZE = 256;
public static final long INTRINSIC_DELEGATION_GAS_COST = 25_000L;

/**
* Default constructor for injection.
Expand All @@ -41,8 +42,18 @@ public HederaGasCalculatorImpl() {
@Override
public GasCharges transactionGasRequirements(
@NonNull final Bytes payload, final boolean isContractCreate, final long baselineCost) {
return transactionGasRequirements(payload, isContractCreate, baselineCost, 0L);
}

@Override
public GasCharges transactionGasRequirements(
@NonNull final Bytes payload,
final boolean isContractCreate,
final long baselineCost,
final long authorizationListSize) {
int zeros = payloadZeroBytes(payload);
final long intrinsicGas = transactionIntrinsicGas(payload, zeros, isContractCreate, baselineCost);
final long intrinsicGas =
transactionIntrinsicGas(payload, zeros, isContractCreate, baselineCost, authorizationListSize);
// gasUsed described at https://eips.ethereum.org/EIPS/eip-7623
final long floorGas = transactionFloorCost(payload, zeros);
return new GasCharges(intrinsicGas, Math.max(intrinsicGas, floorGas), 0L);
Expand All @@ -60,9 +71,16 @@ protected int payloadZeroBytes(@NonNull final Bytes payload) {

// TODO We won't use the baseline cost for now, should revisit with the Pectra support epic
protected long transactionIntrinsicGas(
@NonNull final Bytes payload, final int zeros, final boolean isContractCreate, final long baselineCost) {
@NonNull final Bytes payload,
final int zeros,
final boolean isContractCreate,
final long baselineCost,
final long authorizationListSize) {
final int nonZeros = payload.size() - zeros;
long cost = TX_BASE_COST + TX_DATA_ZERO_COST * zeros + ISTANBUL_TX_DATA_NON_ZERO_COST * nonZeros;
long cost = TX_BASE_COST
+ TX_DATA_ZERO_COST * zeros
+ ISTANBUL_TX_DATA_NON_ZERO_COST * nonZeros
+ INTRINSIC_DELEGATION_GAS_COST * authorizationListSize;
return isContractCreate ? (cost + contractCreationCost(payload.size())) : cost;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,10 @@ public void pureChecks(@NonNull final PureChecksContext context) throws PreCheck
final byte[] callData = ethTxData.hasCallData() ? ethTxData.callData() : new byte[0];
final var isContractCreate = !ethTxData.hasToAddress();
// TODO: Revisit baselineGas with Pectra support epic
final var authorizationListSize =
ethTxData.authorizationList() == null ? 0L : ethTxData.authorizationList().length;
final var gasRequirements = gasCalculator.transactionGasRequirements(
org.apache.tuweni.bytes.Bytes.wrap(callData), isContractCreate, 0L);
org.apache.tuweni.bytes.Bytes.wrap(callData), isContractCreate, 0L, authorizationListSize);
validateTruePreCheck(ethTxData.gasLimit() >= gasRequirements.minimumGasUsed(), INSUFFICIENT_GAS);
} catch (@NonNull final Exception e) {
bumpExceptionMetrics(ETHEREUM_TRANSACTION, e);
Expand Down
Loading
Loading