diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox-4.stateful-prop.test.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox-4.stateful-prop.test.ts index 15f4d4ddc0..bf8b63ffe7 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox-4.stateful-prop.test.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox-4.stateful-prop.test.ts @@ -124,13 +124,17 @@ it("statefully interacts with PoX-4", async () => { poolMembers: [], delegatedTo: "", delegatedMaxAmount: 0, + // We initialize delegatedUntilBurnHt to 0. It will be updated + // after successful delegate-stx calls. It's value will be either + // the unwrapped until-burn-ht uint passed to the delegate-stx, + // or undefined for indefinite delegations. delegatedUntilBurnHt: 0, delegatedPoxAddress: "", amountLocked: 0, amountUnlocked: 100_000_000_000_000, unlockHeight: 0, firstLockedRewardCycle: 0, - allowedContractCaller: "", + allowedContractCallers: [], callerAllowedBy: [], committedRewCycleIndexes: [], }])), diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_AllowContractCallerCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_AllowContractCallerCommand.ts index dad1a381a5..141676cdae 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_AllowContractCallerCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_AllowContractCallerCommand.ts @@ -74,32 +74,17 @@ export class AllowContractCallerCommand implements PoxCommand { // Get the wallets involved from the model and update it with the new state. const wallet = model.stackers.get(this.wallet.stxAddress)!; - const callerAllowedBefore = wallet.allowedContractCaller; - - const callerAllowedBeforeState = model.stackers.get(callerAllowedBefore) || - null; - - if (callerAllowedBeforeState) { - // Remove the allower from the ex-allowed caller's allowance list. - - const walletIndexInsideAllowedByList = callerAllowedBeforeState - .callerAllowedBy.indexOf( - this.wallet.stxAddress, - ); - - expect(walletIndexInsideAllowedByList).toBeGreaterThan(-1); - - callerAllowedBeforeState.callerAllowedBy.splice( - walletIndexInsideAllowedByList, - 1, - ); - } const callerToAllow = model.stackers.get(this.allowanceTo.stxAddress)!; // Update model so that we know this wallet has authorized a contract-caller. + // If the caller is already allowed, there's no need to add it again. + const callerToAllowIndexInAllowedList = wallet.allowedContractCallers + .indexOf(this.allowanceTo.stxAddress); - wallet.allowedContractCaller = this.allowanceTo.stxAddress; - callerToAllow.callerAllowedBy.push(this.wallet.stxAddress); + if (callerToAllowIndexInAllowedList == -1) { + wallet.allowedContractCallers.push(this.allowanceTo.stxAddress); + callerToAllow.callerAllowedBy.push(this.wallet.stxAddress); + } // Log to console for debugging purposes. This is not necessary for the // test to pass but it is useful for debugging and eyeballing the test. diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_CommandModel.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_CommandModel.ts index 6d4d582b58..ce1d2a28b4 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_CommandModel.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_CommandModel.ts @@ -76,9 +76,14 @@ export class Stub { // Get the wallet's ex-delegators by comparing their delegatedUntilBurnHt // to the current burn block height (only if the wallet is a delegatee). - const expiredDelegators = wallet.poolMembers.filter((stackerAddress) => - this.stackers.get(stackerAddress)!.delegatedUntilBurnHt < - burnBlockHeight + // If the delegatedUntilBurnHt is undefined, the delegator is considered + // active for an indefinite period (until a revoke-delegate-stx call). + const expiredDelegators = wallet.poolMembers.filter( + (stackerAddress) => + this.stackers.get(stackerAddress)!.delegatedUntilBurnHt !== + undefined && + this.stackers.get(stackerAddress)!.delegatedUntilBurnHt as number < + burnBlockHeight, ); // Get the operator's pool stackers that no longer have partially commited @@ -180,13 +185,13 @@ export type Stacker = { poolMembers: StxAddress[]; delegatedTo: StxAddress; delegatedMaxAmount: number; - delegatedUntilBurnHt: number; + delegatedUntilBurnHt: number | undefined; delegatedPoxAddress: BtcAddress; amountLocked: number; amountUnlocked: number; unlockHeight: number; firstLockedRewardCycle: number; - allowedContractCaller: StxAddress; + allowedContractCallers: StxAddress[]; callerAllowedBy: StxAddress[]; committedRewCycleIndexes: number[]; }; diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_Commands.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_Commands.ts index ba7043d5ec..bafbe38a43 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_Commands.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_Commands.ts @@ -163,13 +163,16 @@ export function PoxCommands( fc.record({ wallet: fc.constantFrom(...wallets.values()), delegateTo: fc.constantFrom(...wallets.values()), - untilBurnHt: fc.integer({ min: 1 }), + untilBurnHt: fc.oneof( + fc.constant(Cl.none()), + fc.integer({ min: 1 }).map((value) => Cl.some(Cl.uint(value))), + ), amount: fc.bigInt({ min: 0n, max: 100_000_000_000_000n }), }).map(( r: { wallet: Wallet; delegateTo: Wallet; - untilBurnHt: number; + untilBurnHt: OptionalCV; amount: bigint; }, ) => diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackExtendCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackExtendCommand.ts index cfd385cf5a..2875551342 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackExtendCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackExtendCommand.ts @@ -83,7 +83,8 @@ export class DelegateStackExtendCommand implements PoxCommand { stackerWallet.hasDelegated === true && stackerWallet.isStacking === true && stackerWallet.delegatedTo === this.operator.stxAddress && - stackerWallet.delegatedUntilBurnHt >= newUnlockHeight && + (stackerWallet.delegatedUntilBurnHt === undefined || + stackerWallet.delegatedUntilBurnHt >= newUnlockHeight) && stackerWallet.delegatedMaxAmount >= stackedAmount && operatorWallet.poolMembers.includes(this.stacker.stxAddress) && operatorWallet.lockedAddresses.includes(this.stacker.stxAddress) && diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackStxCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackStxCommand.ts index 456983807f..e3d9dd25c1 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackStxCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStackStxCommand.ts @@ -94,7 +94,8 @@ export class DelegateStackStxCommand implements PoxCommand { Number(this.amountUstx) <= stackerWallet.ustxBalance && Number(this.amountUstx) >= model.stackingMinimum && operatorWallet.poolMembers.includes(this.stacker.stxAddress) && - this.unlockBurnHt <= stackerWallet.delegatedUntilBurnHt + (stackerWallet.delegatedUntilBurnHt === undefined || + this.unlockBurnHt <= stackerWallet.delegatedUntilBurnHt) ); } diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStxCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStxCommand.ts index 4a12b0140d..e70d466c9d 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStxCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DelegateStxCommand.ts @@ -7,7 +7,15 @@ import { } from "./pox_CommandModel.ts"; import { poxAddressToTuple } from "@stacks/stacking"; import { expect } from "vitest"; -import { boolCV, Cl } from "@stacks/transactions"; +import { + boolCV, + Cl, + ClarityType, + cvToValue, + isClarityType, + OptionalCV, + UIntCV, +} from "@stacks/transactions"; /** * The `DelegateStxCommand` delegates STX for stacking within PoX-4. This @@ -22,7 +30,7 @@ import { boolCV, Cl } from "@stacks/transactions"; export class DelegateStxCommand implements PoxCommand { readonly wallet: Wallet; readonly delegateTo: Wallet; - readonly untilBurnHt: number; + readonly untilBurnHt: OptionalCV; readonly amount: bigint; /** @@ -37,7 +45,7 @@ export class DelegateStxCommand implements PoxCommand { constructor( wallet: Wallet, delegateTo: Wallet, - untilBurnHt: number, + untilBurnHt: OptionalCV, amount: bigint, ) { this.wallet = wallet; @@ -74,7 +82,7 @@ export class DelegateStxCommand implements PoxCommand { // (delegate-to principal) Cl.principal(this.delegateTo.stxAddress), // (until-burn-ht (optional uint)) - Cl.some(Cl.uint(this.untilBurnHt)), + this.untilBurnHt, // (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) })) Cl.some(poxAddressToTuple(this.delegateTo.btcAddress)), ], @@ -93,7 +101,10 @@ export class DelegateStxCommand implements PoxCommand { wallet.hasDelegated = true; wallet.delegatedTo = this.delegateTo.stxAddress; wallet.delegatedMaxAmount = amountUstx; - wallet.delegatedUntilBurnHt = this.untilBurnHt; + wallet.delegatedUntilBurnHt = + isClarityType(this.untilBurnHt, ClarityType.OptionalNone) + ? undefined + : Number(cvToValue(this.untilBurnHt).value); wallet.delegatedPoxAddress = this.delegateTo.btcAddress; delegatedWallet.poolMembers.push(this.wallet.stxAddress); diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DisallowContractCallerCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DisallowContractCallerCommand.ts index 09618db49c..16b830b5fb 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DisallowContractCallerCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_DisallowContractCallerCommand.ts @@ -42,7 +42,9 @@ export class DisallowContractCallerCommand implements PoxCommand { this.callerToDisallow.stxAddress, )!; return ( - stacker.allowedContractCaller === this.callerToDisallow.stxAddress && + stacker.allowedContractCallers.includes( + this.callerToDisallow.stxAddress, + ) && callerToDisallow.callerAllowedBy.includes( this.stacker.stxAddress, ) === @@ -76,7 +78,12 @@ export class DisallowContractCallerCommand implements PoxCommand { // Update model so that we know that the stacker has revoked stacking // allowance. const stacker = model.stackers.get(this.stacker.stxAddress)!; - stacker.allowedContractCaller = ""; + const callerToDisallowIndex = stacker.allowedContractCallers.indexOf( + this.callerToDisallow.stxAddress, + ); + + expect(callerToDisallowIndex).toBeGreaterThan(-1); + stacker.allowedContractCallers.splice(callerToDisallowIndex, 1); // Remove the operator from the caller to disallow's allowance list. const walletIndexAllowedByList = callerToDisallow.callerAllowedBy.indexOf( diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts index 1c30e3d569..c39a1a5e42 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts @@ -15,6 +15,7 @@ import { Cl, someCV, tupleCV } from "@stacks/transactions"; * * Constraints for running this command include: * - The `Stacker` has to currently be delegating. + * - The `Stacker`'s delegation must not be expired. */ export class RevokeDelegateStxCommand implements PoxCommand { readonly wallet: Wallet; @@ -31,10 +32,13 @@ export class RevokeDelegateStxCommand implements PoxCommand { check(model: Readonly): boolean { // Constraints for running this command include: // - The Stacker has to currently be delegating. - + // - The Stacker's delegation must not be expired. + const stacker = model.stackers.get(this.wallet.stxAddress)!; return ( model.stackingMinimum > 0 && - model.stackers.get(this.wallet.stxAddress)!.hasDelegated === true + stacker.hasDelegated === true && + (stacker.delegatedUntilBurnHt === undefined || + stacker.delegatedUntilBurnHt > model.burnBlockHeight) ); } @@ -43,6 +47,9 @@ export class RevokeDelegateStxCommand implements PoxCommand { const wallet = model.stackers.get(this.wallet.stxAddress)!; const operatorWallet = model.stackers.get(wallet.delegatedTo)!; + const expectedUntilBurnHt = wallet.delegatedUntilBurnHt === undefined + ? Cl.none() + : Cl.some(Cl.uint(wallet.delegatedUntilBurnHt)); // Act const revokeDelegateStx = real.network.callPublicFn( @@ -63,7 +70,7 @@ export class RevokeDelegateStxCommand implements PoxCommand { "pox-addr": Cl.some( poxAddressToTuple(wallet.delegatedPoxAddress || ""), ), - "until-burn-ht": Cl.some(Cl.uint(wallet.delegatedUntilBurnHt)), + "until-burn-ht": expectedUntilBurnHt, }), ), ); @@ -73,6 +80,8 @@ export class RevokeDelegateStxCommand implements PoxCommand { // Update model so that we know this wallet is not delegating anymore. // This is important in order to prevent the test from revoking the // delegation multiple times with the same address. + // We update delegatedUntilBurnHt to 0, and not undefined. Undefined + // stands for indefinite delegation. wallet.hasDelegated = false; wallet.delegatedTo = ""; wallet.delegatedUntilBurnHt = 0; diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitAuthCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitAuthCommand.ts index 5312679833..62622f4bd3 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitAuthCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitAuthCommand.ts @@ -9,6 +9,7 @@ import { poxAddressToTuple } from "@stacks/stacking"; import { expect } from "vitest"; import { Cl } from "@stacks/transactions"; import { currentCycle } from "./pox_Commands.ts"; +import { tx } from "@hirosystems/clarinet-sdk"; /** * The `StackAggregationCommitAuthCommand` allows an operator to commit @@ -60,54 +61,61 @@ export class StackAggregationCommitAuthCommand implements PoxCommand { const operatorWallet = model.stackers.get(this.operator.stxAddress)!; const committedAmount = operatorWallet.amountToCommit; - const { result: setSignature } = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "set-signer-key-authorization", - [ - // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) - poxAddressToTuple(this.operator.btcAddress), - // (period uint) - Cl.uint(1), - // (reward-cycle uint) - Cl.uint(currentRewCycle + 1), - // (topic (string-ascii 14)) - Cl.stringAscii("agg-commit"), - // (signer-key (buff 33)) - Cl.bufferFromHex(this.operator.signerPubKey), - // (allowed bool) - Cl.bool(true), - // (max-amount uint) - Cl.uint(committedAmount), - // (auth-id uint) - Cl.uint(this.authId), - ], - this.operator.stxAddress, - ); - expect(setSignature).toBeOk(Cl.bool(true)); - // Act - const stackAggregationCommit = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "stack-aggregation-commit", - [ - // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) - poxAddressToTuple(this.operator.btcAddress), - // (reward-cycle uint) - Cl.uint(currentRewCycle + 1), - // (signer-sig (optional (buff 65))) - Cl.none(), - // (signer-key (buff 33)) - Cl.bufferFromHex(this.operator.signerPubKey), - // (max-amount uint) - Cl.uint(committedAmount), - // (auth-id uint) - Cl.uint(this.authId), - ], - this.operator.stxAddress, - ); + + // Include the authorization and the `stack-aggregation-commit` transactions + // in a single block. This way we ensure both the authorization and the + // stack-aggregation-commit transactions are called during the same reward + // cycle, so the authorization currentRewCycle param is relevant for the + // upcoming stack-aggregation-commit call. + const block = real.network.mineBlock([ + tx.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "set-signer-key-authorization", + [ + // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + poxAddressToTuple(this.operator.btcAddress), + // (period uint) + Cl.uint(1), + // (reward-cycle uint) + Cl.uint(currentRewCycle + 1), + // (topic (string-ascii 14)) + Cl.stringAscii("agg-commit"), + // (signer-key (buff 33)) + Cl.bufferFromHex(this.operator.signerPubKey), + // (allowed bool) + Cl.bool(true), + // (max-amount uint) + Cl.uint(committedAmount), + // (auth-id uint) + Cl.uint(this.authId), + ], + this.operator.stxAddress, + ), + tx.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "stack-aggregation-commit", + [ + // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + poxAddressToTuple(this.operator.btcAddress), + // (reward-cycle uint) + Cl.uint(currentRewCycle + 1), + // (signer-sig (optional (buff 65))) + Cl.none(), + // (signer-key (buff 33)) + Cl.bufferFromHex(this.operator.signerPubKey), + // (max-amount uint) + Cl.uint(committedAmount), + // (auth-id uint) + Cl.uint(this.authId), + ], + this.operator.stxAddress, + ), + ]); // Assert - expect(stackAggregationCommit.result).toBeOk(Cl.bool(true)); + expect(block[0].result).toBeOk(Cl.bool(true)); + expect(block[1].result).toBeOk(Cl.bool(true)); operatorWallet.amountToCommit -= committedAmount; operatorWallet.committedRewCycleIndexes.push(model.nextRewardSetIndex); diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitIndexedAuthCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitIndexedAuthCommand.ts index dfe7f2beef..cfafccc674 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitIndexedAuthCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackAggregationCommitIndexedAuthCommand.ts @@ -9,6 +9,7 @@ import { poxAddressToTuple } from "@stacks/stacking"; import { expect } from "vitest"; import { Cl } from "@stacks/transactions"; import { currentCycle } from "./pox_Commands.ts"; +import { tx } from "@hirosystems/clarinet-sdk"; /** * The `StackAggregationCommitIndexedAuthCommand` allows an operator to @@ -65,54 +66,61 @@ export class StackAggregationCommitIndexedAuthCommand implements PoxCommand { const operatorWallet = model.stackers.get(this.operator.stxAddress)!; const committedAmount = operatorWallet.amountToCommit; - const { result: setSignature } = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "set-signer-key-authorization", - [ - // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) - poxAddressToTuple(this.operator.btcAddress), - // (period uint) - Cl.uint(1), - // (reward-cycle uint) - Cl.uint(currentRewCycle + 1), - // (topic (string-ascii 14)) - Cl.stringAscii("agg-commit"), - // (signer-key (buff 33)) - Cl.bufferFromHex(this.operator.signerPubKey), - // (allowed bool) - Cl.bool(true), - // (max-amount uint) - Cl.uint(committedAmount), - // (auth-id uint) - Cl.uint(this.authId), - ], - this.operator.stxAddress, - ); - expect(setSignature).toBeOk(Cl.bool(true)); - // Act - const stackAggregationCommitIndexed = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "stack-aggregation-commit-indexed", - [ - // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) - poxAddressToTuple(this.operator.btcAddress), - // (reward-cycle uint) - Cl.uint(currentRewCycle + 1), - // (signer-sig (optional (buff 65))) - Cl.none(), - // (signer-key (buff 33)) - Cl.bufferFromHex(this.operator.signerPubKey), - // (max-amount uint) - Cl.uint(committedAmount), - // (auth-id uint) - Cl.uint(this.authId), - ], - this.operator.stxAddress, - ); + + // Include the authorization and the `stack-aggregation-commit-indexed` + // transactions in a single block. This way we ensure both the authorization + // and the stack-aggregation-commit-indexed transactions are called during + // the same reward cycle, so the authorization currentRewCycle param is + // relevant for the upcoming stack-aggregation-commit-indexed call. + const block = real.network.mineBlock([ + tx.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "set-signer-key-authorization", + [ + // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + poxAddressToTuple(this.operator.btcAddress), + // (period uint) + Cl.uint(1), + // (reward-cycle uint) + Cl.uint(currentRewCycle + 1), + // (topic (string-ascii 14)) + Cl.stringAscii("agg-commit"), + // (signer-key (buff 33)) + Cl.bufferFromHex(this.operator.signerPubKey), + // (allowed bool) + Cl.bool(true), + // (max-amount uint) + Cl.uint(committedAmount), + // (auth-id uint) + Cl.uint(this.authId), + ], + this.operator.stxAddress, + ), + tx.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "stack-aggregation-commit-indexed", + [ + // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + poxAddressToTuple(this.operator.btcAddress), + // (reward-cycle uint) + Cl.uint(currentRewCycle + 1), + // (signer-sig (optional (buff 65))) + Cl.none(), + // (signer-key (buff 33)) + Cl.bufferFromHex(this.operator.signerPubKey), + // (max-amount uint) + Cl.uint(committedAmount), + // (auth-id uint) + Cl.uint(this.authId), + ], + this.operator.stxAddress, + ), + ]); // Assert - expect(stackAggregationCommitIndexed.result).toBeOk( + expect(block[0].result).toBeOk(Cl.bool(true)); + expect(block[1].result).toBeOk( Cl.uint(model.nextRewardSetIndex), ); diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackExtendAuthCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackExtendAuthCommand.ts index a7dbf49cbb..fa796673ea 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackExtendAuthCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackExtendAuthCommand.ts @@ -7,6 +7,7 @@ import { } from "./pox_Commands"; import { Cl, ClarityType, isClarityType } from "@stacks/transactions"; import { assert, expect } from "vitest"; +import { tx } from "@hirosystems/clarinet-sdk"; export class StackExtendAuthCommand implements PoxCommand { readonly wallet: Wallet; @@ -77,51 +78,6 @@ export class StackExtendAuthCommand implements PoxCommand { const stacker = model.stackers.get(this.wallet.stxAddress)!; - const { result: setAuthorization } = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "set-signer-key-authorization", - [ - // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) - poxAddressToTuple(this.wallet.btcAddress), - // (period uint) - Cl.uint(this.extendCount), - // (reward-cycle uint) - Cl.uint(currentRewCycle), - // (topic (string-ascii 14)) - Cl.stringAscii("stack-extend"), - // (signer-key (buff 33)) - Cl.bufferFromHex(this.wallet.signerPubKey), - // (allowed bool) - Cl.bool(true), - // (max-amount uint) - Cl.uint(stacker.amountLocked), - // (auth-id uint) - Cl.uint(this.authId), - ], - this.wallet.stxAddress, - ); - - expect(setAuthorization).toBeOk(Cl.bool(true)); - const stackExtend = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "stack-extend", - [ - // (extend-count uint) - Cl.uint(this.extendCount), - // (pox-addr { version: (buff 1), hashbytes: (buff 32) }) - poxAddressToTuple(this.wallet.btcAddress), - // (signer-sig (optional (buff 65))) - Cl.none(), - // (signer-key (buff 33)) - Cl.bufferFromHex(this.wallet.signerPubKey), - // (max-amount uint) - Cl.uint(stacker.amountLocked), - // (auth-id uint) - Cl.uint(this.authId), - ], - this.wallet.stxAddress, - ); - const { result: firstExtendCycle } = real.network.callReadOnlyFn( "ST000000000000000000002AMW42H.pox-4", "burn-height-to-reward-cycle", @@ -143,7 +99,57 @@ export class StackExtendAuthCommand implements PoxCommand { const newUnlockHeight = extendedUnlockHeight.value; - expect(stackExtend.result).toBeOk( + // Include the authorization and the `stack-extend` transactions in a single + // block. This way we ensure both the authorization and the stack-extend + // transactions are called during the same reward cycle, so the authorization + // currentRewCycle param is relevant for the upcoming stack-extend call. + const block = real.network.mineBlock([ + tx.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "set-signer-key-authorization", + [ + // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + poxAddressToTuple(this.wallet.btcAddress), + // (period uint) + Cl.uint(this.extendCount), + // (reward-cycle uint) + Cl.uint(currentRewCycle), + // (topic (string-ascii 14)) + Cl.stringAscii("stack-extend"), + // (signer-key (buff 33)) + Cl.bufferFromHex(this.wallet.signerPubKey), + // (allowed bool) + Cl.bool(true), + // (max-amount uint) + Cl.uint(stacker.amountLocked), + // (auth-id uint) + Cl.uint(this.authId), + ], + this.wallet.stxAddress, + ), + tx.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "stack-extend", + [ + // (extend-count uint) + Cl.uint(this.extendCount), + // (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + poxAddressToTuple(this.wallet.btcAddress), + // (signer-sig (optional (buff 65))) + Cl.none(), + // (signer-key (buff 33)) + Cl.bufferFromHex(this.wallet.signerPubKey), + // (max-amount uint) + Cl.uint(stacker.amountLocked), + // (auth-id uint) + Cl.uint(this.authId), + ], + this.wallet.stxAddress, + ), + ]); + + expect(block[0].result).toBeOk(Cl.bool(true)); + expect(block[1].result).toBeOk( Cl.tuple({ stacker: Cl.principal(this.wallet.stxAddress), "unlock-burn-height": Cl.uint(newUnlockHeight), diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxAuthCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxAuthCommand.ts index 108f0956b5..53f34ca0bb 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxAuthCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxAuthCommand.ts @@ -15,6 +15,7 @@ import { isClarityType, } from "@stacks/transactions"; import { currentCycle } from "./pox_Commands.ts"; +import { tx } from "@hirosystems/clarinet-sdk"; /** * The `StackStxAuthCommand` locks STX for stacking within PoX-4. This self-service @@ -80,31 +81,6 @@ export class StackStxAuthCommand implements PoxCommand { // generated number passed to the constructor of this class. const maxAmount = model.stackingMinimum * this.margin; - const { result: setAuthorization } = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "set-signer-key-authorization", - [ - // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) - poxAddressToTuple(this.wallet.btcAddress), - // (period uint) - Cl.uint(this.period), - // (reward-cycle uint) - Cl.uint(currentRewCycle), - // (topic (string-ascii 14)) - Cl.stringAscii("stack-stx"), - // (signer-key (buff 33)) - Cl.bufferFromHex(this.wallet.signerPubKey), - // (allowed bool) - Cl.bool(true), - // (max-amount uint) - Cl.uint(maxAmount), - // (auth-id uint) - Cl.uint(this.authId), - ], - this.wallet.stxAddress, - ); - - expect(setAuthorization).toBeOk(Cl.bool(true)); const burnBlockHeightCV = real.network.runSnippet("burn-block-height"); const burnBlockHeight = Number( cvToValue(burnBlockHeightCV as ClarityValue), @@ -115,17 +91,41 @@ export class StackStxAuthCommand implements PoxCommand { // signer key. const amountUstx = maxAmount; - // Act - const stackStx = real.network.callPublicFn( - "ST000000000000000000002AMW42H.pox-4", - "stack-stx", - [ + // Include the authorization and the `stack-stx` transactions in a single + // block. This way we ensure both the authorization and the stack-stx + // transactions are called during the same reward cycle, so the authorization + // currentRewCycle param is relevant for the upcoming stack-stx call. + const block = real.network.mineBlock([ + tx.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "set-signer-key-authorization", + [ + // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + poxAddressToTuple(this.wallet.btcAddress), + // (period uint) + Cl.uint(this.period), + // (reward-cycle uint) + Cl.uint(currentRewCycle), + // (topic (string-ascii 14)) + Cl.stringAscii("stack-stx"), + // (signer-key (buff 33)) + Cl.bufferFromHex(this.wallet.signerPubKey), + // (allowed bool) + Cl.bool(true), + // (max-amount uint) + Cl.uint(maxAmount), + // (auth-id uint) + Cl.uint(this.authId), + ], + this.wallet.stxAddress, + ), + tx.callPublicFn("ST000000000000000000002AMW42H.pox-4", "stack-stx", [ // (amount-ustx uint) Cl.uint(amountUstx), // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) poxAddressToTuple(this.wallet.btcAddress), // (start-burn-ht uint) - Cl.uint(burnBlockHeight), + Cl.uint(burnBlockHeight + 1), // (lock-period uint) Cl.uint(this.period), // (signer-sig (optional (buff 65))) @@ -136,9 +136,10 @@ export class StackStxAuthCommand implements PoxCommand { Cl.uint(maxAmount), // (auth-id uint) Cl.uint(this.authId), - ], - this.wallet.stxAddress, - ); + ], this.wallet.stxAddress), + ]); + + expect(block[0].result).toBeOk(Cl.bool(true)); const { result: rewardCycle } = real.network.callReadOnlyFn( "ST000000000000000000002AMW42H.pox-4", @@ -156,8 +157,7 @@ export class StackStxAuthCommand implements PoxCommand { ); assert(isClarityType(unlockBurnHeight, ClarityType.UInt)); - // Assert - expect(stackStx.result).toBeOk( + expect(block[1].result).toBeOk( Cl.tuple({ "lock-amount": Cl.uint(amountUstx), "signer-key": Cl.bufferFromHex(this.wallet.signerPubKey), diff --git a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxSigCommand.ts b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxSigCommand.ts index baa87015a1..100d84a6e0 100644 --- a/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxSigCommand.ts +++ b/contrib/boot-contracts-stateful-prop-tests/tests/pox-4/pox_StackStxSigCommand.ts @@ -123,7 +123,7 @@ export class StackStxSigCommand implements PoxCommand { // (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) poxAddressToTuple(this.wallet.btcAddress), // (start-burn-ht uint) - Cl.uint(burnBlockHeight), + Cl.uint(burnBlockHeight + 1), // (lock-period uint) Cl.uint(this.period), // (signer-sig (optional (buff 65)))