From 4d94cbc5c6e9def6a769e9297257b1b21b696caa Mon Sep 17 00:00:00 2001 From: BowTiedRadone Date: Fri, 22 Mar 2024 23:04:13 +0200 Subject: [PATCH] test(pox-4): Add RevokeDelegateStxCommand --- .../tests/pox-4/pox-4.stateful-prop.test.ts | 1 + .../tests/pox-4/pox_CommandModel.ts | 1 + .../tests/pox-4/pox_Commands.ts | 19 ++++ .../tests/pox-4/pox_DelegateStxCommand.ts | 1 + .../pox-4/pox_RevokeDelegateStxCommand.ts | 96 +++++++++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 contrib/core-contract-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts diff --git a/contrib/core-contract-tests/tests/pox-4/pox-4.stateful-prop.test.ts b/contrib/core-contract-tests/tests/pox-4/pox-4.stateful-prop.test.ts index c18c11e6c9..805ea2f53a 100644 --- a/contrib/core-contract-tests/tests/pox-4/pox-4.stateful-prop.test.ts +++ b/contrib/core-contract-tests/tests/pox-4/pox-4.stateful-prop.test.ts @@ -67,6 +67,7 @@ describe("PoX-4 invariant tests", () => { hasPoolMembers: [], delegatedTo: "", delegatedMaxAmount: 0, + delegatedUntilBurnHt: 0, amountLocked: 0, amountUnlocked: initialUstxBalance, unlockHeight: 0, diff --git a/contrib/core-contract-tests/tests/pox-4/pox_CommandModel.ts b/contrib/core-contract-tests/tests/pox-4/pox_CommandModel.ts index f88d64ccd4..138027d5d5 100644 --- a/contrib/core-contract-tests/tests/pox-4/pox_CommandModel.ts +++ b/contrib/core-contract-tests/tests/pox-4/pox_CommandModel.ts @@ -28,6 +28,7 @@ export type Wallet = { hasPoolMembers: StxAddress[]; delegatedTo: StxAddress; delegatedMaxAmount: number; + delegatedUntilBurnHt: number; amountLocked: number; amountUnlocked: number; unlockHeight: number; diff --git a/contrib/core-contract-tests/tests/pox-4/pox_Commands.ts b/contrib/core-contract-tests/tests/pox-4/pox_Commands.ts index 88fbea9a6b..92d2b87ae5 100644 --- a/contrib/core-contract-tests/tests/pox-4/pox_Commands.ts +++ b/contrib/core-contract-tests/tests/pox-4/pox_Commands.ts @@ -7,6 +7,7 @@ import { DelegateStxCommand } from "./pox_DelegateStxCommand"; import { DelegateStackStxCommand } from "./pox_DelegateStackStxCommand"; import { Simnet } from "@hirosystems/clarinet-sdk"; import { Cl, cvToValue } from "@stacks/transactions"; +import { RevokeDelegateStxCommand } from "./pox_RevokeDelegateStxCommand"; export function PoxCommands( wallets: Map, network: Simnet, @@ -66,6 +67,24 @@ export function PoxCommands( r.amount, ) ), + // RevokeDelegateStxCommand + fc.record({ + wallet: fc.constantFrom(...wallets.values()), + delegateTo: fc.constantFrom(...wallets.values()), + untilBurnHt: fc.integer({ min: 1 }), + amount: fc.bigInt({ min:0n, max: 100_000_000_000_000n }), + }).map(( + r: { + wallet: Wallet; + delegateTo: Wallet; + untilBurnHt: number; + amount: bigint; + }, + ) => + new RevokeDelegateStxCommand( + r.wallet + ) + ), // DelegateStackStxCommand fc.record({ operator: fc.constantFrom(...wallets.values()), diff --git a/contrib/core-contract-tests/tests/pox-4/pox_DelegateStxCommand.ts b/contrib/core-contract-tests/tests/pox-4/pox_DelegateStxCommand.ts index 08d2988293..28fd596643 100644 --- a/contrib/core-contract-tests/tests/pox-4/pox_DelegateStxCommand.ts +++ b/contrib/core-contract-tests/tests/pox-4/pox_DelegateStxCommand.ts @@ -83,6 +83,7 @@ export class DelegateStxCommand implements PoxCommand { wallet.hasDelegated = true; wallet.delegatedTo = this.delegateTo.stxAddress; wallet.delegatedMaxAmount = amountUstx; + wallet.delegatedUntilBurnHt = this.untilBurnHt delegatedWallet.hasPoolMembers.push(wallet.stxAddress); // Log to console for debugging purposes. This is not necessary for the diff --git a/contrib/core-contract-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts b/contrib/core-contract-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts new file mode 100644 index 0000000000..5882402e7e --- /dev/null +++ b/contrib/core-contract-tests/tests/pox-4/pox_RevokeDelegateStxCommand.ts @@ -0,0 +1,96 @@ +import { PoxCommand, Real, Stub, Wallet } from "./pox_CommandModel.ts"; +import { poxAddressToTuple } from "@stacks/stacking"; +import { expect } from "vitest"; +import { Cl, someCV, tupleCV } from "@stacks/transactions"; + +/** + * The `RevokeDelegateStxCommand` revokes the delegation for stacking within PoX-4. + * + * Constraints for running this command include: + * - The `Stacker` has to currently be delegating. + */ +export class RevokeDelegateStxCommand implements PoxCommand { + readonly wallet: Wallet; + + /** + * Constructs a RevokeDelegateStxCommand to revoke delegate uSTX for stacking. + * + * @param wallet - Represents the Stacker's wallet. + */ + constructor(wallet: Wallet) { + this.wallet = wallet; + } + + check(model: Readonly): boolean { + // Constraints for running this command include: + // - The Stacker has to currently be delegating. + return ( + model.stackingMinimum > 0 && + model.wallets.get(this.wallet.stxAddress)!.hasDelegated === true + ); + } + + run(model: Stub, real: Real): void { + // Get the Operator's wallet + const operatorWallet = model.wallets.get(this.wallet.delegatedTo)!; + + // Act + const revokeDelegateStx = real.network.callPublicFn( + "ST000000000000000000002AMW42H.pox-4", + "revoke-delegate-stx", + [], + this.wallet.stxAddress, + ); + + // Assert + expect(revokeDelegateStx.result).toBeOk( + someCV( + tupleCV({ + "amount-ustx": Cl.uint(this.wallet.delegatedMaxAmount), + "delegated-to": Cl.principal( + operatorWallet.stxAddress || "", + ), + "pox-addr": Cl.some( + poxAddressToTuple(operatorWallet.btcAddress || ""), + ), + "until-burn-ht": Cl.some(Cl.uint(this.wallet.delegatedUntilBurnHt)), + }), + ), + ); + + // Get the Stacker's wallet from the model and update the two wallets involved with the new state. + const wallet = model.wallets.get(this.wallet.stxAddress)!; + // 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. + wallet.hasDelegated = false; + wallet.delegatedTo = ""; + wallet.delegatedUntilBurnHt = 0; + wallet.delegatedMaxAmount = 0; + + // Remove the Stacker from the Pool Operator's pool members list + let walletIndexInDelegatorsList = operatorWallet.hasPoolMembers.indexOf( + wallet.stxAddress, + ); + expect(walletIndexInDelegatorsList).toBeGreaterThan(-1); + operatorWallet.hasPoolMembers.splice(walletIndexInDelegatorsList, 1); + + // 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. + console.info( + `✓ ${ + this.wallet.label.padStart( + 8, + " ", + ) + } ${"revoke-delegate-stx".padStart(34, " ")}`, + ); + } + + toString() { + // fast-check will call toString() in case of errors, e.g. property failed. + // It will then make a minimal counterexample, a process called 'shrinking' + // https://github.com/dubzzz/fast-check/issues/2864#issuecomment-1098002642 + return `${this.wallet.stxAddress} revoke-delegate-stx`; + } +}