Skip to content

Commit

Permalink
feat: implement system contract versioning foundation (#17611)
Browse files Browse the repository at this point in the history
Signed-off-by: Luke Lee <luke.lee@hashgraph.com>
  • Loading branch information
lukelee-sl authored Feb 3, 2025
1 parent d1894b0 commit 54596ec
Show file tree
Hide file tree
Showing 47 changed files with 647 additions and 218 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ public record ContractsConfig(
@ConfigProperty(value = "systemContract.setUnlimitedAutoAssociations.enabled", defaultValue = "true")
@NetworkProperty
boolean systemContractSetUnlimitedAutoAssociationsEnabled,
@ConfigProperty(value = "systemContract.hts.addresses", defaultValue = "359") // 359 = 0x167, 364 = 0x16C
Set<Long> callableHTSAddresses,
@ConfigProperty(value = "evm.ethTransaction.zeroHapiFees.enabled", defaultValue = "false") @NetworkProperty
boolean evmEthTransactionZeroHapiFeesEnabled,
@ConfigProperty(value = "evm.allowCallsToNonContractAccounts", defaultValue = "true") @NetworkProperty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.transfersValue;
import static com.hedera.node.app.service.contract.impl.hevm.HevmPropagatedCallFailure.MISSING_RECEIVER_SIGNATURE;
import static com.hedera.node.app.service.contract.impl.hevm.HevmPropagatedCallFailure.RESULT_CANNOT_BE_EXTERNALIZED;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asNumberedContractId;
import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INSUFFICIENT_GAS;
import static org.hyperledger.besu.evm.frame.MessageFrame.State.EXCEPTIONAL_HALT;

Expand Down Expand Up @@ -137,7 +138,7 @@ public void start(@NonNull final MessageFrame frame, @NonNull final OperationTra
return;
}
}
doExecuteSystemContract(systemContracts.get(codeAddress), frame, tracer);
doExecuteSystemContract(systemContracts.get(codeAddress), codeAddress, frame, tracer);
return;
}

