From 5d821976c6511abf8b153f684c37fecef337bdbf Mon Sep 17 00:00:00 2001 From: Valentin Tronkov Date: Wed, 27 Nov 2024 17:40:20 +0200 Subject: [PATCH 1/7] refactor: delete the `spec.autoScheduledTxns` flag and all related code (#16816) Signed-off-by: Valentin Tronkov <99957253+vtronkov@users.noreply.github.com> --- .../hedera/services/bdd/spec/HapiSpec.java | 153 ------------------ .../services/bdd/spec/HapiSpecOperation.java | 25 --- .../services/bdd/spec/HapiSpecSetup.java | 10 -- .../services/bdd/spec/SpecOperation.java | 12 -- .../bdd/suites/crypto/CryptoUpdateSuite.java | 16 +- .../main/resources/spec-default.properties | 1 - 6 files changed, 5 insertions(+), 212 deletions(-) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java index cc4f0984cd03..d6d7b09df00f 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java @@ -33,33 +33,21 @@ import static com.hedera.services.bdd.spec.HapiSpec.SpecStatus.PENDING; import static com.hedera.services.bdd.spec.HapiSpec.SpecStatus.RUNNING; import static com.hedera.services.bdd.spec.HapiSpecSetup.setupFrom; -import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; import static com.hedera.services.bdd.spec.infrastructure.HapiClients.clientsFor; import static com.hedera.services.bdd.spec.keys.DefaultKeyGen.DEFAULT_KEY_GEN; -import static com.hedera.services.bdd.spec.queries.QueryVerbs.getScheduleInfo; -import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord; import static com.hedera.services.bdd.spec.transactions.TxnUtils.doIfNotInterrupted; import static com.hedera.services.bdd.spec.transactions.TxnUtils.resourceAsString; import static com.hedera.services.bdd.spec.transactions.TxnUtils.turnLoggingOff; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.scheduleCreate; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.scheduleSign; import static com.hedera.services.bdd.spec.utilops.SysFileOverrideOp.Target.FEES; import static com.hedera.services.bdd.spec.utilops.SysFileOverrideOp.Target.THROTTLES; import static com.hedera.services.bdd.spec.utilops.UtilStateChange.createEthereumAccountForSpec; import static com.hedera.services.bdd.spec.utilops.UtilStateChange.isEthereumAccountCreatedForSpec; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.blockingOrder; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.convertHapiCallsToEthereumCalls; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.noOp; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.overridingAllOf; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.remembering; import static com.hedera.services.bdd.suites.HapiSuite.DEFAULT_CONTRACT_SENDER; -import static com.hedera.services.bdd.suites.HapiSuite.DEFAULT_PAYER; import static com.hedera.services.bdd.suites.HapiSuite.ETH_SUFFIX; import static com.hedera.services.bdd.suites.HapiSuite.SECP_256K1_SOURCE_KEY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_SIGNATURE; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.NO_NEW_VALID_SIGNATURES; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.OK; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; import static java.util.concurrent.CompletableFuture.allOf; @@ -100,13 +88,11 @@ import com.hedera.services.bdd.spec.keys.KeyFactory; import com.hedera.services.bdd.spec.keys.KeyGenerator; import com.hedera.services.bdd.spec.props.MapPropertySource; -import com.hedera.services.bdd.spec.transactions.HapiTxnOp; import com.hedera.services.bdd.spec.transactions.TxnFactory; import com.hedera.services.bdd.spec.transactions.TxnUtils; import com.hedera.services.bdd.spec.utilops.SysFileOverrideOp; import com.hedera.services.bdd.spec.utilops.streams.assertions.AbstractEventualStreamAssertion; import com.hedera.services.bdd.spec.verification.traceability.SidecarWatcher; -import com.hederahashgraph.api.proto.java.Key; import com.hederahashgraph.api.proto.java.ResponseCodeEnum; import com.hederahashgraph.api.proto.java.Timestamp; import com.swirlds.state.spi.WritableKVState; @@ -126,7 +112,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.SplittableRandom; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @@ -136,7 +121,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; @@ -190,8 +174,6 @@ public class HapiSpec implements Runnable, Executable { public static final ThreadLocal TEST_LIFECYCLE = new ThreadLocal<>(); public static final ThreadLocal SPEC_NAME = new ThreadLocal<>(); - private static final AtomicLong NEXT_AUTO_SCHEDULE_NUM = new AtomicLong(1); - private static final SplittableRandom RANDOM = new SplittableRandom(); private static final String CI_PROPS_FLAG_FOR_NO_UNRECOVERABLE_NETWORK_FAILURES = "suppressNetworkFailures"; private static final ThreadPoolExecutor THREAD_POOL = new ThreadPoolExecutor(0, 10_000, 250, MILLISECONDS, new SynchronousQueue<>()); @@ -799,22 +781,13 @@ private void exec(@NonNull List ops) { ops.addFirst( new SysFileOverrideOp(FEES, () -> feeResource.isBlank() ? null : resourceAsString(feeResource))); } - final var autoScheduled = setup().txnTypesToSchedule(); - if (!autoScheduled.isEmpty()) { - log.info("Auto-scheduling {}", autoScheduled); - } @Nullable List streamAssertions = null; for (var op : ops) { - if (!autoScheduled.isEmpty() && op.shouldSkipWhenAutoScheduling(autoScheduled)) { - continue; - } if (op instanceof AbstractEventualStreamAssertion streamAssertion) { if (streamAssertions == null) { streamAssertions = new ArrayList<>(); } streamAssertions.add(streamAssertion); - } else if (op instanceof HapiTxnOp txn && autoScheduled.contains(txn.type())) { - op = autoScheduledSequenceFor(txn); } if (quietMode) { turnLoggingOff(op); @@ -881,132 +854,6 @@ private void exec(@NonNull List ops) { } } - /** - * Given a transaction, creates a sequence of operations that schedules the - * transaction and validates its execution and record. This sequence of - * operations looks like, - *
    - *
  1. A {@code ScheduleCreate} using the default payer, and including - * zero or more of the required signing keys for the transaction.
  2. - *
  3. Zero or more {@code ScheduleSign}'s targeting the created - * schedule, each including one or more of the required signing keys - * not used with the {@code ScheduleCreate}.
  4. - *
  5. A {@code ScheduleInfo} query that verifies the schedule has been - * executed (unless the expected pre-check or status of the transaction - * was {@code INVALID_SIGNATURE}, in which case the schedule shouldn't - * have been executed.)
  6. - *
  7. A {@code GetTransactionRecord} query that verifies the triggered - * transaction had the expected status (unless the expected pre-check - * or status of the transaction was {@code INVALID_SIGNATURE}).
  8. - *
- * - * @param txn the transaction to auto-schedule - * @return the sequence of operations that auto-schedules the transaction - */ - private SpecOperation autoScheduledSequenceFor(final HapiTxnOp txn) { - // For the signatures to have the expected semantics, we must - // incorporate any signature control overrides into this spec - final var sigControlOverrides = txn.setKeyControlOverrides(this); - if (!sigControlOverrides.isEmpty()) { - sigControlOverrides.forEach((key, control) -> keys().setControl(key, control)); - } - - final var scheduleCreatePayerKey = registry().getKey(DEFAULT_PAYER); - final var signingKeys = txn.signersToUseFor(this).stream() - // Skip empty keys, which will have no required signatures, but - // should be rejected at consensus in most cases - .filter(key -> !isEmpty(key)) - // The ScheduleCreate uses the default payer key, so we should - // ignore it for signing requirement purposes - .filter(key -> !scheduleCreatePayerKey.equals(key)) - // Without this we would commonly repeat the payer key and run - // into SCHEDULE_ALREADY_EXECUTED - .distinct() - .toList(); - final var numKeys = signingKeys.size(); - final var numSignTxns = RANDOM.nextInt(numKeys + 1); - final var indices = createAndSignIndicesGiven(numKeys, numSignTxns); - // One slot for the ScheduleCreate, one for each ScheduleSign, - // one for the GetScheduleInfo, and one for the GetTxnRecord - final var orderedOps = new SpecOperation[1 + numSignTxns + 2]; - final var num = NEXT_AUTO_SCHEDULE_NUM.getAndIncrement(); - final var schedule = "autoScheduled" + num; - final var creation = "autoScheduleCreation" + num; - orderedOps[0] = scheduleCreate(schedule, txn) - .alsoSigningWithExplicit(signingKeys.subList(indices.get(0), indices.get(1))) - .savingExpectedScheduledTxnId() - .via(creation); - for (int i = 1, n = indices.size(); i < n - 1; i++) { - orderedOps[i] = scheduleSign(schedule) - .alsoSigningWithExplicit(signingKeys.subList(indices.get(i), indices.get(i + 1))) - // It's likely that some of the top-level transaction's signing keys will already - // be present (e.g. the default payer's signature), so we accommodate that here - // by adding NO_NEW_VALID_SIGNATURES to the list of acceptable statuses - .hasKnownStatusFrom(SUCCESS, NO_NEW_VALID_SIGNATURES); - } - - final var expectedStatus = - (txn.getExpectedPrecheck() == OK) ? txn.getExpectedStatus() : txn.getExpectedPrecheck(); - final var scheduleStateAssertion = getScheduleInfo(schedule); - // If the original transaction was supposed to fail with INVALID_SIGNATURE, - // then the schedule should not have been executed - if (expectedStatus != INVALID_SIGNATURE) { - scheduleStateAssertion.isExecuted(); - } else { - scheduleStateAssertion.isNotExecuted(); - } - orderedOps[orderedOps.length - 2] = scheduleStateAssertion; - - if (expectedStatus != INVALID_SIGNATURE) { - final var recordAssertion = getTxnRecord(creation) - .scheduledBy(schedule) - .hasPriority(recordWith().status(expectedStatus)); - orderedOps[orderedOps.length - 1] = recordAssertion; - } else { - // If the original transaction was supposed to fail with INVALID_SIGNATURE, there - // will be no scheduled transaction record to retrieve, so just use noOp() - orderedOps[orderedOps.length - 1] = noOp(); - } - return blockingOrder(orderedOps); - } - - private boolean isEmpty(final Key key) { - if (key.hasKeyList()) { - return key.getKeyList().getKeysCount() == 0 - || key.getKeyList().getKeysList().stream().allMatch(this::isEmpty); - } else if (key.hasThresholdKey()) { - return key.getThresholdKey().getKeys().getKeysCount() == 0 - || key.getThresholdKey().getKeys().getKeysList().stream().allMatch(this::isEmpty); - } else { - return false; - } - } - - /** - * Given the number of keys that will be used to sign the transaction, and the number of - * {@code ScheduleSign} transactions that will be executed, create a list of indices that - * can be used to choose which keys to sign with in both the initial {@code ScheduleCreate}, - * and any {@code ScheduleSign} transactions. - * - * @param numKeys the number of keys that will be used to sign the transaction - * @param numSignTxns the number of {@code ScheduleSign} transactions that will be executed - * @return a list of indices that can be used to choose signing keys - */ - private static List createAndSignIndicesGiven(final int numKeys, final int numSignTxns) { - final List endIndices = new ArrayList<>(); - endIndices.add(numKeys); - int remainingSkipsAllowed = numKeys - numSignTxns; - for (int i = 0; i < numSignTxns; i++) { - final var skips = remainingSkipsAllowed > 0 ? RANDOM.nextInt(remainingSkipsAllowed) : 0; - remainingSkipsAllowed -= skips; - final var curIndex = endIndices.get(0); - final var nextEndIndex = curIndex - skips - 1; - endIndices.add(0, nextEndIndex); - } - endIndices.add(0, 0); - return endIndices; - } - private Optional checkStream(@Nullable final List streamAssertions) { if (streamAssertions == null) { return Optional.empty(); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java index 33cacc056876..9b5b6b7d510a 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java @@ -65,7 +65,6 @@ import java.util.Optional; import java.util.OptionalDouble; import java.util.Random; -import java.util.Set; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; @@ -121,7 +120,6 @@ public abstract class HapiSpecOperation implements SpecOperation { protected boolean useTls = false; protected HapiSpecSetup.TxnProtoStructure txnProtoStructure = HapiSpecSetup.TxnProtoStructure.ALTERNATE; - protected Set skipIfAutoScheduling = Collections.emptySet(); protected Optional expectedLedgerId = Optional.empty(); protected Optional hardcodedNumPayerKeys = Optional.empty(); protected Optional sigMapGen = Optional.empty(); @@ -210,29 +208,6 @@ protected void fixNodeFor(final HapiSpec spec) { } } - /** - * Gives the {@link HapiSpec} author the option to skip this operation if certain - * functions are being auto-scheduled. - * - * @param functions the functions being auto-scheduled - * @return this operation - */ - public HapiSpecOperation skippedIfAutoScheduling(final Set functions) { - skipIfAutoScheduling = functions; - return this; - } - - /** - * Indicates whether this operation should be skipped, given a set of functions being - * auto-scheduled. - * - * @param beingAutoScheduled the functions being auto-scheduled - * @return true if this operation should be skipped - */ - public boolean shouldSkipWhenAutoScheduling(final Set beingAutoScheduled) { - return !Collections.disjoint(skipIfAutoScheduling, beingAutoScheduled); - } - private AccountID randomNodeFrom(final HapiSpec spec) { final List nodes = spec.targetNetworkOrThrow().nodes(); return fromPbj(nodes.get(r.nextInt(nodes.size())).getAccountId()); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java index 37d9c5e95e9b..df497c8a93a3 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecSetup.java @@ -22,7 +22,6 @@ import static com.hedera.services.bdd.spec.keys.KeyFactory.KeyType; import static com.hedera.services.bdd.spec.keys.deterministic.Bip0032.mnemonicToEd25519Key; import static com.hedera.services.bdd.spec.transactions.TxnUtils.bytecodePath; -import static java.util.stream.Collectors.toSet; import com.hedera.node.app.hapi.utils.CommonPbjConverters; import com.hedera.node.app.hapi.utils.keys.Ed25519Utils; @@ -169,15 +168,6 @@ public HapiPropertySource ciPropertiesMap() { return ciPropertiesMap; } - public Set txnTypesToSchedule() { - final var commaDelimited = props.get("spec.autoScheduledTxns"); - return commaDelimited.isBlank() - ? Collections.emptySet() - : Arrays.stream(commaDelimited.split(",")) - .map(HederaFunctionality::valueOf) - .collect(toSet()); - } - public Duration defaultAutoRenewPeriod() { return props.getDurationFromSecs("default.autorenew.secs"); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/SpecOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/SpecOperation.java index a7578b8fbcfe..f19d99c9af75 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/SpecOperation.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/SpecOperation.java @@ -16,10 +16,8 @@ package com.hedera.services.bdd.spec; -import com.hederahashgraph.api.proto.java.HederaFunctionality; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Optional; -import java.util.Set; /** * Defines the interface for a single operation in a {@link HapiSpec}. @@ -33,14 +31,4 @@ public interface SpecOperation { * @return an optional containing any failure that was thrown */ Optional execFor(@NonNull HapiSpec spec); - - /** - * Returns whether the operation should be skipped when auto-scheduling is enabled. - * - * @param beingAutoScheduled the set of {@link HederaFunctionality} that are being auto-scheduled - * @return whether the operation should be skipped when auto-scheduling is enabled - */ - default boolean shouldSkipWhenAutoScheduling(@NonNull Set beingAutoScheduled) { - return false; - } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java index 8223ae0f12c6..a72063196b70 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java @@ -96,7 +96,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.UnaryOperator; import java.util.stream.IntStream; @@ -367,16 +366,11 @@ final Stream usdFeeAsExpectedCryptoUpdate() { getAccountInfo("autoAssocTarget") .hasMaxAutomaticAssociations(-1) .logged(), - validateChargedUsd(baseTxn, baseFeeWithExpiry, allowedPercentDiff) - .skippedIfAutoScheduling(Set.of(CryptoUpdate)), - validateChargedUsd(plusOneTxn, baseFee, allowedPercentDiff) - .skippedIfAutoScheduling(Set.of(CryptoUpdate)), - validateChargedUsd(plusTenTxn, baseFee, allowedPercentDiff) - .skippedIfAutoScheduling(Set.of(CryptoUpdate)), - validateChargedUsd(plusFiveKTxn, baseFee, allowedPercentDiff) - .skippedIfAutoScheduling(Set.of(CryptoUpdate)), - validateChargedUsd(validNegativeTxn, baseFee, allowedPercentDiff) - .skippedIfAutoScheduling(Set.of(CryptoUpdate))); + validateChargedUsd(baseTxn, baseFeeWithExpiry, allowedPercentDiff), + validateChargedUsd(plusOneTxn, baseFee, allowedPercentDiff), + validateChargedUsd(plusTenTxn, baseFee, allowedPercentDiff), + validateChargedUsd(plusFiveKTxn, baseFee, allowedPercentDiff), + validateChargedUsd(validNegativeTxn, baseFee, allowedPercentDiff)); } @HapiTest diff --git a/hedera-node/test-clients/src/main/resources/spec-default.properties b/hedera-node/test-clients/src/main/resources/spec-default.properties index acf8d37e07e5..ac0fbb55142e 100644 --- a/hedera-node/test-clients/src/main/resources/spec-default.properties +++ b/hedera-node/test-clients/src/main/resources/spec-default.properties @@ -96,7 +96,6 @@ nodes=localhost num.opFinisher.threads=8 softwareUpdate.admin.name=SOFTWARE_UPDATE_ADMIN softwareUpdate.admin.id=0.0.54 -spec.autoScheduledTxns= spec.streamlinedIngestChecks=INVALID_FILE_ID,ENTITY_NOT_ALLOWED_TO_DELETE,AUTHORIZATION_FAILED,INVALID_PRNG_RANGE,INVALID_STAKING_ID,NOT_SUPPORTED,TOKEN_ID_REPEATED_IN_TOKEN_LIST,ALIAS_ALREADY_ASSIGNED,INVALID_ALIAS_KEY,KEY_REQUIRED,BAD_ENCODING,AUTORENEW_DURATION_NOT_IN_RANGE,INVALID_ZERO_BYTE_IN_STRING,INVALID_ADMIN_KEY,ACCOUNT_DELETED,BUSY,INSUFFICIENT_PAYER_BALANCE,INSUFFICIENT_TX_FEE,INVALID_ACCOUNT_ID,INVALID_NODE_ACCOUNT,INVALID_SIGNATURE,INVALID_TRANSACTION,INVALID_TRANSACTION_BODY,INVALID_TRANSACTION_DURATION,INVALID_TRANSACTION_ID,INVALID_TRANSACTION_START,KEY_PREFIX_MISMATCH,MEMO_TOO_LONG,PAYER_ACCOUNT_NOT_FOUND,PLATFORM_NOT_ACTIVE,TRANSACTION_EXPIRED,TRANSACTION_HAS_UNKNOWN_FIELDS,TRANSACTION_ID_FIELD_NOT_ALLOWED,TRANSACTION_OVERSIZE,TRANSFER_ACCOUNT_SAME_AS_DELETE_ACCOUNT,EMPTY_ALLOWANCES,REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT,TOKEN_HAS_NO_FREEZE_KEY,TOKEN_HAS_NO_SUPPLY_KEY,INVALID_TOKEN_INITIAL_SUPPLY,INVALID_TOKEN_DECIMALS,INVALID_TOKEN_MAX_SUPPLY,ACCOUNT_REPEATED_IN_ACCOUNT_AMOUNTS,TRANSFERS_NOT_ZERO_SUM_FOR_TOKEN,INVALID_ACCOUNT_AMOUNTS,TOKEN_NAME_TOO_LONG,TOKEN_SYMBOL_TOO_LONG,INVALID_TOKEN_NFT_SERIAL_NUMBER,PERMANENT_REMOVAL_REQUIRES_SYSTEM_INITIATION,MISSING_TOKEN_SYMBOL,MISSING_TOKEN_NAME,INVALID_EXPIRATION_TIME,EMPTY_TOKEN_TRANSFER_ACCOUNT_AMOUNTS,INVALID_ALLOWANCE_OWNER_ID,FUNGIBLE_TOKEN_IN_NFT_ALLOWANCES,TOKEN_NOT_ASSOCIATED_TO_ACCOUNT,MAX_ALLOWANCES_EXCEEDED,INVALID_ALLOWANCE_SPENDER_ID,AMOUNT_EXCEEDS_TOKEN_MAX_SUPPLY,NFT_IN_FUNGIBLE_TOKEN_ALLOWANCES,NEGATIVE_ALLOWANCE_AMOUNT,DELEGATING_SPENDER_DOES_NOT_HAVE_APPROVE_FOR_ALL,DELEGATING_SPENDER_CANNOT_GRANT_APPROVE_FOR_ALL,INVALID_TOKEN_MINT_AMOUNT,INVALID_TOKEN_BURN_AMOUNT,INVALID_WIPING_AMOUNT,INVALID_NFT_ID,BATCH_SIZE_LIMIT_EXCEEDED,METADATA_TOO_LONG,INVALID_RENEWAL_PERIOD,INVALID_CUSTOM_FEE_SCHEDULE_KEY,MAX_GAS_LIMIT_EXCEEDED,CONTRACT_DELETED,INVALID_ETHEREUM_TRANSACTION,INSUFFICIENT_ACCOUNT_BALANCE,INVALID_CONTRACT_ID,INVALID_TOPIC_ID,UPDATE_NODE_ACCOUNT_NOT_ALLOWED,INVALID_NODE_ACCOUNT_ID,INVALID_NODE_ID status.deferredResolves.doAsync=true status.preResolve.pause.ms=0 From d43a749fbb322124aef4aff34357abaa99bf2c2c Mon Sep 17 00:00:00 2001 From: Valentin Tronkov Date: Wed, 27 Nov 2024 17:55:26 +0200 Subject: [PATCH 2/7] feat: withAndWithoutLongTermEnabled mod implementation (#16180) Signed-off-by: Zhivko Kelchev Signed-off-by: Valentin Tronkov <99957253+vtronkov@users.noreply.github.com> Signed-off-by: ibankov Signed-off-by: Michael Tinker Co-authored-by: Zhivko Kelchev Co-authored-by: ibankov Co-authored-by: Michael Tinker Co-authored-by: Joseph S. <121976561+jsync-swirlds@users.noreply.github.com> --- ...LeakyTestsWithLongTermFlagEnabledTest.java | 77 + .../AllTestsWithLongTermFlagEnabledTest.java | 61 + .../schedule/FutureSchedulableOpsTest.java | 112 +- .../suites/schedule/ScheduleCreateTest.java | 448 ++-- .../suites/schedule/ScheduleDeleteTest.java | 102 +- .../schedule/ScheduleExecutionTest.java | 2106 ++++++++--------- .../suites/schedule/ScheduleRecordTest.java | 228 +- .../bdd/suites/schedule/ScheduleSignTest.java | 604 +++-- .../StatefulScheduleExecutionTest.java | 10 +- .../bdd/suites/utils/DynamicTestUtils.java | 65 + .../src/main/java/module-info.java | 1 - 11 files changed, 1842 insertions(+), 1972 deletions(-) create mode 100644 hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllLeakyTestsWithLongTermFlagEnabledTest.java create mode 100644 hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllTestsWithLongTermFlagEnabledTest.java create mode 100644 hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/DynamicTestUtils.java diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllLeakyTestsWithLongTermFlagEnabledTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllLeakyTestsWithLongTermFlagEnabledTest.java new file mode 100644 index 000000000000..26b011427732 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllLeakyTestsWithLongTermFlagEnabledTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 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.services.bdd.suites.schedule; + +import static com.hedera.services.bdd.suites.utils.DynamicTestUtils.extractAllTestAnnotatedMethods; + +import com.hedera.services.bdd.junit.ContextRequirement; +import com.hedera.services.bdd.junit.HapiTestLifecycle; +import com.hedera.services.bdd.junit.LeakyHapiTest; +import com.hedera.services.bdd.junit.support.TestLifecycle; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DynamicTest; + +// Running all leaky test methods that are specified in ALL_TESTS constant with scheduling.longTermEnabled enabled +@HapiTestLifecycle +public class AllLeakyTestsWithLongTermFlagEnabledTest { + + private static final Supplier[] ALL_TESTS = new Supplier[] { + FutureSchedulableOpsTest::new, + ScheduleCreateTest::new, + ScheduleDeleteTest::new, + ScheduleExecutionTest::new, + ScheduleRecordTest::new, + ScheduleSignTest::new, + StatefulScheduleExecutionTest::new + }; + + private static final List IGNORED_TESTS = List.of( + // The test is setting ledger.schedule.txExpiryTimeSecs to 1 second in order + // to expire the transaction. Since this test class is turning on the longTermEnabled + // we never use the txExpiryTimeSecs, thus the test is failing which is expected. + "signFailsDueToDeletedExpiration"); + + @BeforeAll + static void beforeAll(@NonNull final TestLifecycle testLifecycle) { + testLifecycle.overrideInClass(Map.of( + "scheduling.whitelist", + "ContractCall,CryptoCreate,CryptoTransfer,FileDelete,FileUpdate," + + "SystemDelete,ConsensusSubmitMessage,TokenBurn,TokenMint,CryptoApproveAllowance", + "scheduling.longTermEnabled", + "true")); + } + + @LeakyHapiTest( + overrides = { + "tokens.nfts.areEnabled", + "tokens.nfts.maxBatchSizeMint", + "ledger.schedule.txExpiryTimeSecs", + "ledger.transfers.maxLen", + "ledger.tokenTransfers.maxLen", + "scheduling.whitelist" + }, + requirement = ContextRequirement.FEE_SCHEDULE_OVERRIDES) + final Stream runAllTests() { + var allDynamicTests = extractAllTestAnnotatedMethods(ALL_TESTS, IGNORED_TESTS, LeakyHapiTest.class); + return allDynamicTests.stream().flatMap(s -> s); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllTestsWithLongTermFlagEnabledTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllTestsWithLongTermFlagEnabledTest.java new file mode 100644 index 000000000000..e4bd6b28381d --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/AllTestsWithLongTermFlagEnabledTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 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.services.bdd.suites.schedule; + +import static com.hedera.services.bdd.suites.utils.DynamicTestUtils.extractAllTestAnnotatedMethods; + +import com.hedera.services.bdd.junit.HapiTest; +import com.hedera.services.bdd.junit.HapiTestLifecycle; +import com.hedera.services.bdd.junit.support.TestLifecycle; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DynamicTest; + +// Running all test methods that are specified in ALL_TESTS constant with scheduling.longTermEnabled enabled +@HapiTestLifecycle +public class AllTestsWithLongTermFlagEnabledTest { + + private static final Supplier[] ALL_TESTS = new Supplier[] { + FutureSchedulableOpsTest::new, + ScheduleCreateTest::new, + ScheduleDeleteTest::new, + ScheduleExecutionTest::new, + ScheduleRecordTest::new, + ScheduleSignTest::new, + StatefulScheduleExecutionTest::new + }; + + @BeforeAll + static void beforeAll(@NonNull final TestLifecycle testLifecycle) { + testLifecycle.overrideInClass(Map.of( + "scheduling.whitelist", + "ContractCall,CryptoCreate,CryptoTransfer,FileDelete,FileUpdate," + + "SystemDelete,ConsensusSubmitMessage,TokenBurn,TokenMint,CryptoApproveAllowance", + "scheduling.longTermEnabled", + "true")); + } + + @HapiTest + final Stream runAllTests() { + var allDynamicTests = extractAllTestAnnotatedMethods(ALL_TESTS, List.of(), HapiTest.class); + return allDynamicTests.stream().flatMap(s -> s); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/FutureSchedulableOpsTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/FutureSchedulableOpsTest.java index 31778f75b8e7..c4e1f502b49b 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/FutureSchedulableOpsTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/FutureSchedulableOpsTest.java @@ -17,7 +17,6 @@ package com.hedera.services.bdd.suites.schedule; import static com.hedera.services.bdd.junit.ContextRequirement.FEE_SCHEDULE_OVERRIDES; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.keys.ControlForKey.forKey; import static com.hedera.services.bdd.spec.keys.KeyShape.SIMPLE; @@ -106,54 +105,47 @@ static void beforeAll(@NonNull final TestLifecycle testLifecycle) { @LeakyHapiTest(requirement = FEE_SCHEDULE_OVERRIDES) final Stream canonicalScheduleOpsHaveExpectedUsdFees() { - return defaultHapiSpec("CanonicalScheduleOpsHaveExpectedUsdFees") - .given( - uploadScheduledContractPrices(GENESIS), - uploadInitCode(SIMPLE_UPDATE), - cryptoCreate(OTHER_PAYER), - cryptoCreate(PAYING_SENDER), - cryptoCreate(RECEIVER).receiverSigRequired(true), - contractCreate(SIMPLE_UPDATE).gas(300_000L)) - .when( - scheduleCreate( - "canonical", - cryptoTransfer(tinyBarsFromTo(PAYING_SENDER, RECEIVER, 1L)) - .memo("") - .fee(ONE_HBAR)) - .payingWith(OTHER_PAYER) - .via("canonicalCreation") - .alsoSigningWith(PAYING_SENDER) - .adminKey(OTHER_PAYER), - scheduleSign("canonical") - .via("canonicalSigning") - .payingWith(PAYING_SENDER) - .alsoSigningWith(RECEIVER), - scheduleCreate( - "tbd", - cryptoTransfer(tinyBarsFromTo(PAYING_SENDER, RECEIVER, 1L)) - .memo("") - .fee(ONE_HBAR)) - .payingWith(PAYING_SENDER) - .adminKey(PAYING_SENDER), - scheduleDelete("tbd").via("canonicalDeletion").payingWith(PAYING_SENDER), - scheduleCreate( - "contractCall", - contractCall( - SIMPLE_UPDATE, - "set", - BigInteger.valueOf(5), - BigInteger.valueOf(42)) - .gas(24_000) - .memo("") - .fee(ONE_HBAR)) - .payingWith(OTHER_PAYER) - .via("canonicalContractCall") - .adminKey(OTHER_PAYER)) - .then( - validateChargedUsdWithin("canonicalCreation", 0.01, 3.0), - validateChargedUsdWithin("canonicalSigning", 0.001, 3.0), - validateChargedUsdWithin("canonicalDeletion", 0.001, 3.0), - validateChargedUsdWithin("canonicalContractCall", 0.1, 3.0)); + return hapiTest( + uploadScheduledContractPrices(GENESIS), + uploadInitCode(SIMPLE_UPDATE), + cryptoCreate(OTHER_PAYER), + cryptoCreate(PAYING_SENDER), + cryptoCreate(RECEIVER).receiverSigRequired(true), + contractCreate(SIMPLE_UPDATE).gas(300_000L), + scheduleCreate( + "canonical", + cryptoTransfer(tinyBarsFromTo(PAYING_SENDER, RECEIVER, 1L)) + .memo("") + .fee(ONE_HBAR)) + .payingWith(OTHER_PAYER) + .via("canonicalCreation") + .alsoSigningWith(PAYING_SENDER) + .adminKey(OTHER_PAYER), + scheduleSign("canonical") + .via("canonicalSigning") + .payingWith(PAYING_SENDER) + .alsoSigningWith(RECEIVER), + scheduleCreate( + "tbd", + cryptoTransfer(tinyBarsFromTo(PAYING_SENDER, RECEIVER, 1L)) + .memo("") + .fee(ONE_HBAR)) + .payingWith(PAYING_SENDER) + .adminKey(PAYING_SENDER), + scheduleDelete("tbd").via("canonicalDeletion").payingWith(PAYING_SENDER), + scheduleCreate( + "contractCall", + contractCall(SIMPLE_UPDATE, "set", BigInteger.valueOf(5), BigInteger.valueOf(42)) + .gas(24_000) + .memo("") + .fee(ONE_HBAR)) + .payingWith(OTHER_PAYER) + .via("canonicalContractCall") + .adminKey(OTHER_PAYER), + validateChargedUsdWithin("canonicalCreation", 0.01, 3.0), + validateChargedUsdWithin("canonicalSigning", 0.001, 3.0), + validateChargedUsdWithin("canonicalDeletion", 0.001, 3.0), + validateChargedUsdWithin("canonicalContractCall", 0.1, 3.0)); } @HapiTest @@ -303,10 +295,11 @@ final Stream addingSignaturesToExecutedTxFails() { var txnBody = cryptoCreate(SOMEBODY); var creation = "basicCryptoCreate"; - return defaultHapiSpec("AddingSignaturesToExecutedTxFails") - .given(cryptoCreate("somesigner"), scheduleCreate(creation, txnBody)) - .when(getScheduleInfo(creation).isExecuted().logged()) - .then(scheduleSign(creation) + return hapiTest( + cryptoCreate("somesigner"), + scheduleCreate(creation, txnBody), + getScheduleInfo(creation).isExecuted().logged(), + scheduleSign(creation) .via("signing") .alsoSigningWith("somesigner") .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED)); @@ -314,11 +307,10 @@ final Stream addingSignaturesToExecutedTxFails() { @HapiTest final Stream sharedKeyWorksAsExpected() { - return defaultHapiSpec("RequiresSharedKeyToSignBothSchedulingAndScheduledTxns") - .given( - newKeyNamed(SHARED_KEY), - cryptoCreate("payerWithSharedKey").key(SHARED_KEY)) - .when(scheduleCreate( + return hapiTest( + newKeyNamed(SHARED_KEY), + cryptoCreate("payerWithSharedKey").key(SHARED_KEY), + scheduleCreate( "deferredCreation", cryptoCreate("yetToBe") .signedBy() @@ -327,7 +319,7 @@ final Stream sharedKeyWorksAsExpected() { .balance(123L) .fee(ONE_HBAR)) .payingWith("payerWithSharedKey") - .via("creation")) - .then(getTxnRecord("creation").scheduled()); + .via("creation"), + getTxnRecord("creation").scheduled()); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleCreateTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleCreateTest.java index 06daf64a39b3..62f68dda8721 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleCreateTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleCreateTest.java @@ -18,7 +18,6 @@ import static com.hedera.services.bdd.junit.TestTags.NOT_REPEATABLE; import static com.hedera.services.bdd.spec.HapiSpec.customHapiSpec; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.assertions.AccountInfoAsserts.accountWith; import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountBalance; @@ -93,37 +92,36 @@ import org.junit.jupiter.api.Tag; public class ScheduleCreateTest { + @HapiTest final Stream aliasNotAllowedAsPayer() { - return defaultHapiSpec("BodyAndPayerCreation") - .given( - newKeyNamed(ALIAS), - cryptoCreate(PAYER).balance(INITIAL_BALANCE * ONE_HBAR), - cryptoTransfer(tinyBarsFromToWithAlias(PAYER, ALIAS, 2 * ONE_HUNDRED_HBARS)), - withOpContext((spec, opLog) -> updateSpecFor(spec, ALIAS)), - getAliasedAccountInfo(ALIAS) - .has(accountWith().expectedBalanceWithChargedUsd((2 * ONE_HUNDRED_HBARS), 0, 0))) - .when( - scheduleCreate( - ONLY_BODY_AND_PAYER, - cryptoTransfer(tinyBarsFromTo(PAYER, GENESIS, 1)) - .memo("SURPRISE!!!")) - .recordingScheduledTxn() - // prevent multiple runs of this test causing duplicates - .withEntityMemo("" + new SecureRandom().nextLong()) - .designatingPayer(PAYER) - .payingWithAliased(PAYER) - .hasPrecheck(PAYER_ACCOUNT_NOT_FOUND), - scheduleCreate( - ONLY_BODY_AND_PAYER, - cryptoTransfer(tinyBarsFromTo(PAYER, GENESIS, 1)) - .memo("SURPRISE!!!")) - .recordingScheduledTxn() - // prevent multiple runs of this test causing duplicates - .withEntityMemo("" + new SecureRandom().nextLong()) - .designatingPayer(PAYER) - .payingWith(PAYER)) - .then(getScheduleInfo(ONLY_BODY_AND_PAYER) + return hapiTest( + newKeyNamed(ALIAS), + cryptoCreate(PAYER).balance(INITIAL_BALANCE * ONE_HBAR), + cryptoTransfer(tinyBarsFromToWithAlias(PAYER, ALIAS, 2 * ONE_HUNDRED_HBARS)), + withOpContext((spec, opLog) -> updateSpecFor(spec, ALIAS)), + getAliasedAccountInfo(ALIAS) + .has(accountWith().expectedBalanceWithChargedUsd((2 * ONE_HUNDRED_HBARS), 0, 0)), + scheduleCreate( + ONLY_BODY_AND_PAYER, + cryptoTransfer(tinyBarsFromTo(PAYER, GENESIS, 1)) + .memo("SURPRISE!!!")) + .recordingScheduledTxn() + // prevent multiple runs of this test causing duplicates + .withEntityMemo("" + new SecureRandom().nextLong()) + .designatingPayer(PAYER) + .payingWithAliased(PAYER) + .hasPrecheck(PAYER_ACCOUNT_NOT_FOUND), + scheduleCreate( + ONLY_BODY_AND_PAYER, + cryptoTransfer(tinyBarsFromTo(PAYER, GENESIS, 1)) + .memo("SURPRISE!!!")) + .recordingScheduledTxn() + // prevent multiple runs of this test causing duplicates + .withEntityMemo("" + new SecureRandom().nextLong()) + .designatingPayer(PAYER) + .payingWith(PAYER), + getScheduleInfo(ONLY_BODY_AND_PAYER) .hasScheduleId(ONLY_BODY_AND_PAYER) .hasPayerAccountID(PAYER) .hasRecordedScheduledTxn()); @@ -131,10 +129,7 @@ final Stream aliasNotAllowedAsPayer() { @HapiTest final Stream worksAsExpectedWithDefaultScheduleId() { - return defaultHapiSpec("WorksAsExpectedWithDefaultScheduleId") - .given() - .when() - .then(getScheduleInfo("0.0.0").hasCostAnswerPrecheck(INVALID_SCHEDULE_ID)); + return hapiTest(getScheduleInfo("0.0.0").hasCostAnswerPrecheck(INVALID_SCHEDULE_ID)); } @HapiTest @@ -169,12 +164,13 @@ final Stream validateSignersInInfo() { @HapiTest final Stream onlyBodyAndAdminCreation() { - return defaultHapiSpec("OnlyBodyAndAdminCreation") - .given(newKeyNamed(ADMIN), cryptoCreate(SENDER)) - .when(scheduleCreate(ONLY_BODY_AND_ADMIN_KEY, cryptoTransfer(tinyBarsFromTo(SENDER, GENESIS, 1))) + return hapiTest( + newKeyNamed(ADMIN), + cryptoCreate(SENDER), + scheduleCreate(ONLY_BODY_AND_ADMIN_KEY, cryptoTransfer(tinyBarsFromTo(SENDER, GENESIS, 1))) .adminKey(ADMIN) - .recordingScheduledTxn()) - .then(getScheduleInfo(ONLY_BODY_AND_ADMIN_KEY) + .recordingScheduledTxn(), + getScheduleInfo(ONLY_BODY_AND_ADMIN_KEY) .hasScheduleId(ONLY_BODY_AND_ADMIN_KEY) .hasAdminKey(ADMIN) .hasRecordedScheduledTxn()); @@ -182,12 +178,12 @@ final Stream onlyBodyAndAdminCreation() { @HapiTest final Stream onlyBodyAndMemoCreation() { - return defaultHapiSpec("OnlyBodyAndMemoCreation") - .given(cryptoCreate(SENDER)) - .when(scheduleCreate(ONLY_BODY_AND_MEMO, cryptoTransfer(tinyBarsFromTo(SENDER, GENESIS, 1))) + return hapiTest( + cryptoCreate(SENDER), + scheduleCreate(ONLY_BODY_AND_MEMO, cryptoTransfer(tinyBarsFromTo(SENDER, GENESIS, 1))) .recordingScheduledTxn() - .withEntityMemo("sample memo")) - .then(getScheduleInfo(ONLY_BODY_AND_MEMO) + .withEntityMemo("sample memo"), + getScheduleInfo(ONLY_BODY_AND_MEMO) .hasScheduleId(ONLY_BODY_AND_MEMO) .hasEntityMemo("sample memo") .hasRecordedScheduledTxn()); @@ -195,28 +191,25 @@ final Stream onlyBodyAndMemoCreation() { @HapiTest final Stream idVariantsTreatedAsExpected() { - return defaultHapiSpec("idVariantsTreatedAsExpected") - .given(cryptoCreate(PAYER)) - .when() - .then(submitModified(withSuccessivelyVariedBodyIds(), () -> scheduleCreate( - ONLY_BODY_AND_PAYER, cryptoTransfer(tinyBarsFromTo(PAYER, GENESIS, 1))) - .withEntityMemo("" + new SecureRandom().nextLong()) - .designatingPayer(PAYER))); + return hapiTest(cryptoCreate(PAYER), submitModified(withSuccessivelyVariedBodyIds(), () -> scheduleCreate( + ONLY_BODY_AND_PAYER, cryptoTransfer(tinyBarsFromTo(PAYER, GENESIS, 1))) + .withEntityMemo("" + new SecureRandom().nextLong()) + .designatingPayer(PAYER))); } @HapiTest final Stream bodyAndPayerCreation() { - return defaultHapiSpec("BodyAndPayerCreation") - .given(cryptoCreate(PAYER)) - .when(scheduleCreate( + return hapiTest( + cryptoCreate(PAYER), + scheduleCreate( ONLY_BODY_AND_PAYER, cryptoTransfer(tinyBarsFromTo(PAYER, GENESIS, 1)) .memo("SURPRISE!!!")) .recordingScheduledTxn() // prevent multiple runs of this test causing duplicates .withEntityMemo("" + new SecureRandom().nextLong()) - .designatingPayer(PAYER)) - .then(getScheduleInfo(ONLY_BODY_AND_PAYER) + .designatingPayer(PAYER), + getScheduleInfo(ONLY_BODY_AND_PAYER) .hasScheduleId(ONLY_BODY_AND_PAYER) .hasPayerAccountID(PAYER) .hasRecordedScheduledTxn()); @@ -227,18 +220,17 @@ final Stream bodyAndSignatoriesCreation() { var scheduleName = "onlyBodyAndSignatories"; var scheduledTxn = cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)); - return defaultHapiSpec("BodyAndSignatoriesCreation") - .given( - cryptoCreate("payingAccount"), - newKeyNamed("adminKey"), - cryptoCreate(SENDER), - cryptoCreate(RECEIVER).receiverSigRequired(true)) - .when(scheduleCreate(scheduleName, scheduledTxn) + return hapiTest( + cryptoCreate("payingAccount"), + newKeyNamed("adminKey"), + cryptoCreate(SENDER), + cryptoCreate(RECEIVER).receiverSigRequired(true), + scheduleCreate(scheduleName, scheduledTxn) .adminKey("adminKey") .recordingScheduledTxn() .designatingPayer("payingAccount") - .alsoSigningWith(RECEIVER)) - .then(getScheduleInfo(scheduleName) + .alsoSigningWith(RECEIVER), + getScheduleInfo(scheduleName) .hasScheduleId(scheduleName) .hasSignatories(RECEIVER) .hasRecordedScheduledTxn()); @@ -246,38 +238,30 @@ final Stream bodyAndSignatoriesCreation() { @HapiTest final Stream failsWithNonExistingPayerAccountId() { - return defaultHapiSpec("FailsWithNonExistingPayerAccountId") - .given() - .when(scheduleCreate("invalidPayer", cryptoCreate("secondary")) - .designatingPayer(DESIGNATING_PAYER) - .hasKnownStatus(ACCOUNT_ID_DOES_NOT_EXIST)) - .then(); + return hapiTest(scheduleCreate("invalidPayer", cryptoCreate("secondary")) + .designatingPayer(DESIGNATING_PAYER) + .hasKnownStatus(ACCOUNT_ID_DOES_NOT_EXIST)); } @HapiTest final Stream failsWithTooLongMemo() { - return defaultHapiSpec("FailsWithTooLongMemo") - .given() - .when(scheduleCreate("invalidMemo", cryptoCreate("secondary")) - .withEntityMemo(nAscii(101)) - .hasPrecheck(MEMO_TOO_LONG)) - .then(); + return hapiTest(scheduleCreate("invalidMemo", cryptoCreate("secondary")) + .withEntityMemo(nAscii(101)) + .hasPrecheck(MEMO_TOO_LONG)); } @HapiTest final Stream notIdenticalScheduleIfScheduledTxnChanges() { - return defaultHapiSpec("NotIdenticalScheduleIfScheduledTxnChanges") - .given( - cryptoCreate(SENDER).balance(1L), - cryptoCreate(FIRST_PAYER), - scheduleCreate( - ORIGINAL, - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .memo("A") - .fee(ONE_HBAR)) - .payingWith(FIRST_PAYER)) - .when() - .then(scheduleCreate( + return hapiTest( + cryptoCreate(SENDER).balance(1L), + cryptoCreate(FIRST_PAYER), + scheduleCreate( + ORIGINAL, + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .memo("A") + .fee(ONE_HBAR)) + .payingWith(FIRST_PAYER), + scheduleCreate( CONTINUE, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) .memo("B") @@ -287,16 +271,14 @@ final Stream notIdenticalScheduleIfScheduledTxnChanges() { @HapiTest final Stream notIdenticalScheduleIfMemoChanges() { - return defaultHapiSpec("NotIdenticalScheduleIfMemoChanges") - .given( - cryptoCreate(SENDER).balance(1L), - scheduleCreate( - ORIGINAL, - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .fee(ONE_HBAR)) - .withEntityMemo(ENTITY_MEMO)) - .when() - .then(scheduleCreate( + return hapiTest( + cryptoCreate(SENDER).balance(1L), + scheduleCreate( + ORIGINAL, + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .fee(ONE_HBAR)) + .withEntityMemo(ENTITY_MEMO), + scheduleCreate( CONTINUE, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) .fee(ONE_HBAR)) @@ -305,22 +287,20 @@ final Stream notIdenticalScheduleIfMemoChanges() { @HapiTest final Stream notIdenticalScheduleIfAdminKeyChanges() { - return defaultHapiSpec("notIdenticalScheduleIfAdminKeyChanges") - .given( - newKeyNamed("adminA"), - newKeyNamed("adminB"), - cryptoCreate(SENDER).balance(1L), - cryptoCreate(FIRST_PAYER), - scheduleCreate( - ORIGINAL, - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .fee(ONE_HBAR)) - .adminKey("adminA") - .withEntityMemo(ENTITY_MEMO) - .designatingPayer(FIRST_PAYER) - .payingWith(FIRST_PAYER)) - .when() - .then(scheduleCreate( + return hapiTest( + newKeyNamed("adminA"), + newKeyNamed("adminB"), + cryptoCreate(SENDER).balance(1L), + cryptoCreate(FIRST_PAYER), + scheduleCreate( + ORIGINAL, + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .fee(ONE_HBAR)) + .adminKey("adminA") + .withEntityMemo(ENTITY_MEMO) + .designatingPayer(FIRST_PAYER) + .payingWith(FIRST_PAYER), + scheduleCreate( CONTINUE, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) .fee(ONE_HBAR)) @@ -332,70 +312,60 @@ final Stream notIdenticalScheduleIfAdminKeyChanges() { @HapiTest final Stream recognizesIdenticalScheduleEvenWithDifferentDesignatedPayer() { - return defaultHapiSpec("recognizesIdenticalScheduleEvenWithDifferentDesignatedPayer") - .given( - cryptoCreate(SENDER).balance(1L), - cryptoCreate(FIRST_PAYER), - scheduleCreate( - ORIGINAL, - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .fee(ONE_HBAR)) - .designatingPayer(FIRST_PAYER) - .payingWith(FIRST_PAYER) - .savingExpectedScheduledTxnId()) - .when(cryptoCreate(SECOND_PAYER)) - .then( - scheduleCreate( - "duplicate", - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .fee(ONE_HBAR)) - .payingWith(SECOND_PAYER) - .designatingPayer(SECOND_PAYER) - .via(COPYCAT) - .hasKnownStatus(IDENTICAL_SCHEDULE_ALREADY_CREATED), - getTxnRecord(COPYCAT).logged(), - getReceipt(COPYCAT).hasSchedule(ORIGINAL).hasScheduledTxnId(ORIGINAL)); + return hapiTest( + cryptoCreate(SENDER).balance(1L), + cryptoCreate(FIRST_PAYER), + scheduleCreate( + ORIGINAL, + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .fee(ONE_HBAR)) + .designatingPayer(FIRST_PAYER) + .payingWith(FIRST_PAYER) + .savingExpectedScheduledTxnId(), + cryptoCreate(SECOND_PAYER), + scheduleCreate( + "duplicate", + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .fee(ONE_HBAR)) + .payingWith(SECOND_PAYER) + .designatingPayer(SECOND_PAYER) + .via(COPYCAT) + .hasKnownStatus(IDENTICAL_SCHEDULE_ALREADY_CREATED), + getTxnRecord(COPYCAT).logged(), + getReceipt(COPYCAT).hasSchedule(ORIGINAL).hasScheduledTxnId(ORIGINAL)); } @HapiTest final Stream rejectsSentinelKeyListAsAdminKey() { - return defaultHapiSpec("RejectsSentinelKeyListAsAdminKey") - .given() - .when() - .then(scheduleCreate(CREATION, cryptoTransfer(tinyBarsFromTo(GENESIS, FUNDING, 1))) - .usingSentinelKeyListForAdminKey() - .hasPrecheck(INVALID_ADMIN_KEY)); + return hapiTest(scheduleCreate(CREATION, cryptoTransfer(tinyBarsFromTo(GENESIS, FUNDING, 1))) + .usingSentinelKeyListForAdminKey() + .hasPrecheck(INVALID_ADMIN_KEY)); } @HapiTest final Stream rejectsMalformedScheduledTxnMemo() { - return defaultHapiSpec("RejectsMalformedScheduledTxnMemo") - .given( - cryptoCreate("ntb").memo(ZERO_BYTE_MEMO).hasPrecheck(INVALID_ZERO_BYTE_IN_STRING), - cryptoCreate(SENDER)) - .when() - .then( - scheduleCreate( - CREATION, - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .memo(nAscii(101))) - .hasPrecheck(MEMO_TOO_LONG), - scheduleCreate( - "creationPartDeux", - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .memo("Here's s\u0000 to chew on!")) - .hasPrecheck(INVALID_ZERO_BYTE_IN_STRING)); + return hapiTest( + cryptoCreate("ntb").memo(ZERO_BYTE_MEMO).hasPrecheck(INVALID_ZERO_BYTE_IN_STRING), + cryptoCreate(SENDER), + scheduleCreate( + CREATION, + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .memo(nAscii(101))) + .hasPrecheck(MEMO_TOO_LONG), + scheduleCreate( + "creationPartDeux", + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .memo("Here's s\u0000 to chew on!")) + .hasPrecheck(INVALID_ZERO_BYTE_IN_STRING)); } @HapiTest final Stream infoIncludesTxnIdFromCreationReceipt() { - return defaultHapiSpec("InfoIncludesTxnIdFromCreationReceipt") - .given( - cryptoCreate(SENDER), - scheduleCreate(CREATION, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) - .savingExpectedScheduledTxnId()) - .when() - .then(getScheduleInfo(CREATION) + return hapiTest( + cryptoCreate(SENDER), + scheduleCreate(CREATION, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) + .savingExpectedScheduledTxnId(), + getScheduleInfo(CREATION) .hasScheduleId(CREATION) .hasScheduledTxnIdSavedBy(CREATION) .logged()); @@ -407,75 +377,70 @@ final Stream detectsKeysChangedBetweenExpandSigsAndHandleTxn() { var keyGen = OverlappingKeyGenerator.withAtLeastOneOverlappingByte(2); String aKey = "a", bKey = "b"; - return defaultHapiSpec("DetectsKeysChangedBetweenExpandSigsAndHandleTxn") - .given(newKeyNamed(aKey).generator(keyGen), newKeyNamed(bKey).generator(keyGen)) - .when(cryptoCreate(SENDER), cryptoCreate(RECEIVER).key(aKey).receiverSigRequired(true)) - .then( - cryptoUpdate(RECEIVER).key(bKey).deferStatusResolution(), - scheduleCreate( - "outdatedXferSigs", - cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)) - .fee(ONE_HBAR)) - .alsoSigningWith(aKey) - /* In the rare, but possible, case that the overlapping byte shared by aKey - * and bKey is _also_ shared by the DEFAULT_PAYER, the bKey prefix in the sig - * map will probably not collide with aKey any more, and we will get - * SUCCESS instead of SOME_SIGNATURES_WERE_INVALID. - * - * So we need this to stabilize CI. But if just testing locally, you may - * only use .hasKnownStatus(SOME_SIGNATURES_WERE_INVALID) and it will pass - * >99.99% of the time. */ - .hasKnownStatusFrom(SOME_SIGNATURES_WERE_INVALID, SUCCESS)); + return hapiTest( + newKeyNamed(aKey).generator(keyGen), + newKeyNamed(bKey).generator(keyGen), + cryptoCreate(SENDER), + cryptoCreate(RECEIVER).key(aKey).receiverSigRequired(true), + cryptoUpdate(RECEIVER).key(bKey).deferStatusResolution(), + scheduleCreate( + "outdatedXferSigs", + cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)) + .fee(ONE_HBAR)) + .alsoSigningWith(aKey) + /* In the rare, but possible, case that the overlapping byte shared by aKey + * and bKey is _also_ shared by the DEFAULT_PAYER, the bKey prefix in the sig + * map will probably not collide with aKey any more, and we will get + * SUCCESS instead of SOME_SIGNATURES_WERE_INVALID. + * + * So we need this to stabilize CI. But if just testing locally, you may + * only use .hasKnownStatus(SOME_SIGNATURES_WERE_INVALID) and it will pass + * >99.99% of the time. */ + .hasKnownStatusFrom(SOME_SIGNATURES_WERE_INVALID, SUCCESS)); } @HapiTest final Stream onlySchedulesWithMissingReqSimpleSigs() { - return defaultHapiSpec("OnlySchedulesWithMissingReqSimpleSigs") - .given( - cryptoCreate(SENDER).balance(1L), - cryptoCreate(RECEIVER).balance(0L).receiverSigRequired(true)) - .when(scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1))) - .alsoSigningWith(SENDER)) - .then(getAccountBalance(SENDER).hasTinyBars(1L)); + return hapiTest( + cryptoCreate(SENDER).balance(1L), + cryptoCreate(RECEIVER).balance(0L).receiverSigRequired(true), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1))) + .alsoSigningWith(SENDER), + getAccountBalance(SENDER).hasTinyBars(1L)); } @HapiTest final Stream requiresExtantPayer() { - return defaultHapiSpec("RequiresExtantPayer") - .given() - .when() - .then(scheduleCreate( - NEVER_TO_BE, cryptoCreate("nope").key(GENESIS).receiverSigRequired(true)) + return hapiTest( + scheduleCreate(NEVER_TO_BE, cryptoCreate("nope").key(GENESIS).receiverSigRequired(true)) .designatingPayer(DESIGNATING_PAYER) .hasKnownStatus(ACCOUNT_ID_DOES_NOT_EXIST)); } @HapiTest final Stream doesntTriggerUntilPayerSigns() { - return defaultHapiSpec("DoesntTriggerUntilPayerSigns") - .given( - cryptoCreate(PAYER).balance(ONE_HBAR * 5), - cryptoCreate(SENDER).balance(1L), - cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L)) - .when(scheduleCreate( + return hapiTest( + cryptoCreate(PAYER).balance(ONE_HBAR * 5), + cryptoCreate(SENDER).balance(1L), + cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L), + scheduleCreate( BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1L)) .fee(ONE_HBAR)) .designatingPayer(PAYER) .alsoSigningWith(SENDER, RECEIVER) .via(BASIC_XFER) - .recordingScheduledTxn()) - .then( - getScheduleInfo(BASIC_XFER).isNotExecuted(), - getAccountBalance(SENDER).hasTinyBars(1L), - getAccountBalance(RECEIVER).hasTinyBars(0L), - scheduleSign(BASIC_XFER).alsoSigningWith(PAYER).hasKnownStatus(SUCCESS), - getTxnRecord(BASIC_XFER).scheduled(), - getScheduleInfo(BASIC_XFER).isExecuted().hasRecordedScheduledTxn(), - // Very strange. HapiTest fails because We have a - // scheduled and executed record, but the balances did not change... - getAccountBalance(RECEIVER).hasTinyBars(1L), - getAccountBalance(SENDER).hasTinyBars(0L)); + .recordingScheduledTxn(), + getScheduleInfo(BASIC_XFER).isNotExecuted(), + getAccountBalance(SENDER).hasTinyBars(1L), + getAccountBalance(RECEIVER).hasTinyBars(0L), + scheduleSign(BASIC_XFER).alsoSigningWith(PAYER).hasKnownStatus(SUCCESS), + getTxnRecord(BASIC_XFER).scheduled(), + getScheduleInfo(BASIC_XFER).isExecuted().hasRecordedScheduledTxn(), + // Very strange. HapiTest fails because We have a + // scheduled and executed record, but the balances did not change... + getAccountBalance(RECEIVER).hasTinyBars(1L), + getAccountBalance(SENDER).hasTinyBars(0L)); } @HapiTest @@ -483,53 +448,44 @@ final Stream triggersImmediatelyWithBothReqSimpleSigs() { long initialBalance = HapiSpecSetup.getDefaultInstance().defaultBalance(); long transferAmount = 1; - return defaultHapiSpec("TriggersImmediatelyWithBothReqSimpleSigs") - .given(cryptoCreate(SENDER), cryptoCreate(RECEIVER).receiverSigRequired(true)) - .when(scheduleCreate( + return hapiTest( + cryptoCreate(SENDER), + cryptoCreate(RECEIVER).receiverSigRequired(true), + scheduleCreate( BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount)) .memo("Shocked, I tell you!")) .alsoSigningWith(SENDER, RECEIVER) .via(BASIC_XFER) - .recordingScheduledTxn()) - .then( - getTxnRecord(BASIC_XFER).scheduled(), - getScheduleInfo(BASIC_XFER).isExecuted().hasRecordedScheduledTxn(), - getAccountBalance(SENDER).hasTinyBars(initialBalance - transferAmount), - getAccountBalance(RECEIVER).hasTinyBars(initialBalance + transferAmount)); + .recordingScheduledTxn(), + getTxnRecord(BASIC_XFER).scheduled(), + getScheduleInfo(BASIC_XFER).isExecuted().hasRecordedScheduledTxn(), + getAccountBalance(SENDER).hasTinyBars(initialBalance - transferAmount), + getAccountBalance(RECEIVER).hasTinyBars(initialBalance + transferAmount)); } @HapiTest final Stream rejectsUnresolvableReqSigners() { - return defaultHapiSpec("RejectsUnresolvableReqSigners") - .given() - .when() - .then(scheduleCreate( - "xferWithImaginaryAccount", - cryptoTransfer( - tinyBarsFromTo(DEFAULT_PAYER, FUNDING, 1), - tinyBarsFromTo(DESIGNATING_PAYER, FUNDING, 1))) - .hasKnownStatus(UNRESOLVABLE_REQUIRED_SIGNERS)); + return hapiTest(scheduleCreate( + "xferWithImaginaryAccount", + cryptoTransfer( + tinyBarsFromTo(DEFAULT_PAYER, FUNDING, 1), + tinyBarsFromTo(DESIGNATING_PAYER, FUNDING, 1))) + .hasKnownStatus(UNRESOLVABLE_REQUIRED_SIGNERS)); } @HapiTest final Stream rejectsFunctionlessTxn() { - return defaultHapiSpec("RejectsFunctionlessTxn") - .given() - .when() - .then(scheduleCreateFunctionless("unknown") - .hasKnownStatus(SCHEDULED_TRANSACTION_NOT_IN_WHITELIST) - .payingWith(GENESIS)); + return hapiTest(scheduleCreateFunctionless("unknown") + .hasKnownStatus(SCHEDULED_TRANSACTION_NOT_IN_WHITELIST) + .payingWith(GENESIS)); } @HapiTest final Stream functionlessTxnBusyWithNonExemptPayer() { - return defaultHapiSpec("FunctionlessTxnBusyWithNonExemptPayer") - .given() - .when() - .then( - cryptoCreate(SENDER), - scheduleCreateFunctionless("unknown").hasPrecheck(BUSY).payingWith(SENDER)); + return hapiTest( + cryptoCreate(SENDER), + scheduleCreateFunctionless("unknown").hasPrecheck(BUSY).payingWith(SENDER)); } @LeakyHapiTest(overrides = {"scheduling.whitelist"}) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleDeleteTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleDeleteTest.java index cb02f1f9510f..38204307349a 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleDeleteTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleDeleteTest.java @@ -16,7 +16,7 @@ package com.hedera.services.bdd.suites.schedule; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; +import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.queries.QueryVerbs.getScheduleInfo; import static com.hedera.services.bdd.spec.transactions.TxnVerbs.createTopic; import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoCreate; @@ -48,45 +48,40 @@ import org.junit.jupiter.api.DynamicTest; public class ScheduleDeleteTest { + @HapiTest final Stream deleteWithNoAdminKeyFails() { - return defaultHapiSpec("DeleteWithNoAdminKeyFails") - .given( - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)))) - .when() - .then(scheduleDelete(VALID_SCHEDULED_TXN).hasKnownStatus(SCHEDULE_IS_IMMUTABLE)); + return hapiTest( + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1))), + scheduleDelete(VALID_SCHEDULED_TXN).hasKnownStatus(SCHEDULE_IS_IMMUTABLE)); } @HapiTest final Stream unauthorizedDeletionFails() { - return defaultHapiSpec("UnauthorizedDeletionFails") - .given( - newKeyNamed(ADMIN), - newKeyNamed("non-admin-key"), - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1))) - .adminKey(ADMIN)) - .when() - .then(scheduleDelete(VALID_SCHEDULED_TXN) + return hapiTest( + newKeyNamed(ADMIN), + newKeyNamed("non-admin-key"), + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1))) + .adminKey(ADMIN), + scheduleDelete(VALID_SCHEDULED_TXN) .signedBy(DEFAULT_PAYER, "non-admin-key") .hasKnownStatus(INVALID_SIGNATURE)); } @HapiTest final Stream deletingAlreadyDeletedIsObvious() { - return defaultHapiSpec("DeletingAlreadyDeletedIsObvious") - .given( - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - newKeyNamed(ADMIN), - scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1))) - .adminKey(ADMIN), - scheduleDelete(VALID_SCHEDULED_TXN).signedBy(ADMIN, DEFAULT_PAYER)) - .when() - .then(scheduleDelete(VALID_SCHEDULED_TXN) + return hapiTest( + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + newKeyNamed(ADMIN), + scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1))) + .adminKey(ADMIN), + scheduleDelete(VALID_SCHEDULED_TXN).signedBy(ADMIN, DEFAULT_PAYER), + scheduleDelete(VALID_SCHEDULED_TXN) .fee(ONE_HBAR) .signedBy(ADMIN, DEFAULT_PAYER) .hasKnownStatus(SCHEDULE_ALREADY_DELETED)); @@ -94,49 +89,40 @@ final Stream deletingAlreadyDeletedIsObvious() { @HapiTest final Stream idVariantsTreatedAsExpected() { - return defaultHapiSpec("idVariantsTreatedAsExpected") - .given( - newKeyNamed(ADMIN), - cryptoCreate(SENDER), - scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) - .adminKey(ADMIN)) - .when() - .then(submitModified(withSuccessivelyVariedBodyIds(), () -> scheduleDelete(VALID_SCHEDULED_TXN) + return hapiTest( + newKeyNamed(ADMIN), + cryptoCreate(SENDER), + scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) + .adminKey(ADMIN), + submitModified(withSuccessivelyVariedBodyIds(), () -> scheduleDelete(VALID_SCHEDULED_TXN) .signedBy(DEFAULT_PAYER, ADMIN))); } @HapiTest final Stream getScheduleInfoIdVariantsTreatedAsExpected() { - return defaultHapiSpec("getScheduleInfoIdVariantsTreatedAsExpected") - .given( - newKeyNamed(ADMIN), - cryptoCreate(SENDER), - scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) - .adminKey(ADMIN)) - .when() - .then(sendModified(withSuccessivelyVariedQueryIds(), () -> getScheduleInfo(VALID_SCHEDULED_TXN))); + return hapiTest( + newKeyNamed(ADMIN), + cryptoCreate(SENDER), + scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) + .adminKey(ADMIN), + sendModified(withSuccessivelyVariedQueryIds(), () -> getScheduleInfo(VALID_SCHEDULED_TXN))); } @HapiTest final Stream deletingNonExistingFails() { - return defaultHapiSpec("DeletingNonExistingFails") - .given() - .when() - .then( - scheduleDelete("0.0.534").fee(ONE_HBAR).hasKnownStatus(INVALID_SCHEDULE_ID), - scheduleDelete("0.0.0").fee(ONE_HBAR).hasKnownStatus(INVALID_SCHEDULE_ID)); + return hapiTest( + scheduleDelete("0.0.534").fee(ONE_HBAR).hasKnownStatus(INVALID_SCHEDULE_ID), + scheduleDelete("0.0.0").fee(ONE_HBAR).hasKnownStatus(INVALID_SCHEDULE_ID)); } @HapiTest final Stream deletingExecutedIsPointless() { - return defaultHapiSpec("DeletingExecutedIsPointless") - .given( - createTopic("ofGreatInterest"), - newKeyNamed(ADMIN), - scheduleCreate(VALID_SCHEDULED_TXN, submitMessageTo("ofGreatInterest")) - .adminKey(ADMIN)) - .when() - .then(scheduleDelete(VALID_SCHEDULED_TXN) + return hapiTest( + createTopic("ofGreatInterest"), + newKeyNamed(ADMIN), + scheduleCreate(VALID_SCHEDULED_TXN, submitMessageTo("ofGreatInterest")) + .adminKey(ADMIN), + scheduleDelete(VALID_SCHEDULED_TXN) .signedBy(ADMIN, DEFAULT_PAYER) .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED)); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleExecutionTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleExecutionTest.java index 1b00b36a885e..d556de745a3f 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleExecutionTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleExecutionTest.java @@ -16,7 +16,6 @@ package com.hedera.services.bdd.suites.schedule; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountBalance; @@ -133,7 +132,6 @@ import com.hederahashgraph.api.proto.java.AccountID; import com.hederahashgraph.api.proto.java.TokenType; import com.hederahashgraph.api.proto.java.TransactionID; -import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder; import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -144,6 +142,7 @@ import org.junit.jupiter.api.DynamicTest; public class ScheduleExecutionTest { + private final long normalTriggeredTxnTimestampOffset = 1; @SuppressWarnings("java:S2245") // using java.util.Random in tests is fine @@ -151,42 +150,35 @@ public class ScheduleExecutionTest { @HapiTest final Stream scheduledBurnFailsWithInvalidTxBody() { - return defaultHapiSpec("ScheduledBurnFailsWithInvalidTxBody") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0)) - .when(scheduleCreate(A_SCHEDULE, invalidBurnToken(A_TOKEN, List.of(1L, 2L), 123)) + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + scheduleCreate(A_SCHEDULE, invalidBurnToken(A_TOKEN, List.of(1L, 2L), 123)) .designatingPayer(SCHEDULE_PAYER) - .hasKnownStatus(INVALID_TRANSACTION_BODY)) - .then(); + .hasKnownStatus(INVALID_TRANSACTION_BODY)); } @HapiTest final Stream scheduledMintFailsWithInvalidTxBody() { - return defaultHapiSpec("ScheduledMintFailsWithInvalidTxBody") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0)) - .when() - .then( - scheduleCreate( - A_SCHEDULE, - invalidMintToken(A_TOKEN, List.of(ByteString.copyFromUtf8("m1")), 123)) - .hasKnownStatus(INVALID_TRANSACTION_BODY) - .designatingPayer(SCHEDULE_PAYER), - getTokenInfo(A_TOKEN).hasTotalSupply(0)); + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + scheduleCreate(A_SCHEDULE, invalidMintToken(A_TOKEN, List.of(ByteString.copyFromUtf8("m1")), 123)) + .hasKnownStatus(INVALID_TRANSACTION_BODY) + .designatingPayer(SCHEDULE_PAYER), + getTokenInfo(A_TOKEN).hasTotalSupply(0)); } @HapiTest @@ -203,204 +195,165 @@ final Stream scheduledMintWithInvalidTokenThrowsUnresolvableSigners @HapiTest final Stream scheduledUniqueBurnFailsWithInvalidBatchSize() { - return defaultHapiSpec("ScheduledUniqueBurnFailsWithInvalidBatchSize") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0), - mintToken(A_TOKEN, List.of(ByteString.copyFromUtf8("m1"))), - scheduleCreate( - A_SCHEDULE, - burnToken( - A_TOKEN, - List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L))) - .designatingPayer(SCHEDULE_PAYER) - .via(FAILING_TXN)) - .when( - getTokenInfo(A_TOKEN).hasTotalSupply(1), - scheduleSign(A_SCHEDULE) - .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) - .hasKnownStatus(SUCCESS)) - .then( - getTxnRecord(FAILING_TXN) - .scheduled() - .hasPriority(recordWith().status(BATCH_SIZE_LIMIT_EXCEEDED)), - getTokenInfo(A_TOKEN).hasTotalSupply(1)); + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + mintToken(A_TOKEN, List.of(ByteString.copyFromUtf8("m1"))), + scheduleCreate( + A_SCHEDULE, + burnToken(A_TOKEN, List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L))) + .designatingPayer(SCHEDULE_PAYER) + .via(FAILING_TXN), + getTokenInfo(A_TOKEN).hasTotalSupply(1), + scheduleSign(A_SCHEDULE) + .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) + .hasKnownStatus(SUCCESS), + getTxnRecord(FAILING_TXN).scheduled().hasPriority(recordWith().status(BATCH_SIZE_LIMIT_EXCEEDED)), + getTokenInfo(A_TOKEN).hasTotalSupply(1)); } @HapiTest final Stream scheduledUniqueBurnExecutesProperly() { - return defaultHapiSpec("ScheduledUniqueBurnExecutesProperly") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0), - mintToken(A_TOKEN, List.of(ByteString.copyFromUtf8("metadata"))), - scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, List.of(1L))) - .designatingPayer(SCHEDULE_PAYER) - .via(SUCCESS_TXN)) - .when( - getTokenInfo(A_TOKEN).hasTotalSupply(1), - scheduleSign(A_SCHEDULE) - .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) - .via(SIGN_TX) - .hasKnownStatus(SUCCESS)) - .then( - withOpContext((spec, opLog) -> { - var createTx = getTxnRecord(SUCCESS_TXN); - var signTx = getTxnRecord(SIGN_TX); - var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); - allRunFor(spec, createTx, signTx, triggeredTx); - - Assertions.assertEquals( - signTx.getResponseRecord() - .getConsensusTimestamp() - .getNanos() - + normalTriggeredTxnTimestampOffset, - triggeredTx - .getResponseRecord() - .getConsensusTimestamp() - .getNanos(), - WRONG_CONSENSUS_TIMESTAMP); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - WRONG_TRANSACTION_VALID_START); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getAccountID(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getAccountID(), - WRONG_RECORD_ACCOUNT_ID); - - Assertions.assertTrue( - triggeredTx - .getResponseRecord() - .getTransactionID() - .getScheduled(), - TRANSACTION_NOT_SCHEDULED); - - Assertions.assertEquals( - createTx.getResponseRecord().getReceipt().getScheduleID(), - triggeredTx.getResponseRecord().getScheduleRef(), - WRONG_SCHEDULE_ID); - }), - getTokenInfo(A_TOKEN).hasTotalSupply(0)); + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + mintToken(A_TOKEN, List.of(ByteString.copyFromUtf8("metadata"))), + scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, List.of(1L))) + .designatingPayer(SCHEDULE_PAYER) + .via(SUCCESS_TXN), + getTokenInfo(A_TOKEN).hasTotalSupply(1), + scheduleSign(A_SCHEDULE) + .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) + .via(SIGN_TX) + .hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { + var createTx = getTxnRecord(SUCCESS_TXN); + var signTx = getTxnRecord(SIGN_TX); + var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); + allRunFor(spec, createTx, signTx, triggeredTx); + + Assertions.assertEquals( + signTx.getResponseRecord().getConsensusTimestamp().getNanos() + + normalTriggeredTxnTimestampOffset, + triggeredTx + .getResponseRecord() + .getConsensusTimestamp() + .getNanos(), + WRONG_CONSENSUS_TIMESTAMP); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + triggeredTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + WRONG_TRANSACTION_VALID_START); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getAccountID(), + triggeredTx.getResponseRecord().getTransactionID().getAccountID(), + WRONG_RECORD_ACCOUNT_ID); + + Assertions.assertTrue( + triggeredTx.getResponseRecord().getTransactionID().getScheduled(), + TRANSACTION_NOT_SCHEDULED); + + Assertions.assertEquals( + createTx.getResponseRecord().getReceipt().getScheduleID(), + triggeredTx.getResponseRecord().getScheduleRef(), + WRONG_SCHEDULE_ID); + }), + getTokenInfo(A_TOKEN).hasTotalSupply(0)); } @HapiTest final Stream scheduledUniqueMintFailsWithInvalidMetadata() { - return defaultHapiSpec("ScheduledUniqueMintFailsWithInvalidMetadata") - .given( - cryptoCreate("payer"), - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0), - scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, List.of(metadataOfLength(101)))) - .designatingPayer(SCHEDULE_PAYER) - .payingWith("payer") - .via(FAILING_TXN)) - .when(scheduleSign(A_SCHEDULE) + return hapiTest( + cryptoCreate("payer"), + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, List.of(metadataOfLength(101)))) + .designatingPayer(SCHEDULE_PAYER) + .payingWith("payer") + .via(FAILING_TXN), + scheduleSign(A_SCHEDULE) .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) - .hasKnownStatus(SUCCESS)) - .then( - getTxnRecord(FAILING_TXN) - .scheduled() - .hasPriority(recordWith().status(METADATA_TOO_LONG)), - getTokenInfo(A_TOKEN).hasTotalSupply(0)); + .hasKnownStatus(SUCCESS), + getTxnRecord(FAILING_TXN).scheduled().hasPriority(recordWith().status(METADATA_TOO_LONG)), + getTokenInfo(A_TOKEN).hasTotalSupply(0)); } @HapiTest final Stream scheduledUniqueBurnFailsWithInvalidNftId() { - return defaultHapiSpec("ScheduledUniqueBurnFailsWithInvalidNftId") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0), - scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, List.of(123L))) - .designatingPayer(SCHEDULE_PAYER) - .via(FAILING_TXN)) - .when(scheduleSign(A_SCHEDULE) + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, List.of(123L))) + .designatingPayer(SCHEDULE_PAYER) + .via(FAILING_TXN), + scheduleSign(A_SCHEDULE) .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) - .hasKnownStatus(SUCCESS)) - .then(getTxnRecord(FAILING_TXN) - .scheduled() - .hasPriority(recordWith().status(INVALID_NFT_ID))); + .hasKnownStatus(SUCCESS), + getTxnRecord(FAILING_TXN).scheduled().hasPriority(recordWith().status(INVALID_NFT_ID))); } @HapiTest final Stream scheduledBurnForUniqueSucceedsWithExistingAmount() { - return defaultHapiSpec("scheduledBurnForUniqueSucceedsWithExistingAmount") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0), - scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, 123L)) - .designatingPayer(SCHEDULE_PAYER) - .via(SUCCESS_TXN)) - .when(scheduleSign(A_SCHEDULE) + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, 123L)) + .designatingPayer(SCHEDULE_PAYER) + .via(SUCCESS_TXN), + scheduleSign(A_SCHEDULE) .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) - .hasKnownStatus(SUCCESS)) - .then( - getTxnRecord(SUCCESS_TXN) - .scheduled() - .hasPriority(recordWith().status(INVALID_TOKEN_BURN_METADATA)), - getTokenInfo(A_TOKEN).hasTotalSupply(0)); + .hasKnownStatus(SUCCESS), + getTxnRecord(SUCCESS_TXN).scheduled().hasPriority(recordWith().status(INVALID_TOKEN_BURN_METADATA)), + getTokenInfo(A_TOKEN).hasTotalSupply(0)); } @HapiTest final Stream scheduledBurnForUniqueFailsWithInvalidAmount() { - return defaultHapiSpec("ScheduledBurnForUniqueFailsWithInvalidAmount") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0)) - .when() - .then( - scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, -123L)) - .designatingPayer(SCHEDULE_PAYER) - .hasKnownStatus(INVALID_TOKEN_BURN_AMOUNT), - getTokenInfo(A_TOKEN).hasTotalSupply(0)); + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, -123L)) + .designatingPayer(SCHEDULE_PAYER) + .hasKnownStatus(INVALID_TOKEN_BURN_AMOUNT), + getTokenInfo(A_TOKEN).hasTotalSupply(0)); } @LeakyHapiTest(overrides = {"tokens.nfts.maxBatchSizeMint"}) @@ -438,245 +391,238 @@ final Stream scheduledUniqueMintFailsWithInvalidBatchSize() { @HapiTest final Stream scheduledMintFailsWithInvalidAmount() { final var zeroAmountTxn = "zeroAmountTxn"; - return defaultHapiSpec("ScheduledMintFailsWithInvalidAmount") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .initialSupply(101), - scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, 0)) - .designatingPayer(SCHEDULE_PAYER) - .via(zeroAmountTxn)) - .when() - .then( - scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, -1)) - .designatingPayer(SCHEDULE_PAYER) - .hasKnownStatus(INVALID_TOKEN_MINT_AMOUNT), - getTokenInfo(A_TOKEN).hasTotalSupply(101)); + + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN).supplyKey(SUPPLY_KEY).treasury(TREASURY).initialSupply(101), + scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, 0)) + .designatingPayer(SCHEDULE_PAYER) + .via(zeroAmountTxn), + scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, -1)) + .designatingPayer(SCHEDULE_PAYER) + .hasKnownStatus(INVALID_TOKEN_MINT_AMOUNT), + getTokenInfo(A_TOKEN).hasTotalSupply(101)); } @HapiTest final Stream scheduledUniqueMintExecutesProperly() { - return defaultHapiSpec("ScheduledUniqueMintExecutesProperly") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .initialSupply(0), - scheduleCreate( - A_SCHEDULE, - mintToken( - A_TOKEN, - List.of( - ByteString.copyFromUtf8("somemetadata1"), - ByteString.copyFromUtf8("somemetadata2")))) - .designatingPayer(SCHEDULE_PAYER) - .via(SUCCESS_TXN)) - .when(scheduleSign(A_SCHEDULE) + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .initialSupply(0), + scheduleCreate( + A_SCHEDULE, + mintToken( + A_TOKEN, + List.of( + ByteString.copyFromUtf8("somemetadata1"), + ByteString.copyFromUtf8("somemetadata2")))) + .designatingPayer(SCHEDULE_PAYER) + .via(SUCCESS_TXN), + scheduleSign(A_SCHEDULE) .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) .via(SIGN_TX) - .hasKnownStatus(SUCCESS)) - .then( - withOpContext((spec, opLog) -> { - var createTx = getTxnRecord(SUCCESS_TXN); - var signTx = getTxnRecord(SIGN_TX); - var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); - allRunFor(spec, createTx, signTx, triggeredTx); - - Assertions.assertEquals( - signTx.getResponseRecord() - .getConsensusTimestamp() - .getNanos() - + normalTriggeredTxnTimestampOffset, - triggeredTx - .getResponseRecord() - .getConsensusTimestamp() - .getNanos(), - WRONG_CONSENSUS_TIMESTAMP); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - WRONG_TRANSACTION_VALID_START); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getAccountID(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getAccountID(), - WRONG_RECORD_ACCOUNT_ID); - - Assertions.assertTrue( - triggeredTx - .getResponseRecord() - .getTransactionID() - .getScheduled(), - TRANSACTION_NOT_SCHEDULED); - - Assertions.assertEquals( - createTx.getResponseRecord().getReceipt().getScheduleID(), - triggeredTx.getResponseRecord().getScheduleRef(), - WRONG_SCHEDULE_ID); - }), - getTokenInfo(A_TOKEN).hasTotalSupply(2)); + .hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { + var createTx = getTxnRecord(SUCCESS_TXN); + var signTx = getTxnRecord(SIGN_TX); + var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); + allRunFor(spec, createTx, signTx, triggeredTx); + + Assertions.assertEquals( + signTx.getResponseRecord().getConsensusTimestamp().getNanos() + + normalTriggeredTxnTimestampOffset, + triggeredTx + .getResponseRecord() + .getConsensusTimestamp() + .getNanos(), + WRONG_CONSENSUS_TIMESTAMP); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + triggeredTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + WRONG_TRANSACTION_VALID_START); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getAccountID(), + triggeredTx.getResponseRecord().getTransactionID().getAccountID(), + WRONG_RECORD_ACCOUNT_ID); + + Assertions.assertTrue( + triggeredTx.getResponseRecord().getTransactionID().getScheduled(), + TRANSACTION_NOT_SCHEDULED); + + Assertions.assertEquals( + createTx.getResponseRecord().getReceipt().getScheduleID(), + triggeredTx.getResponseRecord().getScheduleRef(), + WRONG_SCHEDULE_ID); + }), + getTokenInfo(A_TOKEN).hasTotalSupply(2)); } @HapiTest final Stream scheduledMintExecutesProperly() { - return defaultHapiSpec("ScheduledMintExecutesProperly") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .initialSupply(101), - scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, 10)) - .designatingPayer(SCHEDULE_PAYER) - .via(SUCCESS_TXN)) - .when(scheduleSign(A_SCHEDULE) + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN).supplyKey(SUPPLY_KEY).treasury(TREASURY).initialSupply(101), + scheduleCreate(A_SCHEDULE, mintToken(A_TOKEN, 10)) + .designatingPayer(SCHEDULE_PAYER) + .via(SUCCESS_TXN), + scheduleSign(A_SCHEDULE) .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) .via(SIGN_TX) - .hasKnownStatus(SUCCESS)) - .then( - withOpContext((spec, opLog) -> { - var createTx = getTxnRecord(SUCCESS_TXN); - var signTx = getTxnRecord(SIGN_TX); - var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); - allRunFor(spec, createTx, signTx, triggeredTx); - - Assertions.assertEquals( - signTx.getResponseRecord() - .getConsensusTimestamp() - .getNanos() - + normalTriggeredTxnTimestampOffset, - triggeredTx - .getResponseRecord() - .getConsensusTimestamp() - .getNanos(), - WRONG_CONSENSUS_TIMESTAMP); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - WRONG_TRANSACTION_VALID_START); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getAccountID(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getAccountID(), - WRONG_RECORD_ACCOUNT_ID); - - Assertions.assertTrue( - triggeredTx - .getResponseRecord() - .getTransactionID() - .getScheduled(), - TRANSACTION_NOT_SCHEDULED); - - Assertions.assertEquals( - createTx.getResponseRecord().getReceipt().getScheduleID(), - triggeredTx.getResponseRecord().getScheduleRef(), - WRONG_SCHEDULE_ID); - }), - getTokenInfo(A_TOKEN).hasTotalSupply(111)); + .hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { + var createTx = getTxnRecord(SUCCESS_TXN); + var signTx = getTxnRecord(SIGN_TX); + var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); + allRunFor(spec, createTx, signTx, triggeredTx); + + Assertions.assertEquals( + signTx.getResponseRecord().getConsensusTimestamp().getNanos() + + normalTriggeredTxnTimestampOffset, + triggeredTx + .getResponseRecord() + .getConsensusTimestamp() + .getNanos(), + WRONG_CONSENSUS_TIMESTAMP); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + triggeredTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + WRONG_TRANSACTION_VALID_START); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getAccountID(), + triggeredTx.getResponseRecord().getTransactionID().getAccountID(), + WRONG_RECORD_ACCOUNT_ID); + + Assertions.assertTrue( + triggeredTx.getResponseRecord().getTransactionID().getScheduled(), + TRANSACTION_NOT_SCHEDULED); + + Assertions.assertEquals( + createTx.getResponseRecord().getReceipt().getScheduleID(), + triggeredTx.getResponseRecord().getScheduleRef(), + WRONG_SCHEDULE_ID); + }), + getTokenInfo(A_TOKEN).hasTotalSupply(111)); + } + + @HapiTest + final Stream scheduledXferFailingWithFrozenAccountTransferPaysServiceFeeButNoImpact() { + String xToken = "XXX"; + String validSchedule = "withUnfrozenAccount"; + String invalidSchedule = "withFrozenAccount"; + String schedulePayer = PAYER; + String xTreasury = "xt"; + String xCivilian = "xc"; + String successTx = "good"; + String failedTx = "bad"; + AtomicReference> successFeesObs = new AtomicReference<>(); + AtomicReference> failureFeesObs = new AtomicReference<>(); + + return hapiTest( + newKeyNamed("freeze"), + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(xCivilian), + tokenCreate(xToken) + .treasury(xTreasury) + .initialSupply(101) + .freezeKey("freeze") + .freezeDefault(true), + tokenAssociate(xCivilian, xToken), + tokenUnfreeze(xToken, xCivilian), + scheduleCreate( + validSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) + .memo(randomUppercase(100))) + .via(successTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + tokenFreeze(xToken, xCivilian), + scheduleCreate( + invalidSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) + .memo(randomUppercase(100))) + .via(failedTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(ACCOUNT_FROZEN_FOR_TOKEN)) + .revealingDebitsTo(failureFeesObs::set), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + assertionsHold( + (spec, opLog) -> assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); } @HapiTest final Stream scheduledBurnExecutesProperly() { - return defaultHapiSpec("ScheduledBurnExecutesProperly") - .given( - cryptoCreate(TREASURY), - cryptoCreate(SCHEDULE_PAYER), - newKeyNamed(SUPPLY_KEY), - tokenCreate(A_TOKEN) - .supplyKey(SUPPLY_KEY) - .treasury(TREASURY) - .tokenType(TokenType.FUNGIBLE_COMMON) - .initialSupply(101), - scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, 10)) - .designatingPayer(SCHEDULE_PAYER) - .via(SUCCESS_TXN)) - .when(scheduleSign(A_SCHEDULE) + return hapiTest( + cryptoCreate(TREASURY), + cryptoCreate(SCHEDULE_PAYER), + newKeyNamed(SUPPLY_KEY), + tokenCreate(A_TOKEN) + .supplyKey(SUPPLY_KEY) + .treasury(TREASURY) + .tokenType(TokenType.FUNGIBLE_COMMON) + .initialSupply(101), + scheduleCreate(A_SCHEDULE, burnToken(A_TOKEN, 10)) + .designatingPayer(SCHEDULE_PAYER) + .via(SUCCESS_TXN), + scheduleSign(A_SCHEDULE) .alsoSigningWith(SUPPLY_KEY, SCHEDULE_PAYER, TREASURY) .via(SIGN_TX) - .hasKnownStatus(SUCCESS)) - .then( - withOpContext((spec, opLog) -> { - var createTx = getTxnRecord(SUCCESS_TXN); - var signTx = getTxnRecord(SIGN_TX); - var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); - allRunFor(spec, createTx, signTx, triggeredTx); - - Assertions.assertEquals( - signTx.getResponseRecord() - .getConsensusTimestamp() - .getNanos() - + normalTriggeredTxnTimestampOffset, - triggeredTx - .getResponseRecord() - .getConsensusTimestamp() - .getNanos(), - WRONG_CONSENSUS_TIMESTAMP); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getTransactionValidStart(), - WRONG_TRANSACTION_VALID_START); - - Assertions.assertEquals( - createTx.getResponseRecord() - .getTransactionID() - .getAccountID(), - triggeredTx - .getResponseRecord() - .getTransactionID() - .getAccountID(), - WRONG_RECORD_ACCOUNT_ID); - - Assertions.assertTrue( - triggeredTx - .getResponseRecord() - .getTransactionID() - .getScheduled(), - TRANSACTION_NOT_SCHEDULED); - - Assertions.assertEquals( - createTx.getResponseRecord().getReceipt().getScheduleID(), - triggeredTx.getResponseRecord().getScheduleRef(), - WRONG_SCHEDULE_ID); - }), - getTokenInfo(A_TOKEN).hasTotalSupply(91)); + .hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { + var createTx = getTxnRecord(SUCCESS_TXN); + var signTx = getTxnRecord(SIGN_TX); + var triggeredTx = getTxnRecord(SUCCESS_TXN).scheduled(); + allRunFor(spec, createTx, signTx, triggeredTx); + + Assertions.assertEquals( + signTx.getResponseRecord().getConsensusTimestamp().getNanos() + + normalTriggeredTxnTimestampOffset, + triggeredTx + .getResponseRecord() + .getConsensusTimestamp() + .getNanos(), + WRONG_CONSENSUS_TIMESTAMP); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + triggeredTx.getResponseRecord().getTransactionID().getTransactionValidStart(), + WRONG_TRANSACTION_VALID_START); + + Assertions.assertEquals( + createTx.getResponseRecord().getTransactionID().getAccountID(), + triggeredTx.getResponseRecord().getTransactionID().getAccountID(), + WRONG_RECORD_ACCOUNT_ID); + + Assertions.assertTrue( + triggeredTx.getResponseRecord().getTransactionID().getScheduled(), + TRANSACTION_NOT_SCHEDULED); + + Assertions.assertEquals( + createTx.getResponseRecord().getReceipt().getScheduleID(), + triggeredTx.getResponseRecord().getScheduleRef(), + WRONG_SCHEDULE_ID); + }), + getTokenInfo(A_TOKEN).hasTotalSupply(91)); } @HapiTest @@ -693,40 +639,33 @@ final Stream scheduledXferFailingWithDeletedAccountPaysServiceFeeBu final AtomicReference> successFeesObs = new AtomicReference<>(); final AtomicReference> failureFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledXferFailingWithDeletedTokenPaysServiceFeeButNoImpact") - .given( - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(xCivilian), - cryptoCreate(deadXCivilian), - tokenCreate(xToken).treasury(xTreasury).initialSupply(101), - tokenAssociate(xCivilian, xToken), - tokenAssociate(deadXCivilian, xToken)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian))) - .via(successTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), - cryptoDelete(deadXCivilian), - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, deadXCivilian))) - .via(failedTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer)) - .then( - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(ACCOUNT_DELETED)) - .revealingDebitsTo(failureFeesObs::set), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - assertionsHold((spec, opLog) -> - assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); + return hapiTest( + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(xCivilian), + cryptoCreate(deadXCivilian), + tokenCreate(xToken).treasury(xTreasury).initialSupply(101), + tokenAssociate(xCivilian, xToken), + tokenAssociate(deadXCivilian, xToken), + scheduleCreate(validSchedule, cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian))) + .via(successTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + cryptoDelete(deadXCivilian), + scheduleCreate(invalidSchedule, cryptoTransfer(moving(1, xToken).between(xTreasury, deadXCivilian))) + .via(failedTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(ACCOUNT_DELETED)) + .revealingDebitsTo(failureFeesObs::set), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + assertionsHold( + (spec, opLog) -> assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); } @HapiTest @@ -742,99 +681,38 @@ final Stream scheduledXferFailingWithDeletedTokenPaysServiceFeeButN AtomicReference> successFeesObs = new AtomicReference<>(); AtomicReference> failureFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledXferFailingWithDeletedTokenPaysServiceFeeButNoImpact") - .given( - newKeyNamed(ADMIN), - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(xCivilian), - tokenCreate(xToken) - .treasury(xTreasury) - .initialSupply(101) - .adminKey(ADMIN), - tokenAssociate(xCivilian, xToken)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) - .memo(randomUppercase(100))) - .via(successTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), - tokenDelete(xToken), - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) - .memo(randomUppercase(100))) - .via(failedTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer)) - .then( - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(TOKEN_WAS_DELETED)) - .revealingDebitsTo(failureFeesObs::set), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - assertionsHold((spec, opLog) -> - assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); - } - - @HapiTest - final Stream scheduledXferFailingWithFrozenAccountTransferPaysServiceFeeButNoImpact() { - String xToken = "XXX"; - String validSchedule = "withUnfrozenAccount"; - String invalidSchedule = "withFrozenAccount"; - String schedulePayer = PAYER; - String xTreasury = "xt"; - String xCivilian = "xc"; - String successTx = "good"; - String failedTx = "bad"; - AtomicReference> successFeesObs = new AtomicReference<>(); - AtomicReference> failureFeesObs = new AtomicReference<>(); - - return defaultHapiSpec("ScheduledXferFailingWithFrozenAccountTransferPaysServiceFeeButNoImpact") - .given( - newKeyNamed("freeze"), - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(xCivilian), - tokenCreate(xToken) - .treasury(xTreasury) - .initialSupply(101) - .freezeKey("freeze") - .freezeDefault(true), - tokenAssociate(xCivilian, xToken), - tokenUnfreeze(xToken, xCivilian)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) - .memo(randomUppercase(100))) - .via(successTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), - tokenFreeze(xToken, xCivilian), - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) - .memo(randomUppercase(100))) - .via(failedTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer)) - .then( - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(ACCOUNT_FROZEN_FOR_TOKEN)) - .revealingDebitsTo(failureFeesObs::set), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - assertionsHold((spec, opLog) -> - assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); + return hapiTest( + newKeyNamed(ADMIN), + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(xCivilian), + tokenCreate(xToken).treasury(xTreasury).initialSupply(101).adminKey(ADMIN), + tokenAssociate(xCivilian, xToken), + scheduleCreate( + validSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) + .memo(randomUppercase(100))) + .via(successTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + tokenDelete(xToken), + scheduleCreate( + invalidSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) + .memo(randomUppercase(100))) + .via(failedTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(TOKEN_WAS_DELETED)) + .revealingDebitsTo(failureFeesObs::set), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + assertionsHold( + (spec, opLog) -> assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); } @HapiTest @@ -850,45 +728,39 @@ final Stream scheduledXferFailingWithNonKycedAccountTransferPaysSer AtomicReference> successFeesObs = new AtomicReference<>(); AtomicReference> failureFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledXferFailingWithNonKycedAccountTransferPaysServiceFeeButNoImpact") - .given( - newKeyNamed("kyc"), - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(xCivilian), - tokenCreate(xToken) - .treasury(xTreasury) - .initialSupply(101) - .kycKey("kyc"), - tokenAssociate(xCivilian, xToken), - grantTokenKyc(xToken, xCivilian)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) - .memo(randomUppercase(100))) - .via(successTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), - revokeTokenKyc(xToken, xCivilian), - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) - .memo(randomUppercase(100))) - .via(failedTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer)) - .then( - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(ACCOUNT_KYC_NOT_GRANTED_FOR_TOKEN)) - .revealingDebitsTo(failureFeesObs::set), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - assertionsHold((spec, opLog) -> - assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); + return hapiTest( + newKeyNamed("kyc"), + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(xCivilian), + tokenCreate(xToken).treasury(xTreasury).initialSupply(101).kycKey("kyc"), + tokenAssociate(xCivilian, xToken), + grantTokenKyc(xToken, xCivilian), + scheduleCreate( + validSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) + .memo(randomUppercase(100))) + .via(successTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + revokeTokenKyc(xToken, xCivilian), + scheduleCreate( + invalidSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) + .memo(randomUppercase(100))) + .via(failedTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(ACCOUNT_KYC_NOT_GRANTED_FOR_TOKEN)) + .revealingDebitsTo(failureFeesObs::set), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + assertionsHold( + (spec, opLog) -> assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); } @HapiTest @@ -905,39 +777,32 @@ final Stream scheduledXferFailingWithUnassociatedAccountTransferPay AtomicReference> successFeesObs = new AtomicReference<>(); AtomicReference> failureFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledXferFailingWithUnassociatedAccountTransferPaysServiceFeeButNoImpact") - .given( - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(xCivilian), - cryptoCreate(nonXCivilian), - tokenCreate(xToken).treasury(xTreasury).initialSupply(101), - tokenAssociate(xCivilian, xToken)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian))) - .via(successTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, nonXCivilian))) - .via(failedTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer)) - .then( - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(TOKEN_NOT_ASSOCIATED_TO_ACCOUNT)) - .revealingDebitsTo(failureFeesObs::set), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountInfo(nonXCivilian).hasNoTokenRelationship(xToken), - assertionsHold((spec, opLog) -> - assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); + return hapiTest( + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(xCivilian), + cryptoCreate(nonXCivilian), + tokenCreate(xToken).treasury(xTreasury).initialSupply(101), + tokenAssociate(xCivilian, xToken), + scheduleCreate(validSchedule, cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian))) + .via(successTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + scheduleCreate(invalidSchedule, cryptoTransfer(moving(1, xToken).between(xTreasury, nonXCivilian))) + .via(failedTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(TOKEN_NOT_ASSOCIATED_TO_ACCOUNT)) + .revealingDebitsTo(failureFeesObs::set), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountInfo(nonXCivilian).hasNoTokenRelationship(xToken), + assertionsHold( + (spec, opLog) -> assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); } @HapiTest @@ -951,33 +816,28 @@ final Stream scheduledXferFailingWithNonNetZeroTokenTransferPaysSer String successTx = "good"; AtomicReference> successFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledXferFailingWithRepeatedTokenIdPaysServiceFeeButNoImpact") - .given( - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(xCivilian), - tokenCreate(xToken).treasury(xTreasury).initialSupply(101), - tokenAssociate(xCivilian, xToken)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian))) - .via(successTx) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set)) - .then( - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) - .breakingNetZeroInvariant()) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer) - .hasKnownStatus(TRANSFERS_NOT_ZERO_SUM_FOR_TOKEN), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xCivilian).hasTokenBalance(xToken, 1)); + return hapiTest( + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(xCivilian), + tokenCreate(xToken).treasury(xTreasury).initialSupply(101), + tokenAssociate(xCivilian, xToken), + scheduleCreate(validSchedule, cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian))) + .via(successTx) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xCivilian).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + scheduleCreate( + invalidSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, xCivilian)) + .breakingNetZeroInvariant()) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer) + .hasKnownStatus(TRANSFERS_NOT_ZERO_SUM_FOR_TOKEN), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xCivilian).hasTokenBalance(xToken, 1)); } @HapiTest @@ -992,41 +852,38 @@ final Stream scheduledXferFailingWithRepeatedTokenIdPaysServiceFeeB String successTx = "good"; AtomicReference> successFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledXferFailingWithRepeatedTokenIdPaysServiceFeeButNoImpact") - .given( - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(yTreasury), - tokenCreate(xToken).treasury(xTreasury).initialSupply(101), - tokenCreate(yToken).treasury(yTreasury).initialSupply(101), - tokenAssociate(xTreasury, yToken), - tokenAssociate(yTreasury, xToken)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer( - moving(1, xToken).between(xTreasury, yTreasury), - moving(1, yToken).between(yTreasury, xTreasury))) - .via(successTx) - .alsoSigningWith(xTreasury, yTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), - getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), - getAccountBalance(yTreasury).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set)) - .then( - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(1, xToken).between(xTreasury, yTreasury)) - .appendingTokenFromTo(xToken, xTreasury, yTreasury, 1)) - .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer) - .hasKnownStatus(TOKEN_ID_REPEATED_IN_TOKEN_LIST), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), - getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), - getAccountBalance(yTreasury).hasTokenBalance(xToken, 1)); + return hapiTest( + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(yTreasury), + tokenCreate(xToken).treasury(xTreasury).initialSupply(101), + tokenCreate(yToken).treasury(yTreasury).initialSupply(101), + tokenAssociate(xTreasury, yToken), + tokenAssociate(yTreasury, xToken), + scheduleCreate( + validSchedule, + cryptoTransfer( + moving(1, xToken).between(xTreasury, yTreasury), + moving(1, yToken).between(yTreasury, xTreasury))) + .via(successTx) + .alsoSigningWith(xTreasury, yTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), + getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), + getAccountBalance(yTreasury).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + scheduleCreate( + invalidSchedule, + cryptoTransfer(moving(1, xToken).between(xTreasury, yTreasury)) + .appendingTokenFromTo(xToken, xTreasury, yTreasury, 1)) + .alsoSigningWith(xTreasury, schedulePayer) + .designatingPayer(schedulePayer) + .hasKnownStatus(TOKEN_ID_REPEATED_IN_TOKEN_LIST), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), + getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), + getAccountBalance(yTreasury).hasTokenBalance(xToken, 1)); } @HapiTest @@ -1042,42 +899,39 @@ final Stream scheduledXferFailingWithEmptyTokenTransferAccountAmoun String successTx = "good"; AtomicReference> successFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledXferFailingWithEmptyTokenTransferAccountAmountsPaysServiceFeeButNoImpact") - .given( - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(yTreasury), - cryptoCreate(xyCivilian), - tokenCreate(xToken).treasury(xTreasury).initialSupply(101), - tokenCreate(yToken).treasury(yTreasury).initialSupply(101), - tokenAssociate(xTreasury, yToken), - tokenAssociate(yTreasury, xToken)) - .when( - scheduleCreate( - validSchedule, - cryptoTransfer( - moving(1, xToken).between(xTreasury, yTreasury), - moving(1, yToken).between(yTreasury, xTreasury))) - .via(successTx) - .alsoSigningWith(xTreasury, yTreasury, schedulePayer) - .designatingPayer(schedulePayer), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), - getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), - getAccountBalance(yTreasury).hasTokenBalance(xToken, 1), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set)) - .then( - scheduleCreate( - invalidSchedule, - cryptoTransfer(moving(2, xToken).distributing(xTreasury, yTreasury, xyCivilian)) - .withEmptyTokenTransfers(yToken)) - .alsoSigningWith(xTreasury, yTreasury, schedulePayer) - .designatingPayer(schedulePayer) - .hasKnownStatus(EMPTY_TOKEN_TRANSFER_ACCOUNT_AMOUNTS), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), - getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), - getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), - getAccountBalance(yTreasury).hasTokenBalance(xToken, 1)); + return hapiTest( + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(yTreasury), + cryptoCreate(xyCivilian), + tokenCreate(xToken).treasury(xTreasury).initialSupply(101), + tokenCreate(yToken).treasury(yTreasury).initialSupply(101), + tokenAssociate(xTreasury, yToken), + tokenAssociate(yTreasury, xToken), + scheduleCreate( + validSchedule, + cryptoTransfer( + moving(1, xToken).between(xTreasury, yTreasury), + moving(1, yToken).between(yTreasury, xTreasury))) + .via(successTx) + .alsoSigningWith(xTreasury, yTreasury, schedulePayer) + .designatingPayer(schedulePayer), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), + getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), + getAccountBalance(yTreasury).hasTokenBalance(xToken, 1), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + scheduleCreate( + invalidSchedule, + cryptoTransfer(moving(2, xToken).distributing(xTreasury, yTreasury, xyCivilian)) + .withEmptyTokenTransfers(yToken)) + .alsoSigningWith(xTreasury, yTreasury, schedulePayer) + .designatingPayer(schedulePayer) + .hasKnownStatus(EMPTY_TOKEN_TRANSFER_ACCOUNT_AMOUNTS), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100), + getAccountBalance(xTreasury).hasTokenBalance(yToken, 1), + getAccountBalance(yTreasury).hasTokenBalance(yToken, 100), + getAccountBalance(yTreasury).hasTokenBalance(xToken, 1)); } @HapiTest @@ -1091,9 +945,10 @@ final Stream scheduledSubmitFailedWithMsgSizeTooLargeStillPaysServi AtomicReference> successFeesObs = new AtomicReference<>(); AtomicReference> failureFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledSubmitFailedWithMsgSizeTooLargeStillPaysServiceFeeButHasNoImpact") - .given(createTopic(immutableTopic), cryptoCreate(schedulePayer)) - .when(doSeveralWithStartupConfig("consensus.message.maxBytesAllowed", value -> { + return hapiTest( + createTopic(immutableTopic), + cryptoCreate(schedulePayer), + doSeveralWithStartupConfig("consensus.message.maxBytesAllowed", value -> { final var maxValidLen = parseInt(value); return specOps( scheduleCreate( @@ -1110,15 +965,14 @@ final Stream scheduledSubmitFailedWithMsgSizeTooLargeStillPaysServi .designatingPayer(schedulePayer) .via(failedTx) .signedBy(DEFAULT_PAYER, schedulePayer)); - })) - .then( - getTopicInfo(immutableTopic).hasSeqNo(1L), - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(MESSAGE_SIZE_TOO_LARGE)) - .revealingDebitsTo(failureFeesObs::set), - assertionsHold((spec, opLog) -> - assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); + }), + getTopicInfo(immutableTopic).hasSeqNo(1L), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(MESSAGE_SIZE_TOO_LARGE)) + .revealingDebitsTo(failureFeesObs::set), + assertionsHold( + (spec, opLog) -> assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); } @HapiTest @@ -1134,41 +988,38 @@ final Stream scheduledSubmitFailedWithInvalidChunkTxnIdStillPaysSer AtomicReference initialTxnId = new AtomicReference<>(); AtomicReference irrelevantTxnId = new AtomicReference<>(); - return defaultHapiSpec("ScheduledSubmitFailedWithInvalidChunkTxnIdStillPaysServiceFeeButHasNoImpact") - .given(createTopic(immutableTopic), cryptoCreate(schedulePayer)) - .when( - withOpContext((spec, opLog) -> { - var subOp = usableTxnIdNamed(successTx).payerId(schedulePayer); - var secondSubOp = usableTxnIdNamed("wontWork").payerId(schedulePayer); - allRunFor(spec, subOp, secondSubOp); - initialTxnId.set(spec.registry().getTxnId(successTx)); - irrelevantTxnId.set(spec.registry().getTxnId("wontWork")); - }), - sourcing(() -> scheduleCreate( - validSchedule, - submitMessageTo(immutableTopic) - .chunkInfo(3, 1, scheduledVersionOf(initialTxnId.get()))) - .txnId(successTx) - .logged() - .signedBy(schedulePayer)), - getTopicInfo(immutableTopic).hasSeqNo(1L), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), - sourcing(() -> scheduleCreate( - invalidSchedule, - submitMessageTo(immutableTopic) - .chunkInfo(3, 1, scheduledVersionOf(irrelevantTxnId.get()))) - .designatingPayer(schedulePayer) - .via(failedTx) - .logged() - .signedBy(DEFAULT_PAYER, schedulePayer))) - .then( - getTopicInfo(immutableTopic).hasSeqNo(1L), - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(INVALID_CHUNK_TRANSACTION_ID)) - .revealingDebitsTo(failureFeesObs::set), - assertionsHold( - (spec, opLog) -> Assertions.assertEquals(successFeesObs.get(), failureFeesObs.get()))); + return hapiTest( + createTopic(immutableTopic), + cryptoCreate(schedulePayer), + withOpContext((spec, opLog) -> { + var subOp = usableTxnIdNamed(successTx).payerId(schedulePayer); + var secondSubOp = usableTxnIdNamed("wontWork").payerId(schedulePayer); + allRunFor(spec, subOp, secondSubOp); + initialTxnId.set(spec.registry().getTxnId(successTx)); + irrelevantTxnId.set(spec.registry().getTxnId("wontWork")); + }), + sourcing(() -> scheduleCreate( + validSchedule, + submitMessageTo(immutableTopic).chunkInfo(3, 1, scheduledVersionOf(initialTxnId.get()))) + .txnId(successTx) + .logged() + .signedBy(schedulePayer)), + getTopicInfo(immutableTopic).hasSeqNo(1L), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + sourcing(() -> scheduleCreate( + invalidSchedule, + submitMessageTo(immutableTopic) + .chunkInfo(3, 1, scheduledVersionOf(irrelevantTxnId.get()))) + .designatingPayer(schedulePayer) + .via(failedTx) + .logged() + .signedBy(DEFAULT_PAYER, schedulePayer)), + getTopicInfo(immutableTopic).hasSeqNo(1L), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(INVALID_CHUNK_TRANSACTION_ID)) + .revealingDebitsTo(failureFeesObs::set), + assertionsHold((spec, opLog) -> Assertions.assertEquals(successFeesObs.get(), failureFeesObs.get()))); } @HapiTest @@ -1183,37 +1034,32 @@ final Stream scheduledSubmitFailedWithInvalidChunkNumberStillPaysSe AtomicReference> failureFeesObs = new AtomicReference<>(); AtomicReference initialTxnId = new AtomicReference<>(); - return defaultHapiSpec("ScheduledSubmitFailedWithInvalidChunkNumberStillPaysServiceFeeButHasNoImpact") - .given(createTopic(immutableTopic), cryptoCreate(schedulePayer)) - .when( - withOpContext((spec, opLog) -> { - var subOp = usableTxnIdNamed(successTx).payerId(schedulePayer); - allRunFor(spec, subOp); - initialTxnId.set(spec.registry().getTxnId(successTx)); - }), - sourcing(() -> scheduleCreate( - validSchedule, - submitMessageTo(immutableTopic) - .chunkInfo(3, 1, scheduledVersionOf(initialTxnId.get()))) - .txnId(successTx) - .logged() - .signedBy(schedulePayer)), - getTopicInfo(immutableTopic).hasSeqNo(1L), - getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), - scheduleCreate( - invalidSchedule, - submitMessageTo(immutableTopic).chunkInfo(3, 111, schedulePayer)) - .via(failedTx) - .logged() - .payingWith(schedulePayer)) - .then( - getTopicInfo(immutableTopic).hasSeqNo(1L), - getTxnRecord(failedTx) - .scheduled() - .hasPriority(recordWith().status(INVALID_CHUNK_NUMBER)) - .revealingDebitsTo(failureFeesObs::set), - assertionsHold( - (spec, opLog) -> Assertions.assertEquals(successFeesObs.get(), failureFeesObs.get()))); + return hapiTest( + createTopic(immutableTopic), + cryptoCreate(schedulePayer), + withOpContext((spec, opLog) -> { + var subOp = usableTxnIdNamed(successTx).payerId(schedulePayer); + allRunFor(spec, subOp); + initialTxnId.set(spec.registry().getTxnId(successTx)); + }), + sourcing(() -> scheduleCreate( + validSchedule, + submitMessageTo(immutableTopic).chunkInfo(3, 1, scheduledVersionOf(initialTxnId.get()))) + .txnId(successTx) + .logged() + .signedBy(schedulePayer)), + getTopicInfo(immutableTopic).hasSeqNo(1L), + getTxnRecord(successTx).scheduled().logged().revealingDebitsTo(successFeesObs::set), + scheduleCreate(invalidSchedule, submitMessageTo(immutableTopic).chunkInfo(3, 111, schedulePayer)) + .via(failedTx) + .logged() + .payingWith(schedulePayer), + getTopicInfo(immutableTopic).hasSeqNo(1L), + getTxnRecord(failedTx) + .scheduled() + .hasPriority(recordWith().status(INVALID_CHUNK_NUMBER)) + .revealingDebitsTo(failureFeesObs::set), + assertionsHold((spec, opLog) -> Assertions.assertEquals(successFeesObs.get(), failureFeesObs.get()))); } @HapiTest @@ -1222,23 +1068,22 @@ final Stream scheduledSubmitThatWouldFailWithInvalidTopicIdCannotBe AtomicReference> successFeesObs = new AtomicReference<>(); AtomicReference> failureFeesObs = new AtomicReference<>(); - return defaultHapiSpec("ScheduledSubmitThatWouldFailWithInvalidTopicIdCannotBeScheduled") - .given(cryptoCreate(civilianPayer), createTopic("fascinating")) - .when( - scheduleCreate("yup", submitMessageTo("fascinating").message(RANDOM_MSG)) - .payingWith(civilianPayer) - .via(CREATION), - scheduleCreate("nope", submitMessageTo("1.2.3").message(RANDOM_MSG)) - .payingWith(civilianPayer) - .via("nothingShouldBeCreated") - .hasKnownStatus(UNRESOLVABLE_REQUIRED_SIGNERS)) - .then( - getTxnRecord(CREATION).revealingDebitsTo(successFeesObs::set), - getTxnRecord("nothingShouldBeCreated") - .revealingDebitsTo(failureFeesObs::set) - .logged(), - assertionsHold((spec, opLog) -> - assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); + return hapiTest( + cryptoCreate(civilianPayer), + createTopic("fascinating"), + scheduleCreate("yup", submitMessageTo("fascinating").message(RANDOM_MSG)) + .payingWith(civilianPayer) + .via(CREATION), + scheduleCreate("nope", submitMessageTo("1.2.3").message(RANDOM_MSG)) + .payingWith(civilianPayer) + .via("nothingShouldBeCreated") + .hasKnownStatus(UNRESOLVABLE_REQUIRED_SIGNERS), + getTxnRecord(CREATION).revealingDebitsTo(successFeesObs::set), + getTxnRecord("nothingShouldBeCreated") + .revealingDebitsTo(failureFeesObs::set) + .logged(), + assertionsHold( + (spec, opLog) -> assertBasicallyIdentical(successFeesObs.get(), failureFeesObs.get(), 1.0))); } @HapiTest @@ -1249,19 +1094,16 @@ final Stream scheduledSubmitThatWouldFailWithTopicDeletedCannotBeSi String schedulePayer = PAYER; String failedTxn = "deleted"; - return defaultHapiSpec("ScheduledSubmitThatWouldFailWithTopicDeletedCannotBeSigned") - .given( - newKeyNamed(adminKey), - createTopic(mutableTopic).adminKeyName(adminKey), - cryptoCreate(schedulePayer), - scheduleCreate( - postDeleteSchedule, - submitMessageTo(mutableTopic).message(RANDOM_MSG)) - .designatingPayer(schedulePayer) - .payingWith(DEFAULT_PAYER) - .via(failedTxn)) - .when(deleteTopic(mutableTopic)) - .then(scheduleSign(postDeleteSchedule) + return hapiTest( + newKeyNamed(adminKey), + createTopic(mutableTopic).adminKeyName(adminKey), + cryptoCreate(schedulePayer), + scheduleCreate(postDeleteSchedule, submitMessageTo(mutableTopic).message(RANDOM_MSG)) + .designatingPayer(schedulePayer) + .payingWith(DEFAULT_PAYER) + .via(failedTxn), + deleteTopic(mutableTopic), + scheduleSign(postDeleteSchedule) .alsoSigningWith(schedulePayer) .hasKnownStatus(UNRESOLVABLE_REQUIRED_SIGNERS)); } @@ -1273,82 +1115,76 @@ final Stream executionTriggersOnceTopicHasSatisfiedSubmitKey() { String mutableTopic = "XXX"; String schedule = "deferredSubmitMsg"; - return defaultHapiSpec("ExecutionTriggersOnceTopicHasNoSubmitKey") - .given( - newKeyNamed(adminKey), - newKeyNamed(submitKey), - createTopic(mutableTopic).adminKeyName(adminKey).submitKeyName(submitKey), - cryptoCreate(PAYER), - scheduleCreate(schedule, submitMessageTo(mutableTopic).message(RANDOM_MSG)) - .designatingPayer(PAYER) - .payingWith(DEFAULT_PAYER) - .alsoSigningWith(PAYER) - .via(CREATION), - getTopicInfo(mutableTopic).hasSeqNo(0L)) - .when( - scheduleSign(schedule) - .alsoSigningWith(adminKey) - /* In the rare, but possible, case that the adminKey and submitKey keys overlap - * in their first byte (and that byte is not shared by the DEFAULT_PAYER), - * we will get SOME_SIGNATURES_WERE_INVALID instead of NO_NEW_VALID_SIGNATURES. - * - * So we need this to stabilize CI. But if just testing locally, you may - * only use .hasKnownStatus(NO_NEW_VALID_SIGNATURES) and it will pass - * >99.99% of the time. */ - .hasKnownStatusFrom(NO_NEW_VALID_SIGNATURES, SOME_SIGNATURES_WERE_INVALID), - updateTopic(mutableTopic).submitKey(PAYER), - scheduleSign(schedule)) - .then( - getScheduleInfo(schedule).isExecuted(), - getTopicInfo(mutableTopic).hasSeqNo(1L)); + return hapiTest( + newKeyNamed(adminKey), + newKeyNamed(submitKey), + createTopic(mutableTopic).adminKeyName(adminKey).submitKeyName(submitKey), + cryptoCreate(PAYER), + scheduleCreate(schedule, submitMessageTo(mutableTopic).message(RANDOM_MSG)) + .designatingPayer(PAYER) + .payingWith(DEFAULT_PAYER) + .alsoSigningWith(PAYER) + .via(CREATION), + getTopicInfo(mutableTopic).hasSeqNo(0L), + scheduleSign(schedule) + .alsoSigningWith(adminKey) + /* In the rare, but possible, case that the adminKey and submitKey keys overlap + * in their first byte (and that byte is not shared by the DEFAULT_PAYER), + * we will get SOME_SIGNATURES_WERE_INVALID instead of NO_NEW_VALID_SIGNATURES. + * + * So we need this to stabilize CI. But if just testing locally, you may + * only use .hasKnownStatus(NO_NEW_VALID_SIGNATURES) and it will pass + * >99.99% of the time. */ + .hasKnownStatusFrom(NO_NEW_VALID_SIGNATURES, SOME_SIGNATURES_WERE_INVALID), + updateTopic(mutableTopic).submitKey(PAYER), + scheduleSign(schedule), + getScheduleInfo(schedule).isExecuted(), + getTopicInfo(mutableTopic).hasSeqNo(1L)); } @HapiTest final Stream executionTriggersWithWeirdlyRepeatedKey() { String schedule = "dupKeyXfer"; - return defaultHapiSpec("ExecutionTriggersWithWeirdlyRepeatedKey") - .given( - cryptoCreate(WEIRDLY_POPULAR_KEY), - cryptoCreate(SENDER_1).key(WEIRDLY_POPULAR_KEY).balance(1L), - cryptoCreate(SENDER_2).key(WEIRDLY_POPULAR_KEY).balance(1L), - cryptoCreate(SENDER_3).key(WEIRDLY_POPULAR_KEY).balance(1L), - cryptoCreate(RECEIVER).balance(0L), - scheduleCreate( - schedule, - cryptoTransfer( - tinyBarsFromTo(SENDER_1, RECEIVER, 1L), - tinyBarsFromTo(SENDER_2, RECEIVER, 1L), - tinyBarsFromTo(SENDER_3, RECEIVER, 1L))) - .payingWith(DEFAULT_PAYER) - .via(CREATION)) - .when(scheduleSign(schedule).alsoSigningWith(WEIRDLY_POPULAR_KEY)) - .then( - getScheduleInfo(schedule).isExecuted(), - getAccountBalance(SENDER_1).hasTinyBars(0L), - getAccountBalance(SENDER_2).hasTinyBars(0L), - getAccountBalance(SENDER_3).hasTinyBars(0L), - getAccountBalance(RECEIVER).hasTinyBars(3L), - scheduleSign(schedule) - .via("repeatedSigning") - .alsoSigningWith(WEIRDLY_POPULAR_KEY) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getTxnRecord("repeatedSigning").logged()); + return hapiTest( + cryptoCreate(WEIRDLY_POPULAR_KEY), + cryptoCreate(SENDER_1).key(WEIRDLY_POPULAR_KEY).balance(1L), + cryptoCreate(SENDER_2).key(WEIRDLY_POPULAR_KEY).balance(1L), + cryptoCreate(SENDER_3).key(WEIRDLY_POPULAR_KEY).balance(1L), + cryptoCreate(RECEIVER).balance(0L), + scheduleCreate( + schedule, + cryptoTransfer( + tinyBarsFromTo(SENDER_1, RECEIVER, 1L), + tinyBarsFromTo(SENDER_2, RECEIVER, 1L), + tinyBarsFromTo(SENDER_3, RECEIVER, 1L))) + .payingWith(DEFAULT_PAYER) + .via(CREATION), + scheduleSign(schedule).alsoSigningWith(WEIRDLY_POPULAR_KEY), + getScheduleInfo(schedule).isExecuted(), + getAccountBalance(SENDER_1).hasTinyBars(0L), + getAccountBalance(SENDER_2).hasTinyBars(0L), + getAccountBalance(SENDER_3).hasTinyBars(0L), + getAccountBalance(RECEIVER).hasTinyBars(3L), + scheduleSign(schedule) + .via("repeatedSigning") + .alsoSigningWith(WEIRDLY_POPULAR_KEY) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getTxnRecord("repeatedSigning").logged()); } @HapiTest final Stream executionWithDefaultPayerWorks() { long transferAmount = 1; - return defaultHapiSpec("ExecutionWithDefaultPayerWorks") - .given( - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - cryptoCreate(PAYING_ACCOUNT), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .payingWith(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when(scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).via(SIGN_TXN)) - .then(withOpContext((spec, opLog) -> { + return hapiTest( + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + cryptoCreate(PAYING_ACCOUNT), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .payingWith(PAYING_ACCOUNT) + .via(CREATE_TXN), + scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).via(SIGN_TXN), + withOpContext((spec, opLog) -> { var createTx = getTxnRecord(CREATE_TXN); var signTx = getTxnRecord(SIGN_TXN).logged(); var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); @@ -1398,73 +1234,66 @@ final Stream executionWithDefaultPayerButNoFundsFails() { long balance = 10_000_000L; long noBalance = 0L; long transferAmount = 1L; - return defaultHapiSpec("ExecutionWithDefaultPayerButNoFundsFails") - .given( - cryptoCreate(PAYING_ACCOUNT).balance(balance), - cryptoCreate(LUCKY_RECEIVER), - cryptoCreate(SENDER).balance(transferAmount), - cryptoCreate(RECEIVER).balance(noBalance), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .payingWith(PAYING_ACCOUNT) - .via(CREATE_TXN), - recordFeeAmount(CREATE_TXN, SCHEDULE_CREATE_FEE)) - .when( - cryptoTransfer(tinyBarsFromTo(PAYING_ACCOUNT, LUCKY_RECEIVER, (spec -> { - long scheduleCreateFee = spec.registry().getAmount(SCHEDULE_CREATE_FEE); - return balance - scheduleCreateFee; - }))), - getAccountBalance(PAYING_ACCOUNT).hasTinyBars(noBalance), - scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).hasKnownStatus(SUCCESS)) - .then( - getAccountBalance(SENDER).hasTinyBars(transferAmount), - getAccountBalance(RECEIVER).hasTinyBars(noBalance), - withOpContext((spec, opLog) -> { - var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); - - allRunFor(spec, triggeredTx); - - Assertions.assertEquals( - INSUFFICIENT_PAYER_BALANCE, - triggeredTx.getResponseRecord().getReceipt().getStatus(), - SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); - })); + + return hapiTest( + cryptoCreate(PAYING_ACCOUNT).balance(balance), + cryptoCreate(LUCKY_RECEIVER), + cryptoCreate(SENDER).balance(transferAmount), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .payingWith(PAYING_ACCOUNT) + .via(CREATE_TXN), + recordFeeAmount(CREATE_TXN, SCHEDULE_CREATE_FEE), + cryptoTransfer(tinyBarsFromTo(PAYING_ACCOUNT, LUCKY_RECEIVER, (spec -> { + long scheduleCreateFee = spec.registry().getAmount(SCHEDULE_CREATE_FEE); + return balance - scheduleCreateFee; + }))), + getAccountBalance(PAYING_ACCOUNT).hasTinyBars(noBalance), + scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).hasKnownStatus(SUCCESS), + getAccountBalance(SENDER).hasTinyBars(transferAmount), + getAccountBalance(RECEIVER).hasTinyBars(noBalance), + withOpContext((spec, opLog) -> { + var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); + + allRunFor(spec, triggeredTx); + + Assertions.assertEquals( + INSUFFICIENT_PAYER_BALANCE, + triggeredTx.getResponseRecord().getReceipt().getStatus(), + SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); + })); } @HapiTest final Stream executionWithCustomPayerWorksWithLastSigBeingCustomPayer() { long noBalance = 0L; long transferAmount = 1; - return defaultHapiSpec("ExecutionWithCustomPayerWorksWithLastSigBeingCustomPayer") - .given( - cryptoCreate(PAYING_ACCOUNT), - cryptoCreate(SENDER).balance(transferAmount), - cryptoCreate(RECEIVER).balance(noBalance), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .designatingPayer(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when(scheduleSign(BASIC_XFER) - .alsoSigningWith(SENDER) + return hapiTest( + cryptoCreate(PAYING_ACCOUNT), + cryptoCreate(SENDER).balance(transferAmount), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .designatingPayer(PAYING_ACCOUNT) + .via(CREATE_TXN), + scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).via(SIGN_TXN).hasKnownStatus(SUCCESS), + getAccountBalance(SENDER).hasTinyBars(transferAmount), + getAccountBalance(RECEIVER).hasTinyBars(noBalance), + scheduleSign(BASIC_XFER) + .alsoSigningWith(PAYING_ACCOUNT) .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then( - getAccountBalance(SENDER).hasTinyBars(transferAmount), - getAccountBalance(RECEIVER).hasTinyBars(noBalance), - scheduleSign(BASIC_XFER) - .alsoSigningWith(PAYING_ACCOUNT) - .via(SIGN_TXN) - .hasKnownStatus(SUCCESS), - withOpContext((spec, opLog) -> { - var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); - - allRunFor(spec, triggeredTx); - - Assertions.assertEquals( - SUCCESS, - triggeredTx.getResponseRecord().getReceipt().getStatus(), - SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); - }), - getAccountBalance(SENDER).hasTinyBars(noBalance), - getAccountBalance(RECEIVER).hasTinyBars(transferAmount)); + .hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { + var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); + + allRunFor(spec, triggeredTx); + + Assertions.assertEquals( + SUCCESS, + triggeredTx.getResponseRecord().getReceipt().getStatus(), + SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); + }), + getAccountBalance(SENDER).hasTinyBars(noBalance), + getAccountBalance(RECEIVER).hasTinyBars(transferAmount)); } @HapiTest @@ -1472,31 +1301,29 @@ final Stream executionWithCustomPayerButNoFundsFails() { long balance = 0L; long noBalance = 0L; long transferAmount = 1; - return defaultHapiSpec("ExecutionWithCustomPayerButNoFundsFails") - .given( - cryptoCreate(PAYING_ACCOUNT).balance(balance), - cryptoCreate(SENDER).balance(transferAmount), - cryptoCreate(RECEIVER).balance(noBalance), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .designatingPayer(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when(scheduleSign(BASIC_XFER) + return hapiTest( + cryptoCreate(PAYING_ACCOUNT).balance(balance), + cryptoCreate(SENDER).balance(transferAmount), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .designatingPayer(PAYING_ACCOUNT) + .via(CREATE_TXN), + scheduleSign(BASIC_XFER) .alsoSigningWith(SENDER, PAYING_ACCOUNT) .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then( - getAccountBalance(SENDER).hasTinyBars(transferAmount), - getAccountBalance(RECEIVER).hasTinyBars(noBalance), - withOpContext((spec, opLog) -> { - var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); - - allRunFor(spec, triggeredTx); - - Assertions.assertEquals( - INSUFFICIENT_PAYER_BALANCE, - triggeredTx.getResponseRecord().getReceipt().getStatus(), - SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); - })); + .hasKnownStatus(SUCCESS), + getAccountBalance(SENDER).hasTinyBars(transferAmount), + getAccountBalance(RECEIVER).hasTinyBars(noBalance), + withOpContext((spec, opLog) -> { + var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); + + allRunFor(spec, triggeredTx); + + Assertions.assertEquals( + INSUFFICIENT_PAYER_BALANCE, + triggeredTx.getResponseRecord().getReceipt().getStatus(), + SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); + })); } @HapiTest @@ -1504,25 +1331,21 @@ final Stream executionWithDefaultPayerButAccountDeletedFails() { long balance = 10_000_000L; long noBalance = 0L; long transferAmount = 1L; - return defaultHapiSpec("ExecutionWithDefaultPayerButAccountDeletedFails") - .given( - cryptoCreate(PAYING_ACCOUNT).balance(balance), - cryptoCreate(LUCKY_RECEIVER), - cryptoCreate(SENDER).balance(transferAmount), - cryptoCreate(RECEIVER).balance(noBalance), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .payingWith(PAYING_ACCOUNT) - .via(CREATE_TXN), - recordFeeAmount(CREATE_TXN, SCHEDULE_CREATE_FEE)) - .when( - cryptoDelete(PAYING_ACCOUNT), - scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).hasKnownStatus(SUCCESS)) - .then( - getScheduleInfo(BASIC_XFER).isExecuted(), - getTxnRecord(CREATE_TXN) - .scheduled() - .hasPriority( - recordWith().statusFrom(INSUFFICIENT_PAYER_BALANCE, PAYER_ACCOUNT_DELETED))); + return hapiTest( + cryptoCreate(PAYING_ACCOUNT).balance(balance), + cryptoCreate(LUCKY_RECEIVER), + cryptoCreate(SENDER).balance(transferAmount), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .payingWith(PAYING_ACCOUNT) + .via(CREATE_TXN), + recordFeeAmount(CREATE_TXN, SCHEDULE_CREATE_FEE), + cryptoDelete(PAYING_ACCOUNT), + scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).hasKnownStatus(SUCCESS), + getScheduleInfo(BASIC_XFER).isExecuted(), + getTxnRecord(CREATE_TXN) + .scheduled() + .hasPriority(recordWith().statusFrom(INSUFFICIENT_PAYER_BALANCE, PAYER_ACCOUNT_DELETED))); } @HapiTest @@ -1530,38 +1353,30 @@ final Stream executionWithCustomPayerButAccountDeletedFails() { long balance = 10_000_000L; long noBalance = 0L; long transferAmount = 1; - return defaultHapiSpec("ExecutionWithCustomPayerButAccountDeletedFails") - .given( - cryptoCreate(PAYING_ACCOUNT).balance(balance), - cryptoCreate(SENDER).balance(transferAmount), - cryptoCreate(RECEIVER).balance(noBalance), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .designatingPayer(PAYING_ACCOUNT) - .alsoSigningWith(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when( - cryptoDelete(PAYING_ACCOUNT), - scheduleSign(BASIC_XFER) - .alsoSigningWith(SENDER) - .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then( - getAccountBalance(SENDER).hasTinyBars(transferAmount), - getAccountBalance(RECEIVER).hasTinyBars(noBalance), - getScheduleInfo(BASIC_XFER).isExecuted(), - withOpContext((spec, opLog) -> { - var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); - - allRunFor(spec, triggeredTx); - - final var failureReasons = EnumSet.of(INSUFFICIENT_PAYER_BALANCE, PAYER_ACCOUNT_DELETED); - Assertions.assertTrue( - failureReasons.contains(triggeredTx - .getResponseRecord() - .getReceipt() - .getStatus()), - SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED + " for one of reasons " + failureReasons); - })); + return hapiTest( + cryptoCreate(PAYING_ACCOUNT).balance(balance), + cryptoCreate(SENDER).balance(transferAmount), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .designatingPayer(PAYING_ACCOUNT) + .alsoSigningWith(PAYING_ACCOUNT) + .via(CREATE_TXN), + cryptoDelete(PAYING_ACCOUNT), + scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).via(SIGN_TXN).hasKnownStatus(SUCCESS), + getAccountBalance(SENDER).hasTinyBars(transferAmount), + getAccountBalance(RECEIVER).hasTinyBars(noBalance), + getScheduleInfo(BASIC_XFER).isExecuted(), + withOpContext((spec, opLog) -> { + var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); + + allRunFor(spec, triggeredTx); + + final var failureReasons = EnumSet.of(INSUFFICIENT_PAYER_BALANCE, PAYER_ACCOUNT_DELETED); + Assertions.assertTrue( + failureReasons.contains( + triggeredTx.getResponseRecord().getReceipt().getStatus()), + SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED + " for one of reasons " + failureReasons); + })); } @HapiTest @@ -1570,31 +1385,29 @@ final Stream executionWithCryptoInsufficientAccountBalanceFails() { long senderBalance = 100L; long transferAmount = 101L; long payerBalance = 1_000_000L; - return defaultHapiSpec("ExecutionWithCryptoInsufficientAccountBalanceFails") - .given( - cryptoCreate(PAYING_ACCOUNT).balance(payerBalance), - cryptoCreate(SENDER).balance(senderBalance), - cryptoCreate(RECEIVER).balance(noBalance), - scheduleCreate(FAILED_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .designatingPayer(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when(scheduleSign(FAILED_XFER) + return hapiTest( + cryptoCreate(PAYING_ACCOUNT).balance(payerBalance), + cryptoCreate(SENDER).balance(senderBalance), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate(FAILED_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .designatingPayer(PAYING_ACCOUNT) + .via(CREATE_TXN), + scheduleSign(FAILED_XFER) .alsoSigningWith(SENDER, PAYING_ACCOUNT) .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then( - getAccountBalance(SENDER).hasTinyBars(senderBalance), - getAccountBalance(RECEIVER).hasTinyBars(noBalance), - withOpContext((spec, opLog) -> { - var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); - - allRunFor(spec, triggeredTx); - - Assertions.assertEquals( - INSUFFICIENT_ACCOUNT_BALANCE, - triggeredTx.getResponseRecord().getReceipt().getStatus(), - SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); - })); + .hasKnownStatus(SUCCESS), + getAccountBalance(SENDER).hasTinyBars(senderBalance), + getAccountBalance(RECEIVER).hasTinyBars(noBalance), + withOpContext((spec, opLog) -> { + var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); + + allRunFor(spec, triggeredTx); + + Assertions.assertEquals( + INSUFFICIENT_ACCOUNT_BALANCE, + triggeredTx.getResponseRecord().getReceipt().getStatus(), + SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); + })); } @HapiTest @@ -1603,33 +1416,30 @@ final Stream executionWithCryptoSenderDeletedFails() { long senderBalance = 100L; long transferAmount = 101L; long payerBalance = 1_000_000L; - return defaultHapiSpec("ExecutionWithCryptoSenderDeletedFails") - .given( - cryptoCreate(PAYING_ACCOUNT).balance(payerBalance), - cryptoCreate(SENDER).balance(senderBalance), - cryptoCreate(RECEIVER).balance(noBalance), - scheduleCreate(FAILED_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .designatingPayer(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when( - cryptoDelete(SENDER), - scheduleSign(FAILED_XFER) - .alsoSigningWith(SENDER, PAYING_ACCOUNT) - .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then( - getAccountBalance(RECEIVER).hasTinyBars(noBalance), - getScheduleInfo(FAILED_XFER).isExecuted(), - withOpContext((spec, opLog) -> { - var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); - - allRunFor(spec, triggeredTx); - - Assertions.assertEquals( - ACCOUNT_DELETED, - triggeredTx.getResponseRecord().getReceipt().getStatus(), - SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); - })); + return hapiTest( + cryptoCreate(PAYING_ACCOUNT).balance(payerBalance), + cryptoCreate(SENDER).balance(senderBalance), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate(FAILED_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .designatingPayer(PAYING_ACCOUNT) + .via(CREATE_TXN), + cryptoDelete(SENDER), + scheduleSign(FAILED_XFER) + .alsoSigningWith(SENDER, PAYING_ACCOUNT) + .via(SIGN_TXN) + .hasKnownStatus(SUCCESS), + getAccountBalance(RECEIVER).hasTinyBars(noBalance), + getScheduleInfo(FAILED_XFER).isExecuted(), + withOpContext((spec, opLog) -> { + var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); + + allRunFor(spec, triggeredTx); + + Assertions.assertEquals( + ACCOUNT_DELETED, + triggeredTx.getResponseRecord().getReceipt().getStatus(), + SCHEDULED_TRANSACTION_MUST_NOT_SUCCEED); + })); } @HapiTest @@ -1640,29 +1450,22 @@ final Stream executionWithTokenInsufficientAccountBalanceFails() { String xTreasury = "xt"; String civilian = "xa"; String failedTxn = "bad"; - return defaultHapiSpec("ExecutionWithTokenInsufficientAccountBalanceFails") - .given( - newKeyNamed(ADMIN), - cryptoCreate(schedulePayer), - cryptoCreate(xTreasury), - cryptoCreate(civilian), - tokenCreate(xToken) - .treasury(xTreasury) - .initialSupply(100) - .adminKey(ADMIN), - tokenAssociate(civilian, xToken)) - .when(scheduleCreate( + return hapiTest( + newKeyNamed(ADMIN), + cryptoCreate(schedulePayer), + cryptoCreate(xTreasury), + cryptoCreate(civilian), + tokenCreate(xToken).treasury(xTreasury).initialSupply(100).adminKey(ADMIN), + tokenAssociate(civilian, xToken), + scheduleCreate( invalidSchedule, cryptoTransfer(moving(101, xToken).between(xTreasury, civilian)) .memo(randomUppercase(100))) .via(failedTxn) .alsoSigningWith(xTreasury, schedulePayer) - .designatingPayer(schedulePayer)) - .then( - getTxnRecord(failedTxn) - .scheduled() - .hasPriority(recordWith().status(INSUFFICIENT_TOKEN_BALANCE)), - getAccountBalance(xTreasury).hasTokenBalance(xToken, 100)); + .designatingPayer(schedulePayer), + getTxnRecord(failedTxn).scheduled().hasPriority(recordWith().status(INSUFFICIENT_TOKEN_BALANCE)), + getAccountBalance(xTreasury).hasTokenBalance(xToken, 100)); } @HapiTest @@ -1671,39 +1474,34 @@ final Stream executionWithInvalidAccountAmountsFails() { long senderBalance = 1000L; long payingAccountBalance = 1_000_000L; long noBalance = 0L; - return defaultHapiSpec("ExecutionWithInvalidAccountAmountsFails") - .given( - cryptoCreate(PAYING_ACCOUNT).balance(payingAccountBalance), - cryptoCreate(SENDER).balance(senderBalance), - cryptoCreate(RECEIVER).balance(noBalance)) - .when() - .then( - scheduleCreate( - FAILED_XFER, - cryptoTransfer( - tinyBarsFromToWithInvalidAmounts(SENDER, RECEIVER, transferAmount))) - .designatingPayer(PAYING_ACCOUNT) - .hasKnownStatus(INVALID_ACCOUNT_AMOUNTS), - getAccountBalance(SENDER).hasTinyBars(senderBalance), - getAccountBalance(RECEIVER).hasTinyBars(noBalance)); + return hapiTest( + cryptoCreate(PAYING_ACCOUNT).balance(payingAccountBalance), + cryptoCreate(SENDER).balance(senderBalance), + cryptoCreate(RECEIVER).balance(noBalance), + scheduleCreate( + FAILED_XFER, + cryptoTransfer(tinyBarsFromToWithInvalidAmounts(SENDER, RECEIVER, transferAmount))) + .designatingPayer(PAYING_ACCOUNT) + .hasKnownStatus(INVALID_ACCOUNT_AMOUNTS), + getAccountBalance(SENDER).hasTinyBars(senderBalance), + getAccountBalance(RECEIVER).hasTinyBars(noBalance)); } @HapiTest final Stream executionWithCustomPayerWorks() { long transferAmount = 1; - return defaultHapiSpec("ExecutionWithCustomPayerWorks") - .given( - cryptoCreate(PAYING_ACCOUNT), - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .designatingPayer(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when(scheduleSign(BASIC_XFER) + return hapiTest( + cryptoCreate(PAYING_ACCOUNT), + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .designatingPayer(PAYING_ACCOUNT) + .via(CREATE_TXN), + scheduleSign(BASIC_XFER) .alsoSigningWith(SENDER, PAYING_ACCOUNT) .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then(withOpContext((spec, opLog) -> { + .hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { var createTx = getTxnRecord(CREATE_TXN); var signTx = getTxnRecord(SIGN_TXN); var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); @@ -1756,21 +1554,20 @@ final Stream executionWithCustomPayerWorks() { @HapiTest final Stream executionWithCustomPayerAndAdminKeyWorks() { long transferAmount = 1; - return defaultHapiSpec("ExecutionWithCustomPayerAndAdminKeyWorks") - .given( - newKeyNamed("adminKey"), - cryptoCreate(PAYING_ACCOUNT), - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .adminKey("adminKey") - .designatingPayer(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when(scheduleSign(BASIC_XFER) + return hapiTest( + newKeyNamed("adminKey"), + cryptoCreate(PAYING_ACCOUNT), + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .adminKey("adminKey") + .designatingPayer(PAYING_ACCOUNT) + .via(CREATE_TXN), + scheduleSign(BASIC_XFER) .alsoSigningWith(SENDER, PAYING_ACCOUNT) .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then(withOpContext((spec, opLog) -> { + .hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { var createTx = getTxnRecord(CREATE_TXN); var signTx = getTxnRecord(SIGN_TXN); var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); @@ -1823,20 +1620,16 @@ final Stream executionWithCustomPayerAndAdminKeyWorks() { @HapiTest final Stream executionWithCustomPayerWhoSignsAtCreationAsPayerWorks() { long transferAmount = 1; - return defaultHapiSpec("ExecutionWithCustomPayerWhoSignsAtCreationAsPayerWorks") - .given( - cryptoCreate(PAYING_ACCOUNT), - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) - .payingWith(PAYING_ACCOUNT) - .designatingPayer(PAYING_ACCOUNT) - .via(CREATE_TXN)) - .when(scheduleSign(BASIC_XFER) - .alsoSigningWith(SENDER) - .via(SIGN_TXN) - .hasKnownStatus(SUCCESS)) - .then(withOpContext((spec, opLog) -> { + return hapiTest( + cryptoCreate(PAYING_ACCOUNT), + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + scheduleCreate(BASIC_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, transferAmount))) + .payingWith(PAYING_ACCOUNT) + .designatingPayer(PAYING_ACCOUNT) + .via(CREATE_TXN), + scheduleSign(BASIC_XFER).alsoSigningWith(SENDER).via(SIGN_TXN).hasKnownStatus(SUCCESS), + withOpContext((spec, opLog) -> { var createTx = getTxnRecord(CREATE_TXN); var signTx = getTxnRecord(SIGN_TXN); var triggeredTx = getTxnRecord(CREATE_TXN).scheduled(); @@ -1886,11 +1679,6 @@ final Stream executionWithCustomPayerWhoSignsAtCreationAsPayerWorks })); } - private T getTestConfig(Class configClass) { - final TestConfigBuilder builder = new TestConfigBuilder(configClass); - return builder.getOrCreateConfig().getConfigData(configClass); - } - private ByteString metadataOfLength(int length) { return ByteString.copyFrom(genRandomBytes(length)); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleRecordTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleRecordTest.java index c4428cf5ca04..e49dd62a332a 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleRecordTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleRecordTest.java @@ -16,7 +16,7 @@ package com.hedera.services.bdd.suites.schedule; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; +import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; import static com.hedera.services.bdd.spec.assertions.TransferListAsserts.exactParticipants; import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountBalance; @@ -69,11 +69,12 @@ import org.junit.jupiter.api.DynamicTest; public class ScheduleRecordTest { + @HapiTest final Stream noFeesChargedIfTriggeredPayerIsUnwilling() { - return defaultHapiSpec("NoFeesChargedIfTriggeredPayerIsUnwilling") - .given(cryptoCreate(UNWILLING_PAYER)) - .when(scheduleCreate( + return hapiTest( + cryptoCreate(UNWILLING_PAYER), + scheduleCreate( SCHEDULE, cryptoTransfer(tinyBarsFromTo(GENESIS, FUNDING, 1)) .fee(1L)) @@ -82,8 +83,8 @@ final Stream noFeesChargedIfTriggeredPayerIsUnwilling() { // prevent multiple runs of this test causing duplicates .withEntityMemo("" + new SecureRandom().nextLong()) .designatingPayer(UNWILLING_PAYER) - .savingExpectedScheduledTxnId()) - .then(getTxnRecord(SIMPLE_XFER_SCHEDULE) + .savingExpectedScheduledTxnId(), + getTxnRecord(SIMPLE_XFER_SCHEDULE) .scheduledBy(SCHEDULE) .hasPriority(recordWith() .transfers(exactParticipants(ignore -> Collections.emptyList())) @@ -92,16 +93,16 @@ final Stream noFeesChargedIfTriggeredPayerIsUnwilling() { @HapiTest final Stream noFeesChargedIfTriggeredPayerIsInsolvent() { - return defaultHapiSpec("NoFeesChargedIfTriggeredPayerIsInsolvent") - .given(cryptoCreate(INSOLVENT_PAYER).balance(0L)) - .when(scheduleCreate(SCHEDULE, cryptoTransfer(tinyBarsFromTo(GENESIS, FUNDING, 1))) + return hapiTest( + cryptoCreate(INSOLVENT_PAYER).balance(0L), + scheduleCreate(SCHEDULE, cryptoTransfer(tinyBarsFromTo(GENESIS, FUNDING, 1))) .alsoSigningWith(GENESIS, INSOLVENT_PAYER) .via(SIMPLE_XFER_SCHEDULE) // prevent multiple runs of this test causing duplicates .withEntityMemo("" + new SecureRandom().nextLong()) .designatingPayer(INSOLVENT_PAYER) - .savingExpectedScheduledTxnId()) - .then(getTxnRecord(SIMPLE_XFER_SCHEDULE) + .savingExpectedScheduledTxnId(), + getTxnRecord(SIMPLE_XFER_SCHEDULE) .scheduledBy(SCHEDULE) .hasPriority(recordWith() .transfers(exactParticipants(ignore -> Collections.emptyList())) @@ -114,129 +115,118 @@ final Stream canScheduleChunkedMessages() { AtomicReference initialTxnId = new AtomicReference<>(); // validation here is checking fees and staking, not message creation on the topic... - return defaultHapiSpec("CanScheduleChunkedMessages") - .given(cryptoCreate(PAYING_SENDER).balance(ONE_HUNDRED_HBARS), createTopic(ofGeneralInterest)) - .when( - withOpContext((spec, opLog) -> { - var subOp = usableTxnIdNamed(BEGIN).payerId(PAYING_SENDER); - allRunFor(spec, subOp); - initialTxnId.set(spec.registry().getTxnId(BEGIN)); - }), - sourcing(() -> scheduleCreate( - "firstChunk", - submitMessageTo(ofGeneralInterest) - .chunkInfo(3, 1, scheduledVersionOf(initialTxnId.get()))) - .txnId(BEGIN) - .logged() - .signedBy(PAYING_SENDER)), - getTxnRecord(BEGIN) - .hasPriority(recordWith() - .status(SUCCESS) - .transfers(exactParticipants(spec -> List.of( - spec.setup().defaultNode(), - spec.setup().fundingAccount(), - spec.setup().stakingRewardAccount(), - spec.setup().nodeRewardAccount(), - spec.registry().getAccountID(PAYING_SENDER))))) - .assertingOnlyPriority() - .logged(), - getTxnRecord(BEGIN) - .scheduled() - .hasPriority(recordWith() - .status(SUCCESS) - .transfers(exactParticipants(spec -> List.of( - spec.setup().fundingAccount(), - spec.setup().stakingRewardAccount(), - spec.setup().nodeRewardAccount(), - spec.registry().getAccountID(PAYING_SENDER))))) - .logged()) - .then( - scheduleCreate( - "secondChunk", - submitMessageTo(ofGeneralInterest).chunkInfo(3, 2, PAYING_SENDER)) - .via("end") - .logged() - .payingWith(PAYING_SENDER), - getTxnRecord("end") - .scheduled() - .hasPriority(recordWith() - .status(SUCCESS) - .transfers(exactParticipants(spec -> List.of( - spec.setup().fundingAccount(), - spec.setup().stakingRewardAccount(), - spec.setup().nodeRewardAccount(), - spec.registry().getAccountID(PAYING_SENDER))))) - .logged(), - getTopicInfo(ofGeneralInterest).logged().hasSeqNo(2L)); + return hapiTest( + cryptoCreate(PAYING_SENDER).balance(ONE_HUNDRED_HBARS), + createTopic(ofGeneralInterest), + withOpContext((spec, opLog) -> { + var subOp = usableTxnIdNamed(BEGIN).payerId(PAYING_SENDER); + allRunFor(spec, subOp); + initialTxnId.set(spec.registry().getTxnId(BEGIN)); + }), + sourcing(() -> scheduleCreate( + "firstChunk", + submitMessageTo(ofGeneralInterest) + .chunkInfo(3, 1, scheduledVersionOf(initialTxnId.get()))) + .txnId(BEGIN) + .logged() + .signedBy(PAYING_SENDER)), + getTxnRecord(BEGIN) + .hasPriority(recordWith() + .status(SUCCESS) + .transfers(exactParticipants(spec -> List.of( + spec.setup().defaultNode(), + spec.setup().fundingAccount(), + spec.setup().stakingRewardAccount(), + spec.setup().nodeRewardAccount(), + spec.registry().getAccountID(PAYING_SENDER))))) + .assertingOnlyPriority() + .logged(), + getTxnRecord(BEGIN) + .scheduled() + .hasPriority(recordWith() + .status(SUCCESS) + .transfers(exactParticipants(spec -> List.of( + spec.setup().fundingAccount(), + spec.setup().stakingRewardAccount(), + spec.setup().nodeRewardAccount(), + spec.registry().getAccountID(PAYING_SENDER))))) + .logged(), + scheduleCreate("secondChunk", submitMessageTo(ofGeneralInterest).chunkInfo(3, 2, PAYING_SENDER)) + .via("end") + .logged() + .payingWith(PAYING_SENDER), + getTxnRecord("end") + .scheduled() + .hasPriority(recordWith() + .status(SUCCESS) + .transfers(exactParticipants(spec -> List.of( + spec.setup().fundingAccount(), + spec.setup().stakingRewardAccount(), + spec.setup().nodeRewardAccount(), + spec.registry().getAccountID(PAYING_SENDER))))) + .logged(), + getTopicInfo(ofGeneralInterest).logged().hasSeqNo(2L)); } @HapiTest final Stream schedulingTxnIdFieldsNotAllowed() { - return defaultHapiSpec("SchedulingTxnIdFieldsNotAllowed") - .given(usableTxnIdNamed("withScheduled").settingScheduledInappropriately()) - .when() - .then(cryptoCreate("nope").txnId("withScheduled").hasPrecheck(TRANSACTION_ID_FIELD_NOT_ALLOWED)); + return hapiTest( + usableTxnIdNamed("withScheduled").settingScheduledInappropriately(), + cryptoCreate("nope").txnId("withScheduled").hasPrecheck(TRANSACTION_ID_FIELD_NOT_ALLOWED)); } @HapiTest final Stream executionTimeIsAvailable() { - return defaultHapiSpec("ExecutionTimeIsAvailable") - .given( - cryptoCreate(PAYER), - cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L)) - .when( - scheduleCreate( - "tb", - cryptoTransfer(tinyBarsFromTo(PAYER, RECEIVER, 1)) - .fee(ONE_HBAR)) - .savingExpectedScheduledTxnId() - .payingWith(PAYER) - .via(CREATION), - scheduleSign("tb").via(TRIGGER).alsoSigningWith(RECEIVER)) - .then(getScheduleInfo("tb").logged().wasExecutedBy(TRIGGER)); + return hapiTest( + cryptoCreate(PAYER), + cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L), + scheduleCreate( + "tb", + cryptoTransfer(tinyBarsFromTo(PAYER, RECEIVER, 1)) + .fee(ONE_HBAR)) + .savingExpectedScheduledTxnId() + .payingWith(PAYER) + .via(CREATION), + scheduleSign("tb").via(TRIGGER).alsoSigningWith(RECEIVER), + getScheduleInfo("tb").logged().wasExecutedBy(TRIGGER)); } @HapiTest final Stream deletionTimeIsAvailable() { - return defaultHapiSpec("DeletionTimeIsAvailable") - .given( - newKeyNamed(ADMIN), - cryptoCreate(PAYER), - cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L)) - .when( - scheduleCreate( - "ntb", - cryptoTransfer(tinyBarsFromTo(PAYER, RECEIVER, 1)) - .fee(ONE_HBAR)) - .payingWith(PAYER) - .adminKey(ADMIN) - .via(CREATION), - scheduleDelete("ntb").via("deletion").signedBy(DEFAULT_PAYER, ADMIN)) - .then(getScheduleInfo("ntb").wasDeletedAtConsensusTimeOf("deletion")); + return hapiTest( + newKeyNamed(ADMIN), + cryptoCreate(PAYER), + cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L), + scheduleCreate( + "ntb", + cryptoTransfer(tinyBarsFromTo(PAYER, RECEIVER, 1)) + .fee(ONE_HBAR)) + .payingWith(PAYER) + .adminKey(ADMIN) + .via(CREATION), + scheduleDelete("ntb").via("deletion").signedBy(DEFAULT_PAYER, ADMIN), + getScheduleInfo("ntb").wasDeletedAtConsensusTimeOf("deletion")); } @HapiTest final Stream allRecordsAreQueryable() { - return defaultHapiSpec("AllRecordsAreQueryable") - .given( - cryptoCreate(PAYER), - cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L)) - .when( - scheduleCreate( - TWO_SIG_XFER, - cryptoTransfer(tinyBarsFromTo(PAYER, RECEIVER, 1)) - .fee(ONE_HBAR)) - .logged() - .savingExpectedScheduledTxnId() - .payingWith(PAYER) - .via(CREATION), - getAccountBalance(RECEIVER).hasTinyBars(0L)) - .then( - scheduleSign(TWO_SIG_XFER).via(TRIGGER).alsoSigningWith(RECEIVER), - getAccountBalance(RECEIVER).hasTinyBars(1L), - getTxnRecord(TRIGGER), - getTxnRecord(CREATION), - getTxnRecord(CREATION).scheduled(), - getTxnRecord(CREATION).scheduledBy(TWO_SIG_XFER)); + return hapiTest( + cryptoCreate(PAYER), + cryptoCreate(RECEIVER).receiverSigRequired(true).balance(0L), + scheduleCreate( + TWO_SIG_XFER, + cryptoTransfer(tinyBarsFromTo(PAYER, RECEIVER, 1)) + .fee(ONE_HBAR)) + .logged() + .savingExpectedScheduledTxnId() + .payingWith(PAYER) + .via(CREATION), + getAccountBalance(RECEIVER).hasTinyBars(0L), + scheduleSign(TWO_SIG_XFER).via(TRIGGER).alsoSigningWith(RECEIVER), + getAccountBalance(RECEIVER).hasTinyBars(1L), + getTxnRecord(TRIGGER), + getTxnRecord(CREATION), + getTxnRecord(CREATION).scheduled(), + getTxnRecord(CREATION).scheduledBy(TWO_SIG_XFER)); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleSignTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleSignTest.java index 30cc90734ad0..06cad7e1a260 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleSignTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/ScheduleSignTest.java @@ -17,7 +17,6 @@ package com.hedera.services.bdd.suites.schedule; import static com.hedera.services.bdd.junit.TestTags.NOT_REPEATABLE; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.assertions.AccountInfoAsserts.changeFromSnapshot; import static com.hedera.services.bdd.spec.keys.ControlForKey.forKey; @@ -83,16 +82,15 @@ import org.junit.jupiter.api.Tag; public class ScheduleSignTest { + @HapiTest final Stream idVariantsTreatedAsExpected() { - return defaultHapiSpec("idVariantsTreatedAsExpected") - .given( - newKeyNamed(ADMIN), - cryptoCreate(SENDER), - scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) - .adminKey(ADMIN)) - .when() - .then(submitModified(withSuccessivelyVariedBodyIds(), () -> scheduleSign(VALID_SCHEDULED_TXN) + return hapiTest( + newKeyNamed(ADMIN), + cryptoCreate(SENDER), + scheduleCreate(VALID_SCHEDULED_TXN, cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1))) + .adminKey(ADMIN), + submitModified(withSuccessivelyVariedBodyIds(), () -> scheduleSign(VALID_SCHEDULED_TXN) .alsoSigningWith(SENDER))); } @@ -128,33 +126,30 @@ final Stream changeInNestedSigningReqsRespected() { String schedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("ChangeInNestedSigningReqsRespected") - .given( - newKeyNamed(senderKey).shape(senderShape), - keyFromMutation(NEW_SENDER_KEY, senderKey).changing(this::bumpThirdNestedThresholdSigningReq), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .payingWith(DEFAULT_PAYER) - .alsoSigningWith(sender) - .sigControl(ControlForKey.forKey(senderKey, sigOne)), - getAccountBalance(receiver).hasTinyBars(0L)) - .when( - cryptoUpdate(sender).key(NEW_SENDER_KEY), - scheduleSign(schedule) - .alsoSigningWith(NEW_SENDER_KEY) - .sigControl(forKey(NEW_SENDER_KEY, firstSigThree)), - getAccountBalance(receiver).hasTinyBars(0L), - scheduleSign(schedule) - .alsoSigningWith(NEW_SENDER_KEY) - .sigControl(forKey(NEW_SENDER_KEY, secondSigThree)), - getAccountBalance(receiver).hasTinyBars(1L)) - .then( - scheduleSign(schedule) - .alsoSigningWith(NEW_SENDER_KEY) - .sigControl(forKey(NEW_SENDER_KEY, sigTwo)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1L)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + keyFromMutation(NEW_SENDER_KEY, senderKey).changing(this::bumpThirdNestedThresholdSigningReq), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .payingWith(DEFAULT_PAYER) + .alsoSigningWith(sender) + .sigControl(ControlForKey.forKey(senderKey, sigOne)), + getAccountBalance(receiver).hasTinyBars(0L), + cryptoUpdate(sender).key(NEW_SENDER_KEY), + scheduleSign(schedule) + .alsoSigningWith(NEW_SENDER_KEY) + .sigControl(forKey(NEW_SENDER_KEY, firstSigThree)), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule) + .alsoSigningWith(NEW_SENDER_KEY) + .sigControl(forKey(NEW_SENDER_KEY, secondSigThree)), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule) + .alsoSigningWith(NEW_SENDER_KEY) + .sigControl(forKey(NEW_SENDER_KEY, sigTwo)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1L)); } @HapiTest @@ -168,32 +163,29 @@ final Stream reductionInSigningReqsAllowsTxnToGoThrough() { String schedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("ReductionInSigningReqsAllowsTxnToGoThrough") - .given( - newKeyNamed(senderKey).shape(senderShape), - keyFromMutation(NEW_SENDER_KEY, senderKey).changing(this::lowerThirdNestedThresholdSigningReq), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .payingWith(DEFAULT_PAYER) - .alsoSigningWith(sender) - .sigControl(ControlForKey.forKey(senderKey, sigOne)), - getAccountBalance(receiver).hasTinyBars(0L)) - .when( - scheduleSign(schedule) - .alsoSigningWith(NEW_SENDER_KEY) - .sigControl(forKey(NEW_SENDER_KEY, firstSigThree)), - getAccountBalance(receiver).hasTinyBars(0L), - cryptoUpdate(sender).key(NEW_SENDER_KEY), - scheduleSign(schedule).via("signTxn"), - getTxnRecord("signTxn").hasScheduledTransactionId().logged(), - getAccountBalance(receiver).hasTinyBars(1L)) - .then( - scheduleSign(schedule) - .alsoSigningWith(NEW_SENDER_KEY) - .sigControl(forKey(NEW_SENDER_KEY, sigTwo)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1L)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + keyFromMutation(NEW_SENDER_KEY, senderKey).changing(this::lowerThirdNestedThresholdSigningReq), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .payingWith(DEFAULT_PAYER) + .alsoSigningWith(sender) + .sigControl(ControlForKey.forKey(senderKey, sigOne)), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule) + .alsoSigningWith(NEW_SENDER_KEY) + .sigControl(forKey(NEW_SENDER_KEY, firstSigThree)), + getAccountBalance(receiver).hasTinyBars(0L), + cryptoUpdate(sender).key(NEW_SENDER_KEY), + scheduleSign(schedule).via("signTxn"), + getTxnRecord("signTxn").hasScheduledTransactionId().logged(), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule) + .alsoSigningWith(NEW_SENDER_KEY) + .sigControl(forKey(NEW_SENDER_KEY, sigTwo)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1L)); } @HapiTest @@ -208,33 +200,28 @@ final Stream reductionInSigningReqsAllowsTxnToGoThroughWithRandomKe String senderKey = "sKey"; String newSenderKey = NEW_SENDER_KEY; - return defaultHapiSpec("ReductionInSigningReqsAllowsTxnToGoThroughWithRandomKey") - .given( - newKeyNamed(RANDOM_KEY), - cryptoCreate("random").key(RANDOM_KEY), - newKeyNamed(senderKey).shape(senderShape), - keyFromMutation(newSenderKey, senderKey).changing(this::lowerThirdNestedThresholdSigningReq), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .payingWith(DEFAULT_PAYER) - .alsoSigningWith(sender) - .sigControl(ControlForKey.forKey(senderKey, sigOne)), - getAccountBalance(receiver).hasTinyBars(0L)) - .when( - scheduleSign(schedule) - .alsoSigningWith(newSenderKey) - .sigControl(forKey(newSenderKey, firstSigThree)), - getAccountBalance(receiver).hasTinyBars(0L), - cryptoUpdate(sender).key(newSenderKey), - scheduleSign(schedule).signedBy(RANDOM_KEY).payingWith("random"), - getAccountBalance(receiver).hasTinyBars(1L)) - .then( - scheduleSign(schedule) - .alsoSigningWith(newSenderKey) - .sigControl(forKey(newSenderKey, sigTwo)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1L)); + return hapiTest( + newKeyNamed(RANDOM_KEY), + cryptoCreate("random").key(RANDOM_KEY), + newKeyNamed(senderKey).shape(senderShape), + keyFromMutation(newSenderKey, senderKey).changing(this::lowerThirdNestedThresholdSigningReq), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .payingWith(DEFAULT_PAYER) + .alsoSigningWith(sender) + .sigControl(ControlForKey.forKey(senderKey, sigOne)), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule).alsoSigningWith(newSenderKey).sigControl(forKey(newSenderKey, firstSigThree)), + getAccountBalance(receiver).hasTinyBars(0L), + cryptoUpdate(sender).key(newSenderKey), + scheduleSign(schedule).signedBy(RANDOM_KEY).payingWith("random"), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule) + .alsoSigningWith(newSenderKey) + .sigControl(forKey(newSenderKey, sigTwo)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1L)); } @HapiTest @@ -248,25 +235,22 @@ final Stream nestedSigningReqsWorkAsExpected() { String schedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("NestedSigningReqsWorkAsExpected") - .given( - newKeyNamed(senderKey).shape(senderShape), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .payingWith(DEFAULT_PAYER) - .alsoSigningWith(sender) - .sigControl(ControlForKey.forKey(senderKey, sigOne)), - getAccountBalance(receiver).hasTinyBars(0L)) - .when( - scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), - getAccountBalance(receiver).hasTinyBars(1L)) - .then( - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigThree)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1L)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .payingWith(DEFAULT_PAYER) + .alsoSigningWith(sender) + .sigControl(ControlForKey.forKey(senderKey, sigOne)), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigThree)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1L)); } @HapiTest @@ -280,29 +264,26 @@ final Stream receiverSigRequiredNotConfusedByOrder() { String schedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("ReceiverSigRequiredNotConfusedByOrder") - .given( - newKeyNamed(senderKey).shape(senderShape), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L).receiverSigRequired(true), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .payingWith(DEFAULT_PAYER), - scheduleSign(schedule).alsoSigningWith(receiver), - getAccountBalance(receiver).hasTinyBars(0L)) - .when( - scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigOne)), - getAccountBalance(receiver).hasTinyBars(1L)) - .then( - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigTwo)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1L), - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigThree)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1L)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L).receiverSigRequired(true), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .payingWith(DEFAULT_PAYER), + scheduleSign(schedule).alsoSigningWith(receiver), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigOne)), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigTwo)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigThree)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1L)); } @HapiTest @@ -316,29 +297,26 @@ final Stream receiverSigRequiredNotConfusedByMultiSigSender() { String schedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("receiverSigRequiredNotConfusedByMultiSigSender") - .given( - newKeyNamed(senderKey).shape(senderShape), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L).receiverSigRequired(true), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .payingWith(DEFAULT_PAYER), - getAccountBalance(receiver).hasTinyBars(0L)) - .when( - scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigOne)), - getAccountBalance(receiver).hasTinyBars(0L), - scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), - getAccountBalance(receiver).hasTinyBars(0L), - scheduleSign(schedule).alsoSigningWith(receiver), - getAccountBalance(receiver).hasTinyBars(1L)) - .then( - scheduleSign(schedule).alsoSigningWith(receiver).hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1), - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigThree)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1L)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L).receiverSigRequired(true), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .payingWith(DEFAULT_PAYER), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigOne)), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule).alsoSigningWith(receiver), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule).alsoSigningWith(receiver).hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigThree)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1L)); } @HapiTest @@ -352,33 +330,30 @@ final Stream receiverSigRequiredUpdateIsRecognized() { String schedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("ReceiverSigRequiredUpdateIsRecognized") - .given( - newKeyNamed(senderKey).shape(senderShape), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .payingWith(DEFAULT_PAYER) - .alsoSigningWith(sender) - .sigControl(forKey(senderKey, sigOne)), - getAccountBalance(receiver).hasTinyBars(0L)) - .when( - cryptoUpdate(receiver).receiverSigRequired(true), - scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), - getAccountBalance(receiver).hasTinyBars(0L)) - .then( - scheduleSign(schedule).alsoSigningWith(receiver), - getAccountBalance(receiver).hasTinyBars(1), - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigTwo)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1), - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigThree)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .payingWith(DEFAULT_PAYER) + .alsoSigningWith(sender) + .sigControl(forKey(senderKey, sigOne)), + getAccountBalance(receiver).hasTinyBars(0L), + cryptoUpdate(receiver).receiverSigRequired(true), + scheduleSign(schedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), + getAccountBalance(receiver).hasTinyBars(0L), + scheduleSign(schedule).alsoSigningWith(receiver), + getAccountBalance(receiver).hasTinyBars(1), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigTwo)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigThree)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1)); } @HapiTest @@ -392,28 +367,25 @@ final Stream scheduleAlreadyExecutedOnCreateDoesntRepeatTransaction String schedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("ScheduleAlreadyExecutedOnCreateDoesntRepeatTransaction") - .given( - newKeyNamed(senderKey).shape(senderShape), - cryptoCreate(sender).key(senderKey), - cryptoCreate(receiver).balance(0L), - scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .memo(randomUppercase(100)) - .payingWith(sender) - .sigControl(forKey(sender, sigOne)), - getAccountBalance(receiver).hasTinyBars(1L)) - .when( - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigTwo)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1)) - .then( - scheduleSign(schedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigThree)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(receiver).hasTinyBars(1)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + cryptoCreate(sender).key(senderKey), + cryptoCreate(receiver).balance(0L), + scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .memo(randomUppercase(100)) + .payingWith(sender) + .sigControl(forKey(sender, sigOne)), + getAccountBalance(receiver).hasTinyBars(1L), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigTwo)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1), + scheduleSign(schedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigThree)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(receiver).hasTinyBars(1)); } @HapiTest @@ -427,26 +399,23 @@ final Stream scheduleAlreadyExecutedDoesntRepeatTransaction() { String firstSchedule = "Z"; String senderKey = "sKey"; - return defaultHapiSpec("ScheduleAlreadyExecutedDoesntRepeatTransaction") - .given( - newKeyNamed(senderKey).shape(senderShape), - cryptoCreate(sender).balance(101L).key(senderKey), - cryptoCreate(receiver), - scheduleCreate(firstSchedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) - .memo(randomUppercase(100)) - .designatingPayer(DEFAULT_PAYER), - getAccountBalance(sender).hasTinyBars(101)) - .when( - scheduleSign(firstSchedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigOne)), - getAccountBalance(sender).hasTinyBars(101), - scheduleSign(firstSchedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), - getAccountBalance(sender).hasTinyBars(100)) - .then( - scheduleSign(firstSchedule) - .alsoSigningWith(senderKey) - .sigControl(forKey(senderKey, sigThree)) - .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), - getAccountBalance(sender).hasTinyBars(100)); + return hapiTest( + newKeyNamed(senderKey).shape(senderShape), + cryptoCreate(sender).balance(101L).key(senderKey), + cryptoCreate(receiver), + scheduleCreate(firstSchedule, cryptoTransfer(tinyBarsFromTo(sender, receiver, 1))) + .memo(randomUppercase(100)) + .designatingPayer(DEFAULT_PAYER), + getAccountBalance(sender).hasTinyBars(101), + scheduleSign(firstSchedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigOne)), + getAccountBalance(sender).hasTinyBars(101), + scheduleSign(firstSchedule).alsoSigningWith(senderKey).sigControl(forKey(senderKey, sigTwo)), + getAccountBalance(sender).hasTinyBars(100), + scheduleSign(firstSchedule) + .alsoSigningWith(senderKey) + .sigControl(forKey(senderKey, sigThree)) + .hasKnownStatus(SCHEDULE_ALREADY_EXECUTED), + getAccountBalance(sender).hasTinyBars(100)); } @HapiTest @@ -465,14 +434,12 @@ final Stream basicSignatureCollectionWorks() { final Stream signalsIrrelevantSig() { var txnBody = cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)); - return defaultHapiSpec("SignalsIrrelevantSig") - .given( - cryptoCreate(SENDER), - cryptoCreate(RECEIVER), - newKeyNamed("somebodyelse"), - scheduleCreate(BASIC_XFER, txnBody)) - .when() - .then(scheduleSign(BASIC_XFER) + return hapiTest( + cryptoCreate(SENDER), + cryptoCreate(RECEIVER), + newKeyNamed("somebodyelse"), + scheduleCreate(BASIC_XFER, txnBody), + scheduleSign(BASIC_XFER) .alsoSigningWith("somebodyelse") .hasKnownStatusFrom(NO_NEW_VALID_SIGNATURES, SOME_SIGNATURES_WERE_INVALID)); } @@ -481,15 +448,14 @@ final Stream signalsIrrelevantSig() { final Stream signalsIrrelevantSigEvenAfterLinkedEntityUpdate() { var txnBody = mintToken(TOKEN_A, 50000000L); - return defaultHapiSpec("SignalsIrrelevantSigEvenAfterLinkedEntityUpdate") - .given( - newKeyNamed(ADMIN), - newKeyNamed("mint"), - newKeyNamed("newMint"), - tokenCreate(TOKEN_A).adminKey(ADMIN).supplyKey("mint"), - scheduleCreate("tokenMintScheduled", txnBody)) - .when(tokenUpdate(TOKEN_A).supplyKey("newMint").signedByPayerAnd(ADMIN)) - .then(scheduleSign("tokenMintScheduled") + return hapiTest( + newKeyNamed(ADMIN), + newKeyNamed("mint"), + newKeyNamed("newMint"), + tokenCreate(TOKEN_A).adminKey(ADMIN).supplyKey("mint"), + scheduleCreate("tokenMintScheduled", txnBody), + tokenUpdate(TOKEN_A).supplyKey("newMint").signedByPayerAnd(ADMIN), + scheduleSign("tokenMintScheduled") .alsoSigningWith("mint") /* In the rare, but possible, case that the the mint and newMint keys overlap * in their first byte (and that byte is not shared by the DEFAULT_PAYER), @@ -503,10 +469,11 @@ final Stream signalsIrrelevantSigEvenAfterLinkedEntityUpdate() { @HapiTest final Stream addingSignaturesToNonExistingTxFails() { - return defaultHapiSpec("AddingSignaturesToNonExistingTxFails") - .given(cryptoCreate(SENDER), newKeyNamed(SOMEBODY)) - .when() - .then(scheduleSign("0.0.123321") + + return hapiTest( + cryptoCreate(SENDER), + newKeyNamed(SOMEBODY), + scheduleSign("0.0.123321") .fee(ONE_HBAR) .alsoSigningWith(SOMEBODY, SENDER) .hasPrecheckFrom(OK, INVALID_SCHEDULE_ID) @@ -515,38 +482,34 @@ final Stream addingSignaturesToNonExistingTxFails() { @HapiTest final Stream triggersUponFinishingPayerSig() { - return defaultHapiSpec("TriggersUponFinishingPayerSig") - .given( - cryptoCreate(PAYER).balance(ONE_HBAR), - cryptoCreate(SENDER).balance(1L), - cryptoCreate(RECEIVER).balance(0L).receiverSigRequired(true)) - .when( - scheduleCreate( - "threeSigXfer", - cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)) - .fee(ONE_HBAR)) - .designatingPayer(PAYER) - .alsoSigningWith(SENDER, RECEIVER), - getAccountBalance(RECEIVER).hasTinyBars(0L), - scheduleSign("threeSigXfer").alsoSigningWith(PAYER)) - .then(getAccountBalance(RECEIVER).hasTinyBars(1L)); + return hapiTest( + cryptoCreate(PAYER).balance(ONE_HBAR), + cryptoCreate(SENDER).balance(1L), + cryptoCreate(RECEIVER).balance(0L).receiverSigRequired(true), + scheduleCreate( + "threeSigXfer", + cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)) + .fee(ONE_HBAR)) + .designatingPayer(PAYER) + .alsoSigningWith(SENDER, RECEIVER), + getAccountBalance(RECEIVER).hasTinyBars(0L), + scheduleSign("threeSigXfer").alsoSigningWith(PAYER), + getAccountBalance(RECEIVER).hasTinyBars(1L)); } @HapiTest final Stream triggersUponAdditionalNeededSig() { - return defaultHapiSpec("TriggersUponAdditionalNeededSig") - .given( - cryptoCreate(SENDER).balance(1L), - cryptoCreate(RECEIVER).balance(0L).receiverSigRequired(true)) - .when( - scheduleCreate( - TWO_SIG_XFER, - cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)) - .fee(ONE_HBAR)) - .alsoSigningWith(SENDER), - getAccountBalance(RECEIVER).hasTinyBars(0L), - scheduleSign(TWO_SIG_XFER).alsoSigningWith(RECEIVER)) - .then(getAccountBalance(RECEIVER).hasTinyBars(1L)); + return hapiTest( + cryptoCreate(SENDER).balance(1L), + cryptoCreate(RECEIVER).balance(0L).receiverSigRequired(true), + scheduleCreate( + TWO_SIG_XFER, + cryptoTransfer(tinyBarsFromTo(SENDER, RECEIVER, 1)) + .fee(ONE_HBAR)) + .alsoSigningWith(SENDER), + getAccountBalance(RECEIVER).hasTinyBars(0L), + scheduleSign(TWO_SIG_XFER).alsoSigningWith(RECEIVER), + getAccountBalance(RECEIVER).hasTinyBars(1L)); } @HapiTest @@ -556,14 +519,12 @@ final Stream okIfAdminKeyOverlapsWithActiveScheduleKey() { var adminKey = "adminKey"; var scheduledTxnKey = "scheduledTxnKey"; - return defaultHapiSpec("OkIfAdminKeyOverlapsWithActiveScheduleKey") - .given( - newKeyNamed(adminKey).generator(keyGen).logged(), - newKeyNamed(scheduledTxnKey).generator(keyGen).logged(), - cryptoCreate(SENDER).key(scheduledTxnKey).balance(1L)) - .when(scheduleCreate(DEFERRED_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, ADDRESS_BOOK_CONTROL, 1))) - .adminKey(adminKey)) - .then(); + return hapiTest( + newKeyNamed(adminKey).generator(keyGen).logged(), + newKeyNamed(scheduledTxnKey).generator(keyGen).logged(), + cryptoCreate(SENDER).key(scheduledTxnKey).balance(1L), + scheduleCreate(DEFERRED_XFER, cryptoTransfer(tinyBarsFromTo(SENDER, ADDRESS_BOOK_CONTROL, 1))) + .adminKey(adminKey)); } @HapiTest @@ -571,56 +532,53 @@ final Stream okIfAdminKeyOverlapsWithActiveScheduleKey() { final Stream overlappingKeysTreatedAsExpected() { var keyGen = OverlappingKeyGenerator.withAtLeastOneOverlappingByte(2); - return defaultHapiSpec("OverlappingKeysTreatedAsExpected") - .given( - newKeyNamed("aKey").generator(keyGen), - newKeyNamed("bKey").generator(keyGen), - newKeyNamed("cKey"), - cryptoCreate("aSender").key("aKey").balance(1L), - cryptoCreate("cSender").key("cKey").balance(1L), - balanceSnapshot("before", ADDRESS_BOOK_CONTROL)) - .when(scheduleCreate( + return hapiTest( + newKeyNamed("aKey").generator(keyGen), + newKeyNamed("bKey").generator(keyGen), + newKeyNamed("cKey"), + cryptoCreate("aSender").key("aKey").balance(1L), + cryptoCreate("cSender").key("cKey").balance(1L), + balanceSnapshot("before", ADDRESS_BOOK_CONTROL), + scheduleCreate( DEFERRED_XFER, cryptoTransfer( tinyBarsFromTo("aSender", ADDRESS_BOOK_CONTROL, 1), - tinyBarsFromTo("cSender", ADDRESS_BOOK_CONTROL, 1)))) - .then( - scheduleSign(DEFERRED_XFER).alsoSigningWith("aKey"), - scheduleSign(DEFERRED_XFER).alsoSigningWith("aKey").hasKnownStatus(NO_NEW_VALID_SIGNATURES), - scheduleSign(DEFERRED_XFER) - .alsoSigningWith("aKey", "bKey") - .hasKnownStatus(NO_NEW_VALID_SIGNATURES), - scheduleSign(DEFERRED_XFER) - .alsoSigningWith("bKey") - /* In the rare, but possible, case that the overlapping byte shared by aKey - * and bKey is _also_ shared by the DEFAULT_PAYER, the bKey prefix in the sig - * map will probably not collide with aKey any more, and we will get - * NO_NEW_VALID_SIGNATURES instead of SOME_SIGNATURES_WERE_INVALID. - * - * So we need this to stabilize CI. But if just testing locally, you may - * only use .hasKnownStatus(SOME_SIGNATURES_WERE_INVALID) and it will pass - * >99.99% of the time. */ - .hasKnownStatusFrom(SOME_SIGNATURES_WERE_INVALID, NO_NEW_VALID_SIGNATURES), - scheduleSign(DEFERRED_XFER).alsoSigningWith("aKey", "bKey", "cKey"), - getAccountBalance(ADDRESS_BOOK_CONTROL).hasTinyBars(changeFromSnapshot("before", +2))); + tinyBarsFromTo("cSender", ADDRESS_BOOK_CONTROL, 1))), + scheduleSign(DEFERRED_XFER).alsoSigningWith("aKey"), + scheduleSign(DEFERRED_XFER).alsoSigningWith("aKey").hasKnownStatus(NO_NEW_VALID_SIGNATURES), + scheduleSign(DEFERRED_XFER).alsoSigningWith("aKey", "bKey").hasKnownStatus(NO_NEW_VALID_SIGNATURES), + scheduleSign(DEFERRED_XFER) + .alsoSigningWith("bKey") + /* In the rare, but possible, case that the overlapping byte shared by aKey + * and bKey is _also_ shared by the DEFAULT_PAYER, the bKey prefix in the sig + * map will probably not collide with aKey any more, and we will get + * NO_NEW_VALID_SIGNATURES instead of SOME_SIGNATURES_WERE_INVALID. + * + * So we need this to stabilize CI. But if just testing locally, you may + * only use .hasKnownStatus(SOME_SIGNATURES_WERE_INVALID) and it will pass + * >99.99% of the time. */ + .hasKnownStatusFrom(SOME_SIGNATURES_WERE_INVALID, NO_NEW_VALID_SIGNATURES), + scheduleSign(DEFERRED_XFER).alsoSigningWith("aKey", "bKey", "cKey"), + getAccountBalance(ADDRESS_BOOK_CONTROL).hasTinyBars(changeFromSnapshot("before", +2))); } @HapiTest final Stream retestsActivationOnSignWithEmptySigMap() { - return defaultHapiSpec("RetestsActivationOnCreateWithEmptySigMap") - .given(newKeyNamed("a"), newKeyNamed("b"), newKeyListNamed("ab", List.of("a", "b")), newKeyNamed(ADMIN)) - .when( - cryptoCreate(SENDER).key("ab").balance(665L), - scheduleCreate( - "deferredFall", - cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) - .fee(ONE_HBAR)) - .alsoSigningWith("a"), - getAccountBalance(SENDER).hasTinyBars(665L), - cryptoUpdate(SENDER).key("a")) - .then( - scheduleSign("deferredFall").alsoSigningWith(), - getAccountBalance(SENDER).hasTinyBars(664L)); + return hapiTest( + newKeyNamed("a"), + newKeyNamed("b"), + newKeyListNamed("ab", List.of("a", "b")), + newKeyNamed(ADMIN), + cryptoCreate(SENDER).key("ab").balance(665L), + scheduleCreate( + "deferredFall", + cryptoTransfer(tinyBarsFromTo(SENDER, FUNDING, 1)) + .fee(ONE_HBAR)) + .alsoSigningWith("a"), + getAccountBalance(SENDER).hasTinyBars(665L), + cryptoUpdate(SENDER).key("a"), + scheduleSign("deferredFall").alsoSigningWith(), + getAccountBalance(SENDER).hasTinyBars(664L)); } @LeakyHapiTest(overrides = {"ledger.schedule.txExpiryTimeSecs"}) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/StatefulScheduleExecutionTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/StatefulScheduleExecutionTest.java index 9fa4faf39f81..e2d298bba64e 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/StatefulScheduleExecutionTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/schedule/StatefulScheduleExecutionTest.java @@ -16,7 +16,6 @@ package com.hedera.services.bdd.suites.schedule; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountBalance; @@ -71,12 +70,11 @@ public class StatefulScheduleExecutionTest { @HapiTest @Order(4) final Stream scheduledBurnWithInvalidTokenThrowsUnresolvableSigners() { - return defaultHapiSpec("ScheduledBurnWithInvalidTokenThrowsUnresolvableSigners") - .given(cryptoCreate(SCHEDULE_PAYER)) - .when(scheduleCreate(VALID_SCHEDULE, burnToken("0.0.123231", List.of(1L, 2L))) + return hapiTest( + cryptoCreate(SCHEDULE_PAYER), + scheduleCreate(VALID_SCHEDULE, burnToken("0.0.123231", List.of(1L, 2L))) .designatingPayer(SCHEDULE_PAYER) - .hasKnownStatus(UNRESOLVABLE_REQUIRED_SIGNERS)) - .then(); + .hasKnownStatus(UNRESOLVABLE_REQUIRED_SIGNERS)); } @LeakyHapiTest(overrides = {"tokens.nfts.areEnabled"}) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/DynamicTestUtils.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/DynamicTestUtils.java new file mode 100644 index 000000000000..523af92deb50 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/DynamicTestUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 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.services.bdd.suites.utils; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.junit.jupiter.api.DynamicTest; + +public class DynamicTestUtils { + + /** + * Loops all the suppliers and their methods. If the method is annotated with the annotationClass annotation + * we invoke the method get the {@code Stream} and collect it to a list. + * @param suppliers Test class suppliers that contain tests methods returning {@code Stream} + * @param ignoredTests The test methods that are ignored + * @param annotationClass The target annotation + * @return list of all {@code Stream} collected from all found methods + */ + public static List> extractAllTestAnnotatedMethods( + @NonNull Supplier[] suppliers, + @NonNull List ignoredTests, + @NonNull Class annotationClass) { + var allDynamicTests = new ArrayList>(); + for (Supplier supplier : suppliers) { + Object instance = supplier.get(); + for (Method method : instance.getClass().getDeclaredMethods()) { + if (method.isAnnotationPresent(annotationClass)) { + if (ignoredTests.contains(method.getName())) { + continue; + } + method.setAccessible(true); + Stream testInvokeResult = null; + try { + testInvokeResult = (Stream) method.invoke(instance); + } catch (Exception e) { + throw new RuntimeException(e); // no handle for now + } + var dynamicTest = DynamicTest.dynamicTest( + method.getName(), testInvokeResult.toList().get(0).getExecutable()); + allDynamicTests.add(Stream.of(dynamicTest)); + } + } + } + return allDynamicTests; + } +} diff --git a/hedera-node/test-clients/src/main/java/module-info.java b/hedera-node/test-clients/src/main/java/module-info.java index b58cb01428d2..465321694c2c 100644 --- a/hedera-node/test-clients/src/main/java/module-info.java +++ b/hedera-node/test-clients/src/main/java/module-info.java @@ -98,7 +98,6 @@ requires com.hedera.node.app.service.token.impl; requires com.hedera.node.app.service.token; requires com.swirlds.base.test.fixtures; - requires com.swirlds.config.extensions.test.fixtures; requires com.swirlds.merkledb; requires com.swirlds.platform.core.test.fixtures; requires com.swirlds.state.impl; From 32089ae5f223fdca4ee320fa2ba003178f48cde8 Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Wed, 27 Nov 2024 10:35:57 -0600 Subject: [PATCH 3/7] fix: correct interpretation of empty child NFT transfer list (#16764) Signed-off-by: Michael Tinker --- .../spec/assertions/NonFungibleTransfers.java | 3 +- .../integration/RepeatableHip423Tests.java | 2 +- .../RepeatableIntegrationTests.java | 57 +++++++++++++++++++ .../integration/RepeatableTssTests.java | 2 +- 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableIntegrationTests.java diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/assertions/NonFungibleTransfers.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/assertions/NonFungibleTransfers.java index e0788ecd94a9..a1c37465f318 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/assertions/NonFungibleTransfers.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/assertions/NonFungibleTransfers.java @@ -18,6 +18,7 @@ import static com.hedera.services.bdd.spec.HapiPropertySource.asAccountString; import static com.hedera.services.bdd.spec.HapiPropertySource.asTokenString; +import static com.hedera.services.bdd.spec.transactions.TxnUtils.asId; import com.hedera.services.bdd.spec.HapiSpec; import com.hederahashgraph.api.proto.java.AccountID; @@ -57,7 +58,7 @@ public ErroringAsserts> assertsFor(HapiSpec spec) { final var sender = change.getLeft(); accountNameLookup.put(registry.getAccountID(sender), sender); final var receiver = change.getMiddle(); - accountNameLookup.put(registry.getAccountID(receiver), receiver); + accountNameLookup.put(asId(receiver, spec), receiver); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableHip423Tests.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableHip423Tests.java index fb63b6cfce02..278149ea1a33 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableHip423Tests.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableHip423Tests.java @@ -119,7 +119,7 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.TestMethodOrder; -@Order(2) +@Order(3) @Tag(INTEGRATION) @HapiTestLifecycle @TargetEmbeddedMode(REPEATABLE) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableIntegrationTests.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableIntegrationTests.java new file mode 100644 index 000000000000..407a0438c5a2 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableIntegrationTests.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 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.services.bdd.suites.integration; + +import static com.hedera.services.bdd.junit.RepeatableReason.NEEDS_VIRTUAL_TIME_FOR_FAST_EXECUTION; +import static com.hedera.services.bdd.junit.TestTags.INTEGRATION; +import static com.hedera.services.bdd.junit.hedera.embedded.EmbeddedMode.REPEATABLE; +import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; +import static com.hedera.services.bdd.spec.assertions.NonFungibleTransfers.changingNFTBalances; +import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; +import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.burnToken; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.doWithStartupConfig; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.waitUntilStartOfNextStakingPeriod; + +import com.hedera.services.bdd.junit.RepeatableHapiTest; +import com.hedera.services.bdd.junit.TargetEmbeddedMode; +import com.hedera.services.bdd.spec.dsl.annotations.NonFungibleToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecNonFungibleToken; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Tag; + +@Order(1) +@Tag(INTEGRATION) +@TargetEmbeddedMode(REPEATABLE) +public class RepeatableIntegrationTests { + @RepeatableHapiTest(NEEDS_VIRTUAL_TIME_FOR_FAST_EXECUTION) + Stream burnAtStakePeriodBoundaryHasExpectedRecord( + @NonFungibleToken(numPreMints = 2) SpecNonFungibleToken nft) { + return hapiTest( + nft.getInfo(), + doWithStartupConfig( + "staking.periodMins", value -> waitUntilStartOfNextStakingPeriod(Long.parseLong(value))), + burnToken(nft.name(), List.of(1L)).via("burn"), + getTxnRecord("burn") + .hasPriority(recordWith() + .tokenTransfers(changingNFTBalances() + .including(nft.name(), nft.treasury().name(), "0.0.0", 1L)))); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableTssTests.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableTssTests.java index 2f4d5aa82a74..1166f8990544 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableTssTests.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/integration/RepeatableTssTests.java @@ -55,7 +55,7 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; -@Order(1) +@Order(2) @Tag(INTEGRATION) @TargetEmbeddedMode(REPEATABLE) public class RepeatableTssTests { From 0759d3db9bc5b341e9180980270a40ecd63701ba Mon Sep 17 00:00:00 2001 From: JivkoKelchev Date: Thu, 28 Nov 2024 04:50:11 +0200 Subject: [PATCH 4/7] fix: Set the designated payer when building an EVM transactions from scheduled transactions (#16708) Signed-off-by: Zhivko Kelchev Signed-off-by: Valentin Tronkov <99957253+vtronkov@users.noreply.github.com> Signed-off-by: ibankov Signed-off-by: Michael Tinker Co-authored-by: Valentin Tronkov <99957253+vtronkov@users.noreply.github.com> Co-authored-by: ibankov Co-authored-by: Michael Tinker Co-authored-by: Joseph S. <121976561+jsync-swirlds@users.noreply.github.com> --- .../exec/ContextTransactionProcessor.java | 2 +- .../impl/infra/HevmTransactionFactory.java | 12 ++-- .../exec/ContextTransactionProcessorTest.java | 51 +++++++++------ .../EthereumTransactionHandlerTest.java | 10 ++- .../infra/HevmTransactionFactoryTest.java | 64 +++++++++++-------- .../hip423/ScheduleLongTermExecutionTest.java | 48 ++++++++------ 6 files changed, 115 insertions(+), 72 deletions(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/ContextTransactionProcessor.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/ContextTransactionProcessor.java index ab4ad6788e99..a4ae69fd18b3 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/ContextTransactionProcessor.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/ContextTransactionProcessor.java @@ -174,7 +174,7 @@ public CallOutcome call() { private HederaEvmTransaction safeCreateHevmTransaction() { try { - return hevmTransactionFactory.fromHapiTransaction(context.body()); + return hevmTransactionFactory.fromHapiTransaction(context.body(), context.payer()); } catch (HandleException e) { // Return a HederaEvmTransaction that represents the error in order to charge fees to the sender return hevmTransactionFactory.fromContractTxException(context.body(), e); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java index c215779d6405..df8a14f84f01 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java @@ -144,17 +144,15 @@ public HevmTransactionFactory( * Given a {@link TransactionBody}, creates the implied {@link HederaEvmTransaction}. * * @param body the {@link TransactionBody} to convert + * @param payerId transaction payer id * @return the implied {@link HederaEvmTransaction} * @throws IllegalArgumentException if the {@link TransactionBody} is not a contract operation */ - public HederaEvmTransaction fromHapiTransaction(@NonNull final TransactionBody body) { + public HederaEvmTransaction fromHapiTransaction(@NonNull final TransactionBody body, @NonNull AccountID payerId) { return switch (body.data().kind()) { - case CONTRACT_CREATE_INSTANCE -> fromHapiCreate( - body.transactionIDOrThrow().accountIDOrThrow(), body.contractCreateInstanceOrThrow()); - case CONTRACT_CALL -> fromHapiCall( - body.transactionIDOrThrow().accountIDOrThrow(), body.contractCallOrThrow()); - case ETHEREUM_TRANSACTION -> fromHapiEthereum( - body.transactionIDOrThrow().accountIDOrThrow(), body.ethereumTransactionOrThrow()); + case CONTRACT_CREATE_INSTANCE -> fromHapiCreate(payerId, body.contractCreateInstanceOrThrow()); + case CONTRACT_CALL -> fromHapiCall(payerId, body.contractCallOrThrow()); + case ETHEREUM_TRANSACTION -> fromHapiEthereum(payerId, body.ethereumTransactionOrThrow()); default -> throw new IllegalArgumentException("Not a contract operation"); }; } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/ContextTransactionProcessorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/ContextTransactionProcessorTest.java index 899c6413df35..ef6b5d29663b 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/ContextTransactionProcessorTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/ContextTransactionProcessorTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.TimestampSeconds; import com.hedera.hapi.node.base.TransactionID; import com.hedera.hapi.node.transaction.ExchangeRate; @@ -132,9 +133,7 @@ void callsComponentInfraAsExpectedForValidEthTx() { customGasCharging); givenSenderAccount(); - given(context.body()).willReturn(TransactionBody.DEFAULT); - given(hevmTransactionFactory.fromHapiTransaction(TransactionBody.DEFAULT)) - .willReturn(HEVM_CREATION); + givenBodyWithTxnIdWillReturnHEVM(); given(processor.processTransaction( HEVM_CREATION, rootProxyWorldUpdater, feesOnlyUpdater, hederaEvmContext, tracer, CONFIGURATION)) .willReturn(SUCCESS_RESULT_WITH_SIGNER_NONCE); @@ -171,9 +170,7 @@ void callsComponentInfraAsExpectedForValidEthTxWithoutTo() { customGasCharging); givenSenderAccount(); - given(context.body()).willReturn(TransactionBody.DEFAULT); - given(hevmTransactionFactory.fromHapiTransaction(TransactionBody.DEFAULT)) - .willReturn(HEVM_CREATION); + givenBodyWithTxnIdWillReturnHEVM(); given(processor.processTransaction( HEVM_CREATION, rootProxyWorldUpdater, feesOnlyUpdater, hederaEvmContext, tracer, CONFIGURATION)) .willReturn(SUCCESS_RESULT_WITH_SIGNER_NONCE); @@ -208,9 +205,7 @@ void callsComponentInfraAsExpectedForNonEthTx() { processor, customGasCharging); - given(context.body()).willReturn(TransactionBody.DEFAULT); - given(hevmTransactionFactory.fromHapiTransaction(TransactionBody.DEFAULT)) - .willReturn(HEVM_CREATION); + givenBodyWithTxnIdWillReturnHEVM(); given(processor.processTransaction( HEVM_CREATION, rootProxyWorldUpdater, feesOnlyUpdater, hederaEvmContext, tracer, CONFIGURATION)) .willReturn(SUCCESS_RESULT); @@ -240,9 +235,7 @@ void stillChargesHapiFeesOnAbort() { processor, customGasCharging); - given(context.body()).willReturn(TransactionBody.DEFAULT); - given(hevmTransactionFactory.fromHapiTransaction(TransactionBody.DEFAULT)) - .willReturn(HEVM_CREATION); + givenBodyWithTxnIdWillReturnHEVM(); given(processor.processTransaction( HEVM_CREATION, rootProxyWorldUpdater, feesOnlyUpdater, hederaEvmContext, tracer, CONFIGURATION)) .willThrow(new HandleException(INVALID_CONTRACT_ID)); @@ -272,8 +265,9 @@ void chargeHapiFeeOnFailedEthTransaction() { customGasCharging); given(context.body()).willReturn(TransactionBody.DEFAULT); + given(context.payer()).willReturn(AccountID.DEFAULT); final var ethTx = wellKnownRelayedHapiCallWithGasLimit(1_000_000L); - given(hevmTransactionFactory.fromHapiTransaction(TransactionBody.DEFAULT)) + given(hevmTransactionFactory.fromHapiTransaction(TransactionBody.DEFAULT, context.payer())) .willReturn(ethTx); given(processor.processTransaction( ethTx, rootProxyWorldUpdater, feesOnlyUpdater, hederaEvmContext, tracer, CONFIGURATION)) @@ -308,7 +302,10 @@ void stillChargesGasFeesOnHevmException() { customGasCharging); given(context.body()).willReturn(transactionBody); - given(hevmTransactionFactory.fromHapiTransaction(transactionBody)).willReturn(HEVM_Exception); + final var payer = AccountID.DEFAULT; + given(context.payer()).willReturn(payer); + given(hevmTransactionFactory.fromHapiTransaction(transactionBody, payer)) + .willReturn(HEVM_Exception); given(transactionBody.transactionIDOrThrow()).willReturn(transactionID); given(transactionID.accountIDOrThrow()).willReturn(SENDER_ID); @@ -337,7 +334,10 @@ void doesNotChargeGasFeesOnHevmExceptionIfSoConfigured() { customGasCharging); given(context.body()).willReturn(transactionBody); - given(hevmTransactionFactory.fromHapiTransaction(transactionBody)).willReturn(HEVM_Exception); + final var payer = AccountID.DEFAULT; + given(context.payer()).willReturn(payer); + given(hevmTransactionFactory.fromHapiTransaction(transactionBody, payer)) + .willReturn(HEVM_Exception); given(transactionBody.transactionIDOrThrow()).willReturn(transactionID); given(transactionID.accountIDOrThrow()).willReturn(SENDER_ID); @@ -366,7 +366,9 @@ void stillChargesGasFeesOnExceptionThrown() { customGasCharging); given(context.body()).willReturn(transactionBody); - given(hevmTransactionFactory.fromHapiTransaction(transactionBody)) + final var payer = AccountID.DEFAULT; + given(context.payer()).willReturn(payer); + given(hevmTransactionFactory.fromHapiTransaction(transactionBody, payer)) .willThrow(new HandleException(INVALID_CONTRACT_ID)); given(hevmTransactionFactory.fromContractTxException(any(), any())).willReturn(HEVM_Exception); given(transactionBody.transactionIDOrThrow()).willReturn(transactionID); @@ -399,7 +401,9 @@ void doesNotChargeGasAndHapiFeesOnExceptionThrownIfSoConfigured() { customGasCharging); given(context.body()).willReturn(transactionBody); - given(hevmTransactionFactory.fromHapiTransaction(transactionBody)) + final var payer = AccountID.DEFAULT; + given(context.payer()).willReturn(payer); + given(hevmTransactionFactory.fromHapiTransaction(transactionBody, payer)) .willThrow(new HandleException(INVALID_CONTRACT_ID)); given(hevmTransactionFactory.fromContractTxException(any(), any())).willReturn(HEVM_Exception); given(transactionBody.transactionIDOrThrow()).willReturn(transactionID); @@ -432,9 +436,10 @@ void reThrowsExceptionWhenNotContractCall() { customGasCharging); given(context.body()).willReturn(transactionBody); + given(context.payer()).willReturn(SENDER_ID); given(transactionBody.transactionIDOrThrow()).willReturn(transactionID); given(transactionID.accountIDOrThrow()).willReturn(SENDER_ID); - given(hevmTransactionFactory.fromHapiTransaction(transactionBody)) + given(hevmTransactionFactory.fromHapiTransaction(transactionBody, SENDER_ID)) .willThrow(new HandleException(INVALID_CONTRACT_ID)); given(hevmTransactionFactory.fromContractTxException(any(), any())).willReturn(HEVM_Exception); @@ -467,4 +472,14 @@ void givenSenderAccount() { given(rootProxyWorldUpdater.getHederaAccount(SENDER_ID)).willReturn(senderAccount); given(senderAccount.getNonce()).willReturn(1L); } + + void givenBodyWithTxnIdWillReturnHEVM() { + final var body = TransactionBody.newBuilder() + .transactionID(TransactionID.DEFAULT) + .build(); + final var payer = AccountID.DEFAULT; + given(context.body()).willReturn(body); + given(context.payer()).willReturn(payer); + given(hevmTransactionFactory.fromHapiTransaction(body, payer)).willReturn(HEVM_CREATION); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java index 151706c9caa5..056586e0d673 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java @@ -37,6 +37,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.TransactionID; import com.hedera.hapi.node.contract.EthereumTransactionBody; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.hapi.utils.ethereum.EthTxData; @@ -177,7 +179,13 @@ void setUpTransactionProcessing() { customGasCharging); given(component.contextTransactionProcessor()).willReturn(contextTransactionProcessor); - given(hevmTransactionFactory.fromHapiTransaction(handleContext.body())).willReturn(HEVM_CREATION); + final var body = TransactionBody.newBuilder() + .transactionID(TransactionID.DEFAULT) + .build(); + given(handleContext.body()).willReturn(body); + given(handleContext.payer()).willReturn(AccountID.DEFAULT); + given(hevmTransactionFactory.fromHapiTransaction(handleContext.body(), handleContext.payer())) + .willReturn(HEVM_CREATION); given(transactionProcessor.processTransaction( HEVM_CREATION, diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java index 87c84f80e411..c8c343cb5794 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java @@ -528,7 +528,9 @@ void fromHapiCreationSkips0xPrefixFromInitcodeIfPresent() { @Test void fromHapiTransactionThrowsOnNonContractOperation() { - assertThrows(IllegalArgumentException.class, () -> subject.fromHapiTransaction(TransactionBody.DEFAULT)); + assertThrows( + IllegalArgumentException.class, + () -> subject.fromHapiTransaction(TransactionBody.DEFAULT, AccountID.DEFAULT)); } @Test @@ -627,53 +629,65 @@ private void assertCreateFailsWith( @NonNull final Consumer spec) { assertFailsWith( status, - () -> subject.fromHapiTransaction(TransactionBody.newBuilder() - .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) - .contractCreateInstance(createWith(spec)) - .build())); + () -> subject.fromHapiTransaction( + TransactionBody.newBuilder() + .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) + .contractCreateInstance(createWith(spec)) + .build(), + SENDER_ID)); } private void assertCallFailsWith( @NonNull final ResponseCodeEnum status, @NonNull final Consumer spec) { assertFailsWith( status, - () -> subject.fromHapiTransaction(TransactionBody.newBuilder() - .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) - .contractCall(callWith(spec)) - .build())); + () -> subject.fromHapiTransaction( + TransactionBody.newBuilder() + .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) + .contractCall(callWith(spec)) + .build(), + SENDER_ID)); } private void assertEthTxFailsWith( @NonNull final ResponseCodeEnum status, @NonNull final Consumer spec) { assertFailsWith( status, - () -> subject.fromHapiTransaction(TransactionBody.newBuilder() - .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) - .ethereumTransaction(ethTxWith(spec)) - .build())); + () -> subject.fromHapiTransaction( + TransactionBody.newBuilder() + .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) + .ethereumTransaction(ethTxWith(spec)) + .build(), + SENDER_ID)); } private HederaEvmTransaction getManufacturedEthTx(@NonNull final Consumer spec) { - return subject.fromHapiTransaction(TransactionBody.newBuilder() - .transactionID(TransactionID.newBuilder().accountID(RELAYER_ID)) - .ethereumTransaction(ethTxWith(spec)) - .build()); + return subject.fromHapiTransaction( + TransactionBody.newBuilder() + .transactionID(TransactionID.newBuilder().accountID(RELAYER_ID)) + .ethereumTransaction(ethTxWith(spec)) + .build(), + RELAYER_ID); } private HederaEvmTransaction getManufacturedCreation( @NonNull final Consumer spec) { - return subject.fromHapiTransaction(TransactionBody.newBuilder() - .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) - .contractCreateInstance(createWith(spec)) - .build()); + return subject.fromHapiTransaction( + TransactionBody.newBuilder() + .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) + .contractCreateInstance(createWith(spec)) + .build(), + SENDER_ID); } private HederaEvmTransaction getManufacturedCall( @NonNull final Consumer spec) { - return subject.fromHapiTransaction(TransactionBody.newBuilder() - .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) - .contractCall(callWith(spec)) - .build()); + return subject.fromHapiTransaction( + TransactionBody.newBuilder() + .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) + .contractCall(callWith(spec)) + .build(), + SENDER_ID); } private HederaEvmTransaction getManufacturedCallException( diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip423/ScheduleLongTermExecutionTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip423/ScheduleLongTermExecutionTest.java index ba89fda4e64d..02aac454f35c 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip423/ScheduleLongTermExecutionTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip423/ScheduleLongTermExecutionTest.java @@ -16,6 +16,7 @@ package com.hedera.services.bdd.suites.hip423; +import static com.hedera.services.bdd.junit.ContextRequirement.FEE_SCHEDULE_OVERRIDES; import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; @@ -86,16 +87,17 @@ import com.hedera.services.bdd.junit.HapiTest; import com.hedera.services.bdd.junit.HapiTestLifecycle; +import com.hedera.services.bdd.junit.LeakyHapiTest; import com.hedera.services.bdd.junit.support.TestLifecycle; import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import java.time.Instant; import java.util.Map; import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; @@ -117,6 +119,7 @@ public class ScheduleLongTermExecutionTest { private static final String FAILED_XFER = "failedXfer"; private static final String WEIRDLY_POPULAR_KEY_TXN = "weirdlyPopularKeyTxn"; private static final String PAYER_TXN = "payerTxn"; + private static final long PAYER_INITIAL_BALANCE = 1000000000000L; @BeforeAll static void beforeAll(@NonNull final TestLifecycle lifecycle) { @@ -558,19 +561,19 @@ public Stream executionWithDefaultPayerWorks() { })); } - @HapiTest + @LeakyHapiTest(requirement = FEE_SCHEDULE_OVERRIDES) @Order(5) - @Disabled - // future: currently contract transactions extract payer id from the trxId, and can't use a custom payer. - // the fix will be in following PR public Stream executionWithContractCallWorksAtExpiry() { + final var payerBalance = new AtomicLong(); return defaultHapiSpec("ExecutionWithContractCallWorksAtExpiry") .given( // upload fees for SCHEDULE_CREATE_CONTRACT_CALL uploadScheduledContractPrices(GENESIS), uploadInitCode(SIMPLE_UPDATE), contractCreate(SIMPLE_UPDATE).gas(500_000L), - cryptoCreate(PAYING_ACCOUNT).balance(1000000000000L).via(PAYING_ACCOUNT_TXN)) + cryptoCreate(PAYING_ACCOUNT) + .balance(PAYER_INITIAL_BALANCE) + .via(PAYING_ACCOUNT_TXN)) .when(scheduleCreate( BASIC_XFER, contractCall(SIMPLE_UPDATE, "set", BigInteger.valueOf(5), BigInteger.valueOf(42)) @@ -591,13 +594,19 @@ public Stream executionWithContractCallWorksAtExpiry() { .hasRecordedScheduledTxn(), sleepFor(5000), cryptoCreate("foo").via(TRIGGERING_TXN), + sleepFor(500), getScheduleInfo(BASIC_XFER).hasCostAnswerPrecheck(INVALID_SCHEDULE_ID), getAccountBalance(PAYING_ACCOUNT) - .hasTinyBars(spec -> - bal -> bal < 1000000000000L ? Optional.empty() : Optional.of("didnt change")), + .hasTinyBars(spec -> bal -> + bal < PAYER_INITIAL_BALANCE ? Optional.empty() : Optional.of("didnt change")) + .exposingBalanceTo(payerBalance::set), withOpContext((spec, opLog) -> { var triggeredTx = getTxnRecord(CREATE_TX).scheduled(); allRunFor(spec, triggeredTx); + final var txnFee = triggeredTx.getResponseRecord().getTransactionFee(); + // check if only designating payer was charged + Assertions.assertEquals(PAYER_INITIAL_BALANCE, txnFee + payerBalance.get()); + Assertions.assertEquals( SUCCESS, triggeredTx.getResponseRecord().getReceipt().getStatus(), @@ -614,15 +623,14 @@ public Stream executionWithContractCallWorksAtExpiry() { @HapiTest @Order(6) - @Disabled - // future: currently contract transactions extract payer id from the trxId, and can't use a custom payer. - // the fix will be in following PR public Stream executionWithContractCreateWorksAtExpiry() { + final var payerBalance = new AtomicLong(); return defaultHapiSpec("ExecutionWithContractCreateWorksAtExpiry") .given( - // overriding(SCHEDULING_WHITELIST, "ContractCreate"), uploadInitCode(SIMPLE_UPDATE), - cryptoCreate(PAYING_ACCOUNT).balance(1000000000000L).via(PAYING_ACCOUNT_TXN)) + cryptoCreate(PAYING_ACCOUNT) + .balance(PAYER_INITIAL_BALANCE) + .via(PAYING_ACCOUNT_TXN)) .when(scheduleCreate( BASIC_XFER, contractCreate(SIMPLE_UPDATE).gas(500_000L).adminKey(PAYING_ACCOUNT)) @@ -642,18 +650,18 @@ public Stream executionWithContractCreateWorksAtExpiry() { .hasRecordedScheduledTxn(), sleepFor(5000), cryptoCreate("foo").via(TRIGGERING_TXN), + sleepFor(2000), getScheduleInfo(BASIC_XFER).hasCostAnswerPrecheck(INVALID_SCHEDULE_ID), - // todo check white list here? - // overriding( - // SCHEDULING_WHITELIST, - // - // HapiSpecSetup.getDefaultNodeProps().get(SCHEDULING_WHITELIST)), getAccountBalance(PAYING_ACCOUNT) - .hasTinyBars(spec -> - bal -> bal < 1000000000000L ? Optional.empty() : Optional.of("didnt change")), + .hasTinyBars(spec -> bal -> + bal < PAYER_INITIAL_BALANCE ? Optional.empty() : Optional.of("didnt change")) + .exposingBalanceTo(payerBalance::set), withOpContext((spec, opLog) -> { var triggeredTx = getTxnRecord(CREATE_TX).scheduled(); allRunFor(spec, triggeredTx); + final var txnFee = triggeredTx.getResponseRecord().getTransactionFee(); + // check if only designating payer was charged + Assertions.assertEquals(PAYER_INITIAL_BALANCE, txnFee + payerBalance.get()); Assertions.assertEquals( SUCCESS, From 539f059126acd770b7f68eabc7b374a3250054e2 Mon Sep 17 00:00:00 2001 From: Ivan Malygin Date: Thu, 28 Nov 2024 08:54:28 -0500 Subject: [PATCH 5/7] fix: 16750 Fixed serialization for `ISSTestingToolState` (#16826) Signed-off-by: Ivan Malygin --- .../swirlds/demo/iss/ISSTestingToolState.java | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java index e48527ead7f7..994c44114486 100644 --- a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java +++ b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java @@ -36,6 +36,9 @@ import com.hedera.hapi.node.state.roster.Roster; import com.hedera.hapi.node.state.roster.RosterEntry; import com.swirlds.common.constructable.ConstructableIgnored; +import com.swirlds.common.io.SelfSerializable; +import com.swirlds.common.io.streams.SerializableDataInputStream; +import com.swirlds.common.io.streams.SerializableDataOutputStream; import com.swirlds.common.merkle.utility.SerializableLong; import com.swirlds.common.platform.NodeId; import com.swirlds.common.utility.ByteUtils; @@ -50,8 +53,13 @@ import com.swirlds.platform.system.events.ConsensusEvent; import com.swirlds.platform.system.transaction.ConsensusTransaction; import com.swirlds.platform.test.fixtures.state.FakeMerkleStateLifecycles; +import com.swirlds.state.merkle.singleton.StringLeaf; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; import java.util.HashMap; @@ -62,6 +70,7 @@ import java.util.Objects; import java.util.Random; import java.util.function.Function; +import java.util.function.Supplier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -89,6 +98,11 @@ private static class ClassVersion { */ private static final Duration INCIDENT_WINDOW = Duration.ofSeconds(10); + private static final int RUNNING_SUM_INDEX = 1; + private static final int GENESIS_TIMESTAMP_INDEX = 2; + private static final int PLANNED_ISS_LIST_INDEX = 3; + private static final int PLANNED_LOG_ERROR_LIST_INDEX = 4; + private NodeId selfId; /** @@ -172,6 +186,19 @@ public void init( this.plannedIssList = testingToolConfig.getPlannedISSs(); this.plannedLogErrorList = testingToolConfig.getPlannedLogErrors(); + writeObjectByChildIndex(PLANNED_ISS_LIST_INDEX, plannedIssList); + writeObjectByChildIndex(PLANNED_LOG_ERROR_LIST_INDEX, plannedLogErrorList); + } else { + StringLeaf runningSumLeaf = getChild(RUNNING_SUM_INDEX); + if (runningSumLeaf != null) { + runningSum = Long.parseLong(runningSumLeaf.getLabel()); + } + StringLeaf genesisTimestampLeaf = getChild(GENESIS_TIMESTAMP_INDEX); + if (genesisTimestampLeaf != null) { + genesisTimestamp = Instant.parse(genesisTimestampLeaf.getLabel()); + } + plannedIssList = readObjectByChildIndex(PLANNED_ISS_LIST_INDEX, PlannedIss::new); + plannedLogErrorList = readObjectByChildIndex(PLANNED_LOG_ERROR_LIST_INDEX, PlannedLogError::new); } this.selfId = platform.getSelfId(); @@ -179,6 +206,32 @@ public void init( Scratchpad.create(platform.getContext(), selfId, IssTestingToolScratchpad.class, "ISSTestingTool"); } + List readObjectByChildIndex(int index, Supplier factory) { + StringLeaf stringValue = getChild(index); + if (stringValue != null) { + try { + SerializableDataInputStream in = new SerializableDataInputStream( + new ByteArrayInputStream(stringValue.getLabel().getBytes(StandardCharsets.UTF_8))); + return in.readSerializableList(1024, false, factory); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + return null; + } + } + + void writeObjectByChildIndex(int index, List list) { + try { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + SerializableDataOutputStream out = new SerializableDataOutputStream(byteOut); + out.writeSerializableList(list, false, true); + setChild(index, new StringLeaf(byteOut.toString(StandardCharsets.UTF_8))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * {@inheritDoc} */ @@ -221,6 +274,7 @@ public void handleConsensusRound(final Round round, final PlatformStateModifier private void captureTimestamp(final ConsensusEvent event) { if (genesisTimestamp == null) { genesisTimestamp = event.getConsensusTimestamp(); + setChild(GENESIS_TIMESTAMP_INDEX, new StringLeaf(genesisTimestamp.toString())); } } @@ -236,6 +290,7 @@ private void handleTransaction(final ConsensusTransaction transaction) { final int delta = ByteUtils.byteArrayToInt(transaction.getApplicationTransaction().toByteArray(), 0); runningSum += delta; + setChild(RUNNING_SUM_INDEX, new StringLeaf(Long.toString(runningSum))); } /** @@ -325,7 +380,7 @@ private int findLargestPartition(@NonNull final Roster roster, @NonNull final Pl int largestPartition = 0; long largestPartitionWeight = 0; for (int partition = 0; partition < plannedIss.getPartitionCount(); partition++) { - if (partitionWeights.get(partition) > largestPartitionWeight) { + if (partitionWeights.get(partition) != null && partitionWeights.get(partition) > largestPartitionWeight) { largestPartition = partition; largestPartitionWeight = partitionWeights.getOrDefault(partition, 0L); } From 2a82e0d1503f6599fdd7aa3e85aabd8ffc75ba46 Mon Sep 17 00:00:00 2001 From: Petar Tonev Date: Thu, 28 Nov 2024 21:06:41 +0200 Subject: [PATCH 6/7] test: Increase code coverage in `CryptoDeleteAllowanceHandlerTest` (#16834) Signed-off-by: Petar Tonev --- .../CryptoDeleteAllowanceHandlerTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java index fe206a575965..37b5bfc6f409 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java @@ -23,6 +23,7 @@ import static com.hedera.node.app.spi.fixtures.workflows.ExceptionConditions.responseCode; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertIterableEquals; import static org.mockito.ArgumentMatchers.any; @@ -30,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.mock; import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.TransactionID; @@ -39,6 +41,10 @@ import com.hedera.node.app.service.token.impl.handlers.CryptoDeleteAllowanceHandler; import com.hedera.node.app.service.token.impl.test.handlers.util.CryptoTokenHandlerTestBase; import com.hedera.node.app.service.token.impl.validators.DeleteAllowanceValidator; +import com.hedera.node.app.spi.fees.FeeCalculator; +import com.hedera.node.app.spi.fees.FeeCalculatorFactory; +import com.hedera.node.app.spi.fees.FeeContext; +import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.fixtures.workflows.FakePreHandleContext; import com.hedera.node.app.spi.validation.ExpiryValidator; import com.hedera.node.app.spi.workflows.HandleContext; @@ -46,6 +52,7 @@ import com.hedera.node.app.spi.workflows.PreCheckException; import java.util.List; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -119,6 +126,32 @@ void happyPathDeletesAllowances() { assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNull(); } + @Test + void testNoOpDeleteAllowancesWhenListIsEmpty() { + writableNftStore.put(nftSl1.copyBuilder().spenderId(spenderId).build()); + writableNftStore.put(nftSl2.copyBuilder().spenderId(spenderId).build()); + + final var txn = allowancesTxn(payerId, List.of()); + given(handleContext.body()).willReturn(txn); + given(handleContext.payer()).willReturn(payerId); + given(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong())).willReturn(OK); + + assertThat(ownerAccount.approveForAllNftAllowances()).hasSize(1); + assertThat(writableNftStore.get(nftIdSl1).ownerId()).isEqualTo(ownerId); + assertThat(writableNftStore.get(nftIdSl2).ownerId()).isEqualTo(ownerId); + assertThat(writableNftStore.get(nftIdSl1).spenderId()).isEqualTo(spenderId); + assertThat(writableNftStore.get(nftIdSl2).spenderId()).isEqualTo(spenderId); + + subject.handle(handleContext); + + assertThat(ownerAccount.approveForAllNftAllowances()).hasSize(1); + assertThat(writableNftStore.get(nftIdSl1).ownerId()).isEqualTo(ownerId); + assertThat(writableNftStore.get(nftIdSl2).ownerId()).isEqualTo(ownerId); + // we expect the allowances to not be removed because of no op when list is empty + assertThat(writableNftStore.get(nftIdSl1).spenderId()).isNotNull(); + assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNotNull(); + } + @Test void canDeleteAllowancesOnTreasury() { writableNftStore.put(nftSl1.copyBuilder().spenderId(spenderId).build()); @@ -157,6 +190,17 @@ void validateIfSerialsEmpty() { .has(responseCode(EMPTY_ALLOWANCES)); } + @Test + void validateIfPureChecksDoesNotThrow() { + final var nftAllowance = NftRemoveAllowance.newBuilder() + .owner(payerId) + .tokenId(nonFungibleTokenId) + .serialNumbers(List.of(1L, 2L)) + .build(); + final var txn = allowancesTxn(payerId, List.of(nftAllowance)); + assertDoesNotThrow(() -> subject.pureChecks(txn)); + } + @Test void checksEmptyAllowancesInTxn() { final var txn = TransactionBody.newBuilder() @@ -284,6 +328,59 @@ void considersPayerIfOwnerNotSpecified() { assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNull(); } + @Test + @DisplayName("check that fees are 1 for delete account allowance trx") + void testCalculateFeesReturnsCorrectFeeForDeleteAccountAllowance() { + final var feeCtx = mock(FeeContext.class); + final var feeCalcFact = mock(FeeCalculatorFactory.class); + final var feeCalc = mock(FeeCalculator.class); + final var txnBody = cryptoDeleteAllowanceTransaction(payerId); + given(feeCtx.feeCalculatorFactory()).willReturn(feeCalcFact); + given(feeCtx.body()).willReturn(txnBody); + given(feeCalcFact.feeCalculator(any())).willReturn(feeCalc); + given(feeCalc.addBytesPerTransaction(anyLong())).willReturn(feeCalc); + given(feeCalc.calculate()).willReturn(new Fees(1, 0, 0)); + + assertThat(subject.calculateFees(feeCtx)).isEqualTo(new Fees(1, 0, 0)); + } + + @Test + @DisplayName("calculate fees correctly considering bytes per transaction") + void testCalculateFeesConsideringBytesPerTransaction() { + final var feeCtx = mock(FeeContext.class); + final var feeCalcFact = mock(FeeCalculatorFactory.class); + final var feeCalc = mock(FeeCalculator.class); + final var txnBody = cryptoDeleteAllowanceTransaction(payerId); + given(feeCtx.feeCalculatorFactory()).willReturn(feeCalcFact); + given(feeCtx.body()).willReturn(txnBody); + given(feeCalcFact.feeCalculator(any())).willReturn(feeCalc); + final var cryptoDeleteAllowanceTransactionBody = txnBody.cryptoDeleteAllowanceOrThrow(); + final var longSize = 8L; + final var nftDeleteAllowanceSize = 6 * longSize; + final var bytesPerTransaction = + cryptoDeleteAllowanceTransactionBody.nftAllowances().size() * nftDeleteAllowanceSize + (2 * longSize); + given(feeCalc.addBytesPerTransaction(bytesPerTransaction)).willReturn(feeCalc); + given(feeCalc.calculate()).willReturn(new Fees(1, 0, 0)); + + assertThat(subject.calculateFees(feeCtx)).isEqualTo(new Fees(1, 0, 0)); + } + + @Test + @DisplayName("check that fees are 1 for delete NFT serials trx") + void testCountNftDeleteSerials() { + final var feeCtx = mock(FeeContext.class); + final var feeCalcFact = mock(FeeCalculatorFactory.class); + final var feeCalc = mock(FeeCalculator.class); + final var txnBody = cryptoDeleteAllowanceTransaction(payerId); + given(feeCtx.feeCalculatorFactory()).willReturn(feeCalcFact); + given(feeCtx.body()).willReturn(txnBody); + given(feeCalcFact.feeCalculator(any())).willReturn(feeCalc); + given(feeCalc.addBytesPerTransaction(anyLong())).willReturn(feeCalc); + given(feeCalc.calculate()).willReturn(new Fees(1, 0, 0)); + + assertThat(subject.calculateFees(feeCtx)).isEqualTo(new Fees(1, 0, 0)); + } + private TransactionBody cryptoDeleteAllowanceTransaction(final AccountID txnPayer) { final var nftAllowance = NftRemoveAllowance.newBuilder() .owner(ownerId) From b8f06371c31c6dbe5479fe0c63702571cefdd476 Mon Sep 17 00:00:00 2001 From: Petar Tonev Date: Fri, 29 Nov 2024 10:28:46 +0200 Subject: [PATCH 7/7] test: Increase code coverage in `CryptoDeleteHandlerTest` (#16832) Signed-off-by: Petar Tonev --- .../handlers/CryptoDeleteHandlerTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java index 30ddf1df2091..7dbcbf852234 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java @@ -34,11 +34,13 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import com.hedera.hapi.node.base.AccountID; @@ -55,6 +57,10 @@ import com.hedera.node.app.service.token.impl.test.handlers.util.CryptoHandlerTestBase; import com.hedera.node.app.service.token.impl.validators.StakingValidator; import com.hedera.node.app.service.token.records.CryptoDeleteStreamBuilder; +import com.hedera.node.app.spi.fees.FeeCalculator; +import com.hedera.node.app.spi.fees.FeeCalculatorFactory; +import com.hedera.node.app.spi.fees.FeeContext; +import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.fixtures.workflows.FakePreHandleContext; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.store.StoreFactory; @@ -68,7 +74,9 @@ import com.swirlds.state.spi.WritableStates; import java.util.List; import java.util.Map; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -384,6 +392,19 @@ void failsIfEitherDeleteOrTransferAccountDoesntExist() throws PreCheckException .has(responseCode(ACCOUNT_ID_DOES_NOT_EXIST)); } + @Test + @DisplayName("check that fees are 1 for delete account trx") + void testCalculateFeesReturnsCorrectFeeForDeleteAccount() { + final var feeCtx = mock(FeeContext.class); + final var feeCalcFact = mock(FeeCalculatorFactory.class); + final var feeCalc = mock(FeeCalculator.class); + given(feeCtx.feeCalculatorFactory()).willReturn(feeCalcFact); + given(feeCalcFact.feeCalculator(any())).willReturn(feeCalc); + given(feeCalc.legacyCalculate(any())).willReturn(new Fees(1, 0, 0)); + + Assertions.assertThat(subject.calculateFees(feeCtx)).isEqualTo(new Fees(1, 0, 0)); + } + private TransactionBody deleteAccountTransaction( final AccountID deleteAccountId, final AccountID transferAccountId) { final var transactionID = TransactionID.newBuilder().accountID(id).transactionValidStart(consensusTimestamp);