Skip to content

Commit

Permalink
Merge pull request #4820 from stacks-network/feat/pox-4-stateful-prop…
Browse files Browse the repository at this point in the history
…-updates

PoX-4 stateful property tests updates
  • Loading branch information
moodmosaic committed May 31, 2024
2 parents b5138d0 + c24aee1 commit aede203
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 215 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
}])),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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[];
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<UIntCV>;
amount: bigint;
},
) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<UIntCV>;
readonly amount: bigint;

/**
Expand All @@ -37,7 +45,7 @@ export class DelegateStxCommand implements PoxCommand {
constructor(
wallet: Wallet,
delegateTo: Wallet,
untilBurnHt: number,
untilBurnHt: OptionalCV<UIntCV>,
amount: bigint,
) {
this.wallet = wallet;
Expand Down Expand Up @@ -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)),
],
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) ===
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,10 +32,13 @@ export class RevokeDelegateStxCommand implements PoxCommand {
check(model: Readonly<Stub>): 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)
);
}

Expand All @@ -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(
Expand All @@ -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,
}),
),
);
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit aede203

Please sign in to comment.