Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stateful property-tests for the error/failure paths in PoX-4 #4842

Merged
merged 73 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
050418c
feat(pox-4-tests): add check function delegate to PoxCommand-derived …
moodmosaic May 21, 2024
6f02aeb
feat(pox-4-tests): add check function delegate to PoxCommand-derived …
moodmosaic May 22, 2024
04e7fe6
Remove command tracking from the command's `run` method
BowTiedRadone May 22, 2024
a243d3b
Pass the incremented burn height when calling `stack-stx`
BowTiedRadone May 22, 2024
7a0c1fd
Add the unhappy path cases for `StackStxXCommand_Err`
BowTiedRadone May 22, 2024
bd159e3
Remove `StackStxXCommand.ts` from statistics
BowTiedRadone May 22, 2024
7405ab0
Add unhappy path for `revoke-delegate-stx`
BowTiedRadone May 24, 2024
10d0b9b
Remove `RevokeDelegateStxCommand_Err` from statistics
BowTiedRadone May 24, 2024
0c83016
Add unhappy path for `delegate-stx`
BowTiedRadone May 24, 2024
48b9032
Remove `DelegateStxCommand_Err` from statistics
BowTiedRadone May 24, 2024
b38c224
Use simnet `mineBlock` inside `StackStxAuthCommand_Err`
BowTiedRadone May 24, 2024
93bfa6c
Add the unhappy path cases for `StackAggCommitSigCommand_Err`
BowTiedRadone May 27, 2024
16311d8
Remove `StackAggregationCommitSigCommand_Err` from statistics
BowTiedRadone May 27, 2024
a8e7ea3
Add the unhappy path cases for `StackAggCommitAuthCommand_Err`
BowTiedRadone May 27, 2024
b592998
Remove `StackAggregationCommitAuthCommand_Err` from statistics
BowTiedRadone May 27, 2024
41c0386
Use strict equality inside generator
BowTiedRadone May 27, 2024
788f986
Order statistics alphabetically
BowTiedRadone May 27, 2024
23e6bc1
Add the unhappy path cases for `StackAggCommitIndexedSigCommand_Err`
BowTiedRadone May 27, 2024
74b4a92
Add the unhappy path cases for `StackAggCommitIndexedAuthCommand_Err`
BowTiedRadone May 28, 2024
91f99db
Remove all files containing `_Err` from command tracking
BowTiedRadone May 28, 2024
48a759a
Add one unhappy path case for `StackAggIncreaseCommand_Err`
BowTiedRadone May 28, 2024
70990a4
Format using `deno` according to the other generators
BowTiedRadone May 28, 2024
c66e679
Add unhappy path cases for `DelegateStackStxCommand_Err`
BowTiedRadone May 29, 2024
17fddaf
Add unhappy path cases for `StackIncreaseSigCommand_Err`
BowTiedRadone May 29, 2024
2a7135f
Add unhappy path cases for `StackIncreaseAuthCommand_Err`
BowTiedRadone May 30, 2024
9a4f775
Add unhappy path cases for `StackExtendSigCommand_Err`
BowTiedRadone May 30, 2024
eaf9274
Add unhappy path cases for `StackExtendAuthCommand_Err`
BowTiedRadone May 30, 2024
be39b1a
Add unhappy path cases for `DelegateStackExtendCommand_Err`
BowTiedRadone May 31, 2024
64e2ece
feat(pox-4-tests): add check function delegate to PoxCommand-derived …
moodmosaic May 21, 2024
7f4b536
feat(pox-4-tests): add check function delegate to PoxCommand-derived …
moodmosaic May 22, 2024
124abd4
Remove command tracking from the command's `run` method
BowTiedRadone May 22, 2024
dfb7230
Pass the incremented burn height when calling `stack-stx`
BowTiedRadone May 22, 2024
1de982d
Add the unhappy path cases for `StackStxXCommand_Err`
BowTiedRadone May 22, 2024
be6966c
Remove `StackStxXCommand.ts` from statistics
BowTiedRadone May 22, 2024
544382c
Add unhappy path for `revoke-delegate-stx`
BowTiedRadone May 24, 2024
3227bc4
Remove `RevokeDelegateStxCommand_Err` from statistics
BowTiedRadone May 24, 2024
e7610e3
Add unhappy path for `delegate-stx`
BowTiedRadone May 24, 2024
232d388
Remove `DelegateStxCommand_Err` from statistics
BowTiedRadone May 24, 2024
bdcee6b
Use simnet `mineBlock` inside `StackStxAuthCommand_Err`
BowTiedRadone May 24, 2024
190d7a5
Add the unhappy path cases for `StackAggCommitSigCommand_Err`
BowTiedRadone May 27, 2024
b0f8464
Remove `StackAggregationCommitSigCommand_Err` from statistics
BowTiedRadone May 27, 2024
367e89a
Add the unhappy path cases for `StackAggCommitAuthCommand_Err`
BowTiedRadone May 27, 2024
abdf8bd
Remove `StackAggregationCommitAuthCommand_Err` from statistics
BowTiedRadone May 27, 2024
3ab6e61
Use strict equality inside generator
BowTiedRadone May 27, 2024
91a7c43
Order statistics alphabetically
BowTiedRadone May 27, 2024
ab686c4
Add the unhappy path cases for `StackAggCommitIndexedSigCommand_Err`
BowTiedRadone May 27, 2024
50475b7
Add the unhappy path cases for `StackAggCommitIndexedAuthCommand_Err`
BowTiedRadone May 28, 2024
7173ad6
Remove all files containing `_Err` from command tracking
BowTiedRadone May 28, 2024
c6ff82d
Add one unhappy path case for `StackAggIncreaseCommand_Err`
BowTiedRadone May 28, 2024
a267a04
Format using `deno` according to the other generators
BowTiedRadone May 28, 2024
1bd9c78
Add unhappy path cases for `DelegateStackStxCommand_Err`
BowTiedRadone May 29, 2024
b66b19c
Add unhappy path cases for `StackIncreaseSigCommand_Err`
BowTiedRadone May 29, 2024
50bd1b5
Add unhappy path cases for `StackIncreaseAuthCommand_Err`
BowTiedRadone May 30, 2024
4c2f3b6
Add unhappy path cases for `StackExtendSigCommand_Err`
BowTiedRadone May 30, 2024
add9d55
Add unhappy path cases for `StackExtendAuthCommand_Err`
BowTiedRadone May 30, 2024
3f3da16
Add unhappy path cases for `DelegateStackExtendCommand_Err`
BowTiedRadone May 31, 2024
8a3dc45
Merge branch 'feat/pox-4-stateful-prop-tests-check-err' of https://gi…
BowTiedRadone May 31, 2024
115a385
Update `err_Commands` to include the `delegatedUntilBurnHt` none branch
BowTiedRadone May 31, 2024
6eb8eec
Add unhappy path cases for `DelegateStackIncreaseCommand_Err`
BowTiedRadone May 31, 2024
da87ec8
Order imports inside `err_Commands`
BowTiedRadone May 31, 2024
9365892
Add unhappy path case for `DisallowContractCallerCommand_Err`
BowTiedRadone May 31, 2024
f03bedf
Add tree logging for test-runs statistics
BowTiedRadone Jun 3, 2024
dc9e019
Fix PoX-4 stateful prop tests comments
BowTiedRadone Jun 10, 2024
f460834
Remove types from `err_Commands` `check` functions
BowTiedRadone Jun 18, 2024
452d7bd
Add an additional condition inside `DelegateStackIncreaseCommand`
BowTiedRadone Jun 20, 2024
90d4a2b
Fix typo in `DelegateStackStxCommand_Err` logging
BowTiedRadone Jun 20, 2024
fa5b78f
Update the err_Commands check functions to return the false first
BowTiedRadone Jun 20, 2024
d10d760
Update the way `firstRewardCycle` is calculated
BowTiedRadone Jun 20, 2024
7c8c540
Remove unused `Stub` import inside `err_Commands`
BowTiedRadone Jun 20, 2024
934d554
Update the way `firstRewardCycle` is calculated all over the stateful…
BowTiedRadone Jun 20, 2024
3057aaa
Add helper functions to use in the commands' check methods
BowTiedRadone Jun 20, 2024
dbaf000
Update `err_Commands` check functions to use suggestive helpers
BowTiedRadone Jun 20, 2024
00c0738
Update happy path commands check functions to use suggestive helpers
BowTiedRadone Jun 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,147 changes: 2,147 additions & 0 deletions contrib/boot-contracts-stateful-prop-tests/tests/pox-4/err_Commands.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { StackingClient } from "@stacks/stacking";