Expand Down Expand Up @@ -252,9 +253,11 @@ private void doExecutePrecompile(
*/
private void doExecuteSystemContract(
@NonNull final HederaSystemContract systemContract,
@NonNull final Address systemContractAddress,
@NonNull final MessageFrame frame,
@NonNull final OperationTracer tracer) {
final var fullResult = systemContract.computeFully(frame.getInputData(), frame);
final var fullResult =
systemContract.computeFully(asNumberedContractId(systemContractAddress), frame.getInputData(), frame);
final var gasRequirement = fullResult.gasRequirement();
final PrecompileContractResult result;
if (frame.getRemainingGas() < gasRequirement) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2023-2025 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.node.app.service.contract.impl.exec.processors;

import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.CallTranslator;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.address_0x16c.balanceof.BalanceOfTranslator;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;
import edu.umd.cs.findbugs.annotations.NonNull;
import javax.inject.Named;
import javax.inject.Singleton;

/**
* Provides the {@link CallTranslator} implementations for the HTS system contract at address 0x16c.
*/
@Module
public interface Hts0x16cTranslatorsModule {

@Provides
@Singleton
@IntoSet
@Named("HtsTranslators")
static CallTranslator<HtsCallAttempt> provideBalanceOfTranslator(@NonNull final BalanceOfTranslator translator) {
return translator;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2024 Hedera Hashgraph, LLC
* Copyright (C) 2023-2025 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2024 Hedera Hashgraph, LLC
* Copyright (C) 2023-2025 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 All @@ -19,7 +19,8 @@
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.ExchangeRateSystemContract.EXCHANGE_RATE_SYSTEM_CONTRACT_ADDRESS;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract.HAS_EVM_ADDRESS;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HssSystemContract.HSS_EVM_ADDRESS;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_EVM_ADDRESS;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_167_EVM_ADDRESS;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_16C_EVM_ADDRESS;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.PrngSystemContract.PRNG_PRECOMPILE_ADDRESS;
import static java.util.Map.entry;
import static java.util.Objects.requireNonNull;
Expand All @@ -41,7 +42,13 @@
import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.evm.contractvalidation.PrefixCodeRule;

@Module(includes = {HtsTranslatorsModule.class, HasTranslatorsModule.class, HssTranslatorsModule.class})
@Module(
includes = {
HtsTranslatorsModule.class,
Hts0x16cTranslatorsModule.class,
HasTranslatorsModule.class,
HssTranslatorsModule.class
})
public interface ProcessorModule {
long INITIAL_CONTRACT_NONCE = 1L;
boolean REQUIRE_CODE_DEPOSIT_TO_SUCCEED = true;
Expand Down Expand Up @@ -70,7 +77,8 @@ static Map<Address, HederaSystemContract> provideHederaSystemContracts(
@NonNull final HasSystemContract hasSystemContract,
@NonNull final HssSystemContract hssSystemContract) {
return Map.ofEntries(
entry(Address.fromHexString(HTS_EVM_ADDRESS), requireNonNull(htsSystemContract)),
entry(Address.fromHexString(HTS_167_EVM_ADDRESS), requireNonNull(htsSystemContract)),
entry(Address.fromHexString(HTS_16C_EVM_ADDRESS), requireNonNull(htsSystemContract)),
entry(
Address.fromHexString(EXCHANGE_RATE_SYSTEM_CONTRACT_ADDRESS),
requireNonNull(exchangeRateSystemContract)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2024 Hedera Hashgraph, LLC
* Copyright (C) 2023-2025 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 @@ -34,11 +34,14 @@ protected AbstractFullContract(@NonNull final String name, @NonNull final GasCal

@Override
public long gasRequirement(final Bytes bytes) {
throw new UnsupportedOperationException(getName() + " requires a MessageFrame to compute gas");
throw new UnsupportedOperationException(
getName()
+ " requires a MessageFrame to compute gas. Gas requirement using the input bytes is not supported on Hedera.");
}

@Override
public @NonNull PrecompileContractResult computePrecompile(final Bytes input, @NonNull final MessageFrame frame) {
throw new UnsupportedOperationException(getName() + " only supports full results");
throw new UnsupportedOperationException(
getName() + " only supports full results. This operation is not supported on Hedera.");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2024 Hedera Hashgraph, LLC
* Copyright (C) 2022-2025 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 All @@ -19,18 +19,21 @@
import static com.hedera.node.app.hapi.utils.ValidationUtils.validateTrue;
import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.contractsConfigOf;
import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.proxyUpdaterFor;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asNumberedContractId;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TRANSACTION_BODY;
import static java.util.Objects.requireNonNull;

import com.esaulpaugh.headlong.abi.BigIntegerType;
import com.esaulpaugh.headlong.abi.TypeFactory;
import com.hedera.hapi.node.base.ContractID;
import com.hedera.node.app.service.contract.impl.utils.ConversionUtils;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.math.BigInteger;
import java.util.Optional;
import javax.inject.Inject;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
Expand All @@ -45,6 +48,9 @@ public class ExchangeRateSystemContract extends AbstractFullContract implements
public static final int TO_TINYCENTS_SELECTOR = 0x43a88229;

public static final String EXCHANGE_RATE_SYSTEM_CONTRACT_ADDRESS = "0x168";
public static final ContractID EXCHANGE_RATE_CONTRACT_ID =
asNumberedContractId(Address.fromHexString(EXCHANGE_RATE_SYSTEM_CONTRACT_ADDRESS));

private long gasRequirement;

@Inject
Expand All @@ -54,7 +60,8 @@ public ExchangeRateSystemContract(@NonNull final GasCalculator gasCalculator) {

@Override
@NonNull
public FullResult computeFully(@NonNull Bytes input, @NonNull MessageFrame messageFrame) {
public FullResult computeFully(
@NonNull ContractID contractID, @NonNull Bytes input, @NonNull MessageFrame messageFrame) {
requireNonNull(input);
requireNonNull(messageFrame);
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public HasSystemContract(
@NonNull final GasCalculator gasCalculator,
@NonNull final HasCallFactory callFactory,
@NonNull final ContractMetrics contractMetrics) {
super(HAS_SYSTEM_CONTRACT_NAME, callFactory, HAS_CONTRACT_ID, gasCalculator, contractMetrics);
super(HAS_SYSTEM_CONTRACT_NAME, callFactory, gasCalculator, contractMetrics);
}

@Override
Expand All @@ -56,7 +56,8 @@ protected FrameUtils.CallType callTypeOf(@NonNull MessageFrame frame) {
}

@Override
public FullResult computeFully(@NonNull final Bytes input, @NonNull final MessageFrame frame) {
public FullResult computeFully(
@NonNull ContractID contractID, @NonNull final Bytes input, @NonNull final MessageFrame frame) {
requireNonNull(input);
requireNonNull(frame);

Expand All @@ -65,6 +66,6 @@ public FullResult computeFully(@NonNull final Bytes input, @NonNull final Messag
return haltResult(NOT_SUPPORTED, frame.getRemainingGas());
}

return super.computeFully(input, frame);
return super.computeFully(contractID, input, frame);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2024 Hedera Hashgraph, LLC
* Copyright (C) 2023-2025 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 All @@ -16,6 +16,7 @@

package com.hedera.node.app.service.contract.impl.exec.systemcontracts;

import com.hedera.hapi.node.base.ContractID;
import edu.umd.cs.findbugs.annotations.NonNull;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.evm.frame.MessageFrame;
Expand All @@ -35,11 +36,13 @@ public interface HederaSystemContract extends PrecompiledContract {
/**
* Computes the result of this contract, and also returns the gas requirement.
*
* @param contractID the contractID of the called system contract
* @param input the input to the contract
* @param messageFrame the message frame
* @return the result of the computation, and its gas requirement
*/
default FullResult computeFully(@NonNull Bytes input, @NonNull MessageFrame messageFrame) {
default FullResult computeFully(
@NonNull ContractID contractID, @NonNull Bytes input, @NonNull MessageFrame messageFrame) {
return new FullResult(computePrecompile(input, messageFrame), gasRequirement(input), null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public HssSystemContract(
@NonNull final GasCalculator gasCalculator,
@NonNull final HssCallFactory callFactory,
@NonNull final ContractMetrics contractMetrics) {
super(HSS_SYSTEM_CONTRACT_NAME, callFactory, HSS_CONTRACT_ID, gasCalculator, contractMetrics);
super(HSS_SYSTEM_CONTRACT_NAME, callFactory, gasCalculator, contractMetrics);
}

@Override
Expand All @@ -59,7 +59,8 @@ protected FrameUtils.CallType callTypeOf(@NonNull MessageFrame frame) {
}

@Override
public FullResult computeFully(@NonNull final Bytes input, @NonNull final MessageFrame frame) {
public FullResult computeFully(
@NonNull ContractID contractID, @NonNull final Bytes input, @NonNull final MessageFrame frame) {
requireNonNull(input);
requireNonNull(frame);

Expand All @@ -68,6 +69,6 @@ public FullResult computeFully(@NonNull final Bytes input, @NonNull final Messag
return haltResult(NOT_SUPPORTED, frame.getRemainingGas());
}

return super.computeFully(input, frame);
return super.computeFully(contractID, input, frame);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package com.hedera.node.app.service.contract.impl.exec.systemcontracts;

import static com.hedera.node.app.service.contract.impl.exec.failure.CustomExceptionalHaltReason.NOT_SUPPORTED;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.haltResult;
import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.contractsConfigOf;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asNumberedContractId;

import com.hedera.hapi.node.base.ContractID;
Expand All @@ -27,26 +30,43 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

@Singleton
public class HtsSystemContract extends AbstractNativeSystemContract implements HederaSystemContract {
public static final String HTS_SYSTEM_CONTRACT_NAME = "HTS";
public static final String HTS_EVM_ADDRESS = "0x167";
public static final ContractID HTS_CONTRACT_ID = asNumberedContractId(Address.fromHexString(HTS_EVM_ADDRESS));
public static final String HTS_167_EVM_ADDRESS = "0x167";
public static final String HTS_16C_EVM_ADDRESS = "0x16C";
public static final ContractID HTS_167_CONTRACT_ID =
asNumberedContractId(Address.fromHexString(HTS_167_EVM_ADDRESS));
public static final ContractID HTS_16C_CONTRACT_ID =
asNumberedContractId(Address.fromHexString(HTS_16C_EVM_ADDRESS));

@Inject
public HtsSystemContract(
@NonNull final GasCalculator gasCalculator,
@NonNull final HtsCallFactory callFactory,
@NonNull final ContractMetrics contractMetrics) {
super(HTS_SYSTEM_CONTRACT_NAME, callFactory, HTS_CONTRACT_ID, gasCalculator, contractMetrics);
super(HTS_SYSTEM_CONTRACT_NAME, callFactory, gasCalculator, contractMetrics);
}

@Override
protected FrameUtils.CallType callTypeOf(@NonNull MessageFrame frame) {
return FrameUtils.callTypeOf(frame, EntityType.TOKEN);
}

@Override
public FullResult computeFully(
@NonNull ContractID contractID, @NonNull final Bytes input, @NonNull final MessageFrame frame) {

// Check if calls to the current contract address are enabled
if (!contractsConfigOf(frame).callableHTSAddresses().contains(contractID.contractNum())) {
return haltResult(NOT_SUPPORTED, frame.getRemainingGas());
}

return super.computeFully(contractID, input, frame);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2024 Hedera Hashgraph, LLC
* Copyright (C) 2022-2025 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 All @@ -20,7 +20,7 @@
import static com.hedera.node.app.hapi.utils.CommonPbjConverters.toPbj;
import static com.hedera.node.app.hapi.utils.ValidationUtils.validateTrue;
import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.systemContractGasCalculatorOf;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asEvmContractId;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asNumberedContractId;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.tuweniToPbjBytes;
import static com.hedera.node.app.service.contract.impl.utils.SystemContractUtils.successResultOfZeroValueTraceable;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.FAIL_INVALID;
Expand Down Expand Up @@ -63,6 +63,8 @@ public class PrngSystemContract extends AbstractFullContract implements HederaSy
// random256BitGenerator(uint256)
static final int PSEUDORANDOM_SEED_GENERATOR_SELECTOR = 0xd83bf9a1;
public static final String PRNG_PRECOMPILE_ADDRESS = "0x169";
public static final ContractID PRNG_CONTRACT_ID =
asNumberedContractId(Address.fromHexString(PRNG_PRECOMPILE_ADDRESS));
private long gasRequirement;

@Inject
Expand All @@ -71,16 +73,14 @@ public PrngSystemContract(@NonNull final GasCalculator gasCalculator) {
}

@Override
public FullResult computeFully(@NonNull final Bytes input, @NonNull final MessageFrame frame) {
public FullResult computeFully(
@NonNull ContractID contractID, @NonNull final Bytes input, @NonNull final MessageFrame frame) {
requireNonNull(input);
requireNonNull(frame);

// compute the gas requirement
gasRequirement = calculateGas(frame);

// get the contract ID
final ContractID contractID = asEvmContractId(Address.fromHexString(PRNG_PRECOMPILE_ADDRESS));

try {
validateTrue(input.size() >= 4, INVALID_TRANSACTION_BODY);
// compute the pseudorandom number
Expand Down
Loading

0 comments on commit 54596ec

Please sign in to comment.