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 hss sign schedule function #16789

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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 @@ -23,7 +23,6 @@
import static com.hedera.node.app.workflows.prehandle.PreHandleResult.Status.PRE_HANDLE_FAILURE;
import static com.hedera.node.app.workflows.prehandle.PreHandleResult.Status.SO_FAR_SO_GOOD;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.emptySortedSet;
import static java.util.Collections.unmodifiableSortedSet;
import static java.util.Objects.requireNonNull;
Expand Down Expand Up @@ -169,7 +168,7 @@ public Dispatch createChildDispatch(
requireNonNull(options);

final var preHandleResult = preHandleChild(options.body(), options.payerId(), config, readableStoreFactory);
final var childVerifier = getKeyVerifier(options.effectiveKeyVerifier(), config, emptySet());
final var childVerifier = getKeyVerifier(options.effectiveKeyVerifier(), config, options.authorizingKeys());
final var childTxnInfo = getTxnInfoFrom(options.payerId(), options.body());
final var streamMode = config.getConfigData(BlockStreamConfig.class).streamMode();
final var childStack = SavepointStackImpl.newChildStack(
Expand Down Expand Up @@ -438,8 +437,7 @@ public SignatureVerification verificationFor(@NonNull final Key key) {
@Override
public SignatureVerification verificationFor(
@NonNull final Key key, @NonNull final VerificationAssistant callback) {
// We do not yet support signing scheduled transactions from within the EVM
throw new UnsupportedOperationException();
return verifier.verificationFor(key, callback);
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,28 @@ void keyVerifierWithNullCallbackAndAuthorizingKeysAsExpected() {
assertThat(derivedVerifier.authorizingSimpleKeys()).containsExactly(A_CONTRACT_ID_KEY);
}

@Test
void keyVerifierWithCallbackAndAuthorizingKeysAsExpected() {
final var derivedVerifier =
ChildDispatchFactory.getKeyVerifier(a -> true, DEFAULT_CONFIG, Set.of(A_CONTRACT_ID_KEY));
assertThat(derivedVerifier.verificationFor(Key.DEFAULT).passed()).isTrue();
assertThat(derivedVerifier.verificationFor(Key.DEFAULT, (k, v) -> true).passed())
.isTrue();
assertThatThrownBy(() -> derivedVerifier.verificationFor(Bytes.EMPTY))
.isInstanceOf(UnsupportedOperationException.class);
assertThat(derivedVerifier.numSignaturesVerified()).isZero();
assertThat(derivedVerifier.authorizingSimpleKeys()).containsExactly(A_CONTRACT_ID_KEY);
assertThat(derivedVerifier.authorizingSimpleKeys()).isNotEmpty();
}

@Test
void keyVerifierOnlySupportsKeyVerification() {
final var derivedVerifier = ChildDispatchFactory.getKeyVerifier(verifierCallback, DEFAULT_CONFIG, emptySet());
assertThatThrownBy(() -> derivedVerifier.verificationFor(Key.DEFAULT, assistant))
.isInstanceOf(UnsupportedOperationException.class);
assertThat(derivedVerifier.verificationFor(Key.DEFAULT, assistant).passed())
.isFalse();
assertThatThrownBy(() -> derivedVerifier.verificationFor(Bytes.EMPTY))
.isInstanceOf(UnsupportedOperationException.class);
assertThat(derivedVerifier.numSignaturesVerified()).isEqualTo(0L);
assertThat(derivedVerifier.numSignaturesVerified()).isZero();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,14 @@ public record ContractsConfig(
boolean precompileHrcFacadeAssociateEnabled,
@ConfigProperty(value = "systemContract.accountService.enabled", defaultValue = "true") @NetworkProperty
boolean systemContractAccountServiceEnabled,
@ConfigProperty(value = "systemContract.scheduleService.enabled", defaultValue = "false") @NetworkProperty
@ConfigProperty(value = "systemContract.scheduleService.enabled", defaultValue = "true") @NetworkProperty
boolean systemContractScheduleServiceEnabled,
@ConfigProperty(value = "systemContract.scheduleService.signSchedule.enabled", defaultValue = "true")
@NetworkProperty
boolean systemContractSignScheduleEnabled,
@ConfigProperty(value = "systemContract.scheduleService.authorizeSchedule.enabled", defaultValue = "false")
stoyanov-st marked this conversation as resolved.
Show resolved Hide resolved
@NetworkProperty
boolean systemContractAuthorizeScheduleEnabled,
@ConfigProperty(value = "systemContract.accountService.isAuthorizedRawEnabled", defaultValue = "true")
@NetworkProperty
boolean systemContractAccountServiceIsAuthorizedRawEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,9 @@ private InvolvedParties computeInvolvedParties(
}

private boolean contractNotRequired(@Nullable final HederaEvmAccount to, @NonNull final Configuration config) {
final var maybeGrandfatheredNumber =
(to == null) ? null : to.isTokenFacade() ? null : to.hederaId().accountNumOrThrow();
final var maybeGrandfatheredNumber = (to == null || to.isTokenFacade() || to.isScheduleTxnFacade())
? null
: to.hederaId().accountNumOrThrow();

return featureFlags.isAllowCallsToNonContractAccountsEnabled(
config.getConfigData(ContractsConfig.class), maybeGrandfatheredNumber);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,11 @@ public enum DispatchType {
/**
* Dispatch for Hedera token reject functionality with resource prices on a non-fungible token.
*/
TOKEN_REJECT_NFT(HederaFunctionality.TOKEN_REJECT, TOKEN_NON_FUNGIBLE_UNIQUE);
TOKEN_REJECT_NFT(HederaFunctionality.TOKEN_REJECT, TOKEN_NON_FUNGIBLE_UNIQUE),
/**
* Dispatch for Hedera schedule sign functionality with default resource prices.
*/
SCHEDULE_SIGN(HederaFunctionality.SCHEDULE_SIGN, DEFAULT);

private final HederaFunctionality functionality;
private final SubType subtype;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,10 @@
public ExchangeRate currentExchangeRate() {
return context.exchangeRateInfo().activeRate(context.consensusNow());
}

@Override
@Nullable
public Key maybeEthSenderKey() {
return maybeEthSenderKey;

Check warning on line 156 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleSystemContractOperations.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/scope/HandleSystemContractOperations.java#L156

Added line #L156 was not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,10 @@ public Transaction syntheticTransactionForNativeCall(
public ExchangeRate currentExchangeRate() {
return context.exchangeRateInfo().activeRate(instantSource.instant());
}

@Override
@Nullable
public Key maybeEthSenderKey() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.hedera.node.app.service.contract.impl.records.ContractCallStreamBuilder;
import com.hedera.node.app.spi.workflows.record.StreamBuilder;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.tuweni.bytes.Bytes;
Expand Down Expand Up @@ -154,4 +155,10 @@ Transaction syntheticTransactionForNativeCall(
*/
@NonNull
ExchangeRate currentExchangeRate();

/**
* Returns the ecdsa eth key for the sender of current transaction if one exists.
*/
@Nullable
Key maybeEthSenderKey();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

import com.hedera.hapi.node.base.ContractID;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractNativeSystemContract;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.HasCallFactory;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hss.HssCallFactory;
import com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils;
import com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.EntityType;
import edu.umd.cs.findbugs.annotations.NonNull;
Expand All @@ -45,7 +45,7 @@ public class HssSystemContract extends AbstractNativeSystemContract implements H
public static final ContractID HSS_CONTRACT_ID = asNumberedContractId(Address.fromHexString(HSS_EVM_ADDRESS));

@Inject
public HssSystemContract(@NonNull final GasCalculator gasCalculator, @NonNull final HasCallFactory callFactory) {
public HssSystemContract(@NonNull final GasCalculator gasCalculator, @NonNull final HssCallFactory callFactory) {
super(HSS_SYSTEM_CONTRACT_NAME, callFactory, HSS_CONTRACT_ID, gasCalculator);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (C) 2023-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.
* 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.systemcontracts.hss;

import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TRANSACTION_BODY;
import static com.hedera.hapi.node.base.ResponseCodeEnum.SUCCESS;
import static com.hedera.node.app.service.contract.impl.exec.failure.CustomExceptionalHaltReason.ERROR_DECODING_PRECOMPILE_INPUT;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.haltResult;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.Call.PricedResult.gasOnly;
import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.encodedRc;
import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.contractsConfigOf;

import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.Key;
import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.service.contract.impl.exec.gas.DispatchGasCalculator;
import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator;
import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCall;
import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater;
import com.hedera.node.app.service.contract.impl.records.ContractCallStreamBuilder;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Objects;
import java.util.Set;
import org.hyperledger.besu.evm.frame.MessageFrame;

/**
* An HSS call that simply dispatches a synthetic transaction body and returns a result that is
* an encoded {@link ResponseCodeEnum}.
*/
public class DispatchForResponseCodeHssCall extends AbstractCall {

private final AccountID senderId;

@Nullable
private final TransactionBody syntheticBody;

private final VerificationStrategy verificationStrategy;
private final DispatchGasCalculator dispatchGasCalculator;
private final Set<Key> authorizingKeys;

/**
* Convenience overload that slightly eases construction for the most common case.
*
* @param attempt the attempt to translate to a dispatching
* @param syntheticBody the synthetic body to dispatch
* @param dispatchGasCalculator the dispatch gas calculator to use
*/
public DispatchForResponseCodeHssCall(
@NonNull final HssCallAttempt attempt,
@Nullable final TransactionBody syntheticBody,
@NonNull final DispatchGasCalculator dispatchGasCalculator,
@NonNull final Set<Key> authorizingKeys) {
this(
attempt.enhancement(),
attempt.systemContractGasCalculator(),
attempt.addressIdConverter().convertSender(attempt.senderAddress()),
syntheticBody,
attempt.defaultVerificationStrategy(),
dispatchGasCalculator,
authorizingKeys);
}

/**
* More general constructor, for cases where perhaps a custom {@link VerificationStrategy} is needed.
*
* @param enhancement the enhancement to use
* @param senderId the id of the spender
* @param syntheticBody the synthetic body to dispatch
* @param verificationStrategy the verification strategy to use
* @param dispatchGasCalculator the dispatch gas calculator to use
*/
// too many parameters
@SuppressWarnings("java:S107")
public DispatchForResponseCodeHssCall(
@NonNull final HederaWorldUpdater.Enhancement enhancement,
@NonNull final SystemContractGasCalculator gasCalculator,
@NonNull final AccountID senderId,
@Nullable final TransactionBody syntheticBody,
@NonNull final VerificationStrategy verificationStrategy,
@NonNull final DispatchGasCalculator dispatchGasCalculator,
@NonNull final Set<Key> authorizingKeys) {
super(gasCalculator, enhancement, false);
this.senderId = Objects.requireNonNull(senderId);
this.syntheticBody = syntheticBody;
this.verificationStrategy = Objects.requireNonNull(verificationStrategy);
this.dispatchGasCalculator = Objects.requireNonNull(dispatchGasCalculator);
this.authorizingKeys = authorizingKeys;
}

@Override
public @NonNull PricedResult execute(@NonNull final MessageFrame frame) {
if (syntheticBody == null) {
return gasOnly(
haltResult(
ERROR_DECODING_PRECOMPILE_INPUT,
contractsConfigOf(frame).precompileHtsDefaultGasCost()),
INVALID_TRANSACTION_BODY,
false);
}
final var recordBuilder = systemContractOperations()
.dispatch(
syntheticBody,
verificationStrategy,
senderId,
ContractCallStreamBuilder.class,
authorizingKeys);
final var gasRequirement =
dispatchGasCalculator.gasRequirement(syntheticBody, gasCalculator, enhancement, senderId);
var status = recordBuilder.status();
if (status != SUCCESS) {
recordBuilder.status(status);

Check warning on line 128 in hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/DispatchForResponseCodeHssCall.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/systemcontracts/hss/DispatchForResponseCodeHssCall.java#L128

Added line #L128 was not covered by tests
}
return completionWith(gasRequirement, recordBuilder, encodedRc(recordBuilder.status()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hss;

import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.isLongZeroAddress;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.maybeMissingNumberOf;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.numberOfLongZero;
import static java.util.Objects.requireNonNull;

import com.esaulpaugh.headlong.abi.Function;
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.ScheduleID;
import com.hedera.hapi.node.state.schedule.Schedule;
import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator;
Expand Down Expand Up @@ -51,6 +53,9 @@ public class HssCallAttempt extends AbstractCallAttempt<HssCallAttempt> {
@Nullable
private final Schedule redirectScheduleTxn;

@NonNull
private final Address originatorAddress;

// too many parameters
@SuppressWarnings("java:S107")
public HssCallAttempt(
Expand All @@ -63,7 +68,8 @@ public HssCallAttempt(
@NonNull final VerificationStrategies verificationStrategies,
@NonNull final SystemContractGasCalculator gasCalculator,
@NonNull final List<CallTranslator<HssCallAttempt>> callTranslators,
final boolean isStaticCall) {
final boolean isStaticCall,
@NonNull final Address originatorAddress) {
super(
input,
senderAddress,
Expand All @@ -82,6 +88,7 @@ public HssCallAttempt(
} else {
this.redirectScheduleTxn = null;
}
this.originatorAddress = requireNonNull(originatorAddress);
}

@Override
Expand Down Expand Up @@ -149,4 +156,14 @@ public boolean isScheduleRedirect() {
}
return null;
}

/**
* Returns the AccountID of the originator of this call.
*
* @return the AccountID of the originator of this call
*/
public @NonNull AccountID originatorAccount() {
final var number = maybeMissingNumberOf(originatorAddress, this.enhancement.nativeOperations());
return AccountID.newBuilder().accountNum(number).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public HssCallFactory(
verificationStrategies,
systemContractGasCalculatorOf(frame),
callTranslators,
frame.isStatic());
frame.isStatic(),
frame.getOriginatorAddress());
}
}

This file was deleted.

Loading
Loading