import fc from "fast-check";
import { PoxCommands } from "./pox_Commands.ts";
import { ErrCommands } from "./err_Commands.ts";

import fs from "fs";
import path from "path";
Expand Down Expand Up @@ -108,7 +109,8 @@ it("statefully interacts with PoX-4", async () => {
// commands are run at least once.
const statistics = fs.readdirSync(path.join(__dirname)).filter((file) =>
file.startsWith("pox_") && file.endsWith(".ts") &&
file !== "pox_CommandModel.ts" && file !== "pox_Commands.ts"
file !== "pox_CommandModel.ts" && file !== "pox_Commands.ts" &&
!file.includes("_Err")
).map((file) => file.slice(4, -3)); // Remove "pox_" prefix and ".ts" suffix.

// This is the initial state of the model.
Expand Down Expand Up @@ -143,17 +145,22 @@ it("statefully interacts with PoX-4", async () => {

simnet.setEpoch("3.0");

const successPath = PoxCommands(model.wallets, model.stackers, sut.network);
BowTiedRadone marked this conversation as resolved.
Show resolved Hide resolved
const failurePath = ErrCommands(model.wallets, model.stackers, sut.network);
BowTiedRadone marked this conversation as resolved.
Show resolved Hide resolved

fc.assert(
fc.property(
PoxCommands(model.wallets, model.stackers, sut.network),
// More on size: https://github.com/dubzzz/fast-check/discussions/2978
// More on cmds: https://github.com/dubzzz/fast-check/discussions/3026
fc.commands(successPath.concat(failurePath), { size: "xsmall" }),
(cmds) => {
const initialState = () => ({ model: model, real: sut });
fc.modelRun(initialState, cmds);
},
),
{
// Defines the number of test iterations to run; default is 100.
numRuns: 1000,
numRuns: 20000,
// Adjusts the level of detail in test reports. Default is 0 (minimal).
// At level 2, reports include extensive details, helpful for deep
// debugging. This includes not just the failing case and its seed, but
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export class AllowContractCallerCommand implements PoxCommand {

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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,49 @@ export class Stub {

reportCommandRuns() {
console.log("Command run method execution counts:");
this.statistics.forEach((count, commandName) => {
console.log(`${commandName}: ${count}`);
const orderedStatistics = Array.from(this.statistics.entries()).sort(
([keyA], [keyB]) => {
return keyA.localeCompare(keyB);
},
);

this.logAsTree(orderedStatistics);
}

private logAsTree(statistics: [string, number][]) {
const tree: { [key: string]: any } = {};

statistics.forEach(([commandName, count]) => {
const split = commandName.split("_");
let root: string = split[0],
rest: string = "base";

if (split.length > 1) {
rest = split.slice(1).join("_");
}
if (!tree[root]) {
tree[root] = {};
}
tree[root][rest] = count;
});

const printTree = (node: any, indent: string = "") => {
const keys = Object.keys(node);
keys.forEach((key, index) => {
const isLast = index === keys.length - 1;
const boxChar = isLast ? "└─ " : "├─ ";
if (key !== "base") {
if (typeof node[key] === "object") {
console.log(`${indent}${boxChar}${key}: ${node[key]["base"]}`);
printTree(node[key], indent + (isLast ? " " : "│ "));
} else {
console.log(`${indent}${boxChar}${key}: ${node[key]}`);
}
}
});
};

printTree(tree);
}

refreshStateForNextRewardCycle(real: Real) {
Expand Down Expand Up @@ -215,3 +255,258 @@ export const logCommand = (...items: (string | undefined)[]) => {

process.stdout.write(prettyPrint.join(""));
};

/**
* Helper function that checks if the minimum uSTX threshold was set in the model.
* @param model - the model at a given moment in time.
* @returns boolean.
*/
export const isStackingMinimumCalculated = (model: Readonly<Stub>): boolean =>
model.stackingMinimum > 0;

/**
* Helper function that checks if a stacker is currently stacking.
* @param stacker - the stacker's state at a given moment in time.
* @returns boolean.
*/
export const isStacking = (stacker: Stacker): boolean =>
stacker.isStacking;

/**
* Helper function that checks if a stacker has an active delegation.
* @param stacker - the stacker's state at a given moment in time.
* @returns boolean.
*/
export const isDelegating = (stacker: Stacker): boolean =>
stacker.hasDelegated;

/**
* Helper function that checks if the stacker is stacking using solo
* stacking methods.
* @param stacker - the stacker's state at a given moment in time.
* @returns boolean.
*/
export const isStackingSolo = (stacker: Stacker): boolean =>
stacker.isStackingSolo;

/**
* Helper function that checks if the stacker has locked uSTX.
* @param stacker - the stacker's state at a given moment in time.
* @returns boolean.
*/
export const isAmountLockedPositive = (stacker: Stacker): boolean =>
stacker.amountLocked > 0;

/**
* Helper function that checks if an operator has locked uSTX on
* behalf of at least one stacker.
* @param operator - the operator's state at a given moment in time.
* @returns boolean.
*/
export const hasLockedStackers = (operator: Stacker): boolean =>
operator.lockedAddresses.length > 0;

/**
* Helper function that checks if an operator has uSTX that was not
* yet committed.
* @param operator - the operator's state at a given moment in time.
* @returns boolean.
*
* NOTE: ATC is an abbreviation for "amount to commit".
*/
export const isATCPositive = (operator: Stacker): boolean =>
operator.amountToCommit > 0;

/**
* Helper function that checks if an operator's not committed uSTX
* amount is above the minimum stacking threshold.
* @param operator - the operator's state at a given moment in time.
* @param model - the model at a given moment in time.
* @returns boolean.
*
* NOTE: ATC is an abbreviation for "amount to commit".
*/ export const isATCAboveThreshold = (
operator: Stacker,
model: Readonly<Stub>,
): boolean => operator.amountToCommit >= model.stackingMinimum;

/**
* Helper function that checks if a uSTX amount fits within a stacker's
* delegation limit.
* @param stacker - the stacker's state at a given moment in time.
* @param amountToCheck - the uSTX amount to check.
* @returns boolean.
*/
export const isAmountWithinDelegationLimit = (
stacker: Stacker,
amountToCheck: bigint | number,
): boolean => stacker.delegatedMaxAmount >= Number(amountToCheck);

/**
* Helper function that checks if a given unlock burn height is within
* a stacker's delegation limit.
* @param stacker - the stacker's state at a given moment in time.
* @param unlockBurnHt - the verified unlock burn height.
* @returns boolean.
*
* NOTE: UBH is an abbreviation for "unlock burn height".
*/
export const isUBHWithinDelegationLimit = (
stacker: Stacker,
unlockBurnHt: number,
): boolean =>
stacker.delegatedUntilBurnHt === undefined ||
unlockBurnHt <= stacker.delegatedUntilBurnHt;

/**
* Helper function that checks if a given amount is within a stacker's
* unlocked uSTX balance.
* @param stacker - the stacker's state at a given moment in time.
* @param amountToCheck - the amount to check.
* @returns boolean.
*/
export const isAmountWithinBalance = (
stacker: Stacker,
amountToCheck: bigint | number,
): boolean => stacker.ustxBalance >= Number(amountToCheck);

/**
* Helper function that checks if a given amount is above the minimum
* stacking threshold.
* @param model - the model at a given moment in time.
* @param amountToCheck - the amount to check.
* @returns boolean.
*/
export const isAmountAboveThreshold = (
model: Readonly<Stub>,
amountToCheck: bigint | number,
): boolean => Number(amountToCheck) >= model.stackingMinimum;

/**
* Helper function that checks if an operator has at least one pool
* participant.
* @param operator - the operator's state at a given moment in time.
* @returns boolean.
*/
export const hasPoolMembers = (operator: Stacker): boolean =>
operator.poolMembers.length > 0;

/**
* Helper function that checks if a stacker is a pool member of a
* given operator.
* @param operator - the operator's state at a given moment in time.
* @param stacker - the stacker's state at a given moment in time.
* @returns boolean
*/
export const isStackerInOperatorPool = (
operator: Stacker,
stacker: Wallet,
): boolean => operator.poolMembers.includes(stacker.stxAddress);

/**
* Helper function that checks if a given stacker's funds are locked
* by a given operator.
* @param stacker - the stacker's state at a given moment in time.
* @param operator - the operator's state at a given moment in time.
* @returns boolean.
*/
export const isStackerLockedByOperator = (
operator: Stacker,
stacker: Wallet,
): boolean =>
operator.lockedAddresses.includes(
stacker.stxAddress,
);

/**
* Helper function that checks if a given stacker's unlock height is
* within the current reward cycle.
* @param stacker - the stacker's state at a given moment in time.
* @param model - the model at a given moment in time.
* @returns boolean.
*
* NOTE: RC is an abbreviation for "reward cycle".
*/
export const isUnlockedWithinCurrentRC = (
stackerWallet: Stacker,
model: Readonly<Stub>,
): boolean => (stackerWallet.unlockHeight <=
model.burnBlockHeight + REWARD_CYCLE_LENGTH);

/**
* Helper function that checks if the increase amount is within a given
* stacker's unlocked balance.
* @param stacker - the stacker's state at a given moment in time.
* @param increaseBy - the increase amount to check.
* @returns boolean.
*/
export const isIncreaseByWithinUnlockedBalance = (
stacker: Stacker,
increaseBy: number,
): boolean => increaseBy <= stacker.amountUnlocked;

/**
* Helper function that checks if the increase amount is greater than zero.
* @param increaseBy - the increase amount to check.
* @returns boolean.
*/
export const isIncreaseByGTZero = (increaseBy: number): boolean =>
increaseBy >= 1;

/**
* Helper function that checks if the increase amount does not exceed the
* PoX-4 maximum lock period.
* @param period - the period to check.
* @returns boolean.
*/
export const isPeriodWithinMax = (period: number) => period <= 12;

/**
* Helper function that checks if a given stacker is currently delegating
* to a given operator.
* @param stacker - the stacker's state at a given moment in time.
* @param operator - the operator's state at a given moment in time.
* @returns boolean.
*/
export const isStackerDelegatingToOperator = (
stacker: Stacker,
operator: Wallet,
): boolean => stacker.delegatedTo === operator.stxAddress;

/**
* Helper function that checks if a given increase amount is greater than
* zero.
* @param increaseAmount - the increase amount to check
* @returns boolean.
*/
export const isIncreaseAmountGTZero = (increaseAmount: number): boolean =>
increaseAmount > 0;

/**
* Helper function that checks if a given stacker's has issued an allowance
* to a potential contract caller.
* @param stacker - the stacker's state at a given moment in time.
* @param potentialAllowedStacker - the potential contract caller's state.
* @returns boolean.
*/
export const isAllowedContractCaller = (
stacker: Stacker,
potentialAllowedStacker: Wallet,
): boolean =>
stacker.allowedContractCallers.includes(
potentialAllowedStacker.stxAddress,
);

/**
* Helper function that checks if a given contract caller has been allowed by
* a given stacker.
* @param stacker - the stacker's state at a given moment in time.
* @param caller - the contract caller's state.
* @returns boolean.
*/
export const isCallerAllowedByStacker = (
stacker: Wallet,
caller: Stacker,
): boolean => caller.callerAllowedBy.includes(stacker.stxAddress);

export const isPositive = (value: number): boolean => value >= 0;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fc from "fast-check";
import { Real, Stacker, Stub, StxAddress, Wallet } from "./pox_CommandModel";
import { PoxCommand, Stacker, StxAddress, Wallet } from "./pox_CommandModel";
import { GetStackingMinimumCommand } from "./pox_GetStackingMinimumCommand";
import { GetStxAccountCommand } from "./pox_GetStxAccountCommand";
import { StackStxSigCommand } from "./pox_StackStxSigCommand";
Expand Down Expand Up @@ -27,7 +27,7 @@ export function PoxCommands(
wallets: Map<StxAddress, Wallet>,
stackers: Map<StxAddress, Stacker>,
network: Simnet,
): fc.Arbitrary<Iterable<fc.Command<Stub, Real>>> {
): fc.Arbitrary<PoxCommand>[] {
const cmds = [
// GetStackingMinimumCommand
fc.record({
Expand Down Expand Up @@ -452,9 +452,7 @@ export function PoxCommands(
),
];

// More on size: https://github.com/dubzzz/fast-check/discussions/2978
// More on cmds: https://github.com/dubzzz/fast-check/discussions/3026
return fc.commands(cmds, { size: "xsmall" });
return cmds;
}

export const REWARD_CYCLE_LENGTH = 1050;
Expand All @@ -481,7 +479,7 @@ export const currentCycleFirstBlock = (network: Simnet) =>
).result,
));

const nextCycleFirstBlock = (network: Simnet) =>
export const nextCycleFirstBlock = (network: Simnet) =>
Number(cvToValue(
network.callReadOnlyFn(
"ST000000000000000000002AMW42H.pox-4",
Expand Down
Loading