-
Notifications
You must be signed in to change notification settings - Fork 138
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
base: develop
Are you sure you want to change the base?
Changes from 2 commits
ac810e5
83e1936
17dfde9
7e99e3c
3d87889
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given this test what does this test's name mean? (I know you didn't name it, but you could fix the name anyway, if I'm not just confused.) |
||
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -258,8 +258,9 @@ private InvolvedParties computeInvolvedParties( | |
} | ||
|
||
private boolean contractNotRequired(@Nullable final HederaEvmAccount to, @NonNull final Configuration config) { | ||
david-bakin-sl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
final var maybeGrandfatheredNumber = | ||
(to == null) ? null : to.isTokenFacade() ? null : to.hederaId().accountNumOrThrow(); | ||
final var maybeGrandfatheredNumber = (to == null || to.isTokenFacade() || to.isScheduleTxnFacade()) | ||
david-bakin-sl marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also I was curious why this change didn't have a corresponding test to modify in
|
||
? null | ||
: to.hederaId().accountNumOrThrow(); | ||
|
||
return featureFlags.isAllowCallsToNonContractAccountsEnabled( | ||
config.getConfigData(ContractsConfig.class), maybeGrandfatheredNumber); | ||
|
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So close to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't want all of the clutter if possible. Maybe I'll create an abstract class if it turns out the addition updaters are needed here. |
||
|
||
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()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be a config for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it is worth it to have a different gas cost for this for each system contract. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe should be renamed? |
||
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 Codecov / codecov/patchhedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/DispatchForResponseCodeHssCall.java#L128
|
||
} | ||
return completionWith(gasRequirement, recordBuilder, encodedRc(recordBuilder.status())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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( | ||
|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be moved up, perhaps under There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Michael, convinced me that originatorAddress is not really needed. I will remove it. |
||
super( | ||
input, | ||
senderAddress, | ||
|
@@ -82,6 +88,7 @@ public HssCallAttempt( | |
} else { | ||
this.redirectScheduleTxn = null; | ||
} | ||
this.originatorAddress = requireNonNull(originatorAddress); | ||
} | ||
|
||
@Override | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe worth explaining here how the originator (*) is different from the sender? Term "originator" doesn't appear in HIP-755 and of course there is no HIP for the schedule service itself. (*) Using me myself as a proxy for the typical reader: I don't know the difference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removing - see above comment |
||
*/ | ||
public @NonNull AccountID originatorAccount() { | ||
final var number = maybeMissingNumberOf(originatorAddress, this.enhancement.nativeOperations()); | ||
return AccountID.newBuilder().accountNum(number).build(); | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI,
assertThat
has a "fluid" API, you can write(though in fact that's redundant in this particular case, don't need the
isNotEmpty()
here)(in general should be
containsOnly
as even if this test had more then one element to be contained the order doesn't matter)(just spitting out AssertJ stuff for posterity, don't even think about it.)