Add property-based tests for signers-voting contract functions

- reward-cycle-to-burn-height
- burn-height-to-reward-cycle
- is-in-prepare-phase

This effort, initially part of https://github.com/stacks-network/stacks-core/pull/4286,
is now in a separate commit and PR as it's autonomous with regards to PoX-4
property and fuzz testing. Additional property-based tests for the remaining
functions of the signers-voting contract are warranted and will be addressed
in separate commits/PRs.
This commit is contained in:
Nikos Baxevanis
2024-02-21 14:26:57 +01:00
parent 059ae88dea
commit ae05a62b3e
3 changed files with 169 additions and 0 deletions

View File

@@ -12,6 +12,7 @@
"@hirosystems/clarinet-sdk": "^1.1.0",
"@stacks/transactions": "^6.9.0",
"chokidar-cli": "^3.0.0",
"fast-check": "^3.15.1",
"typescript": "^5.2.2",
"vite": "^4.4.9",
"vitest": "^0.34.4",
@@ -955,6 +956,27 @@
"node": ">=6"
}
},
"node_modules/fast-check": {
"version": "3.15.1",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.15.1.tgz",
"integrity": "sha512-GutOXZ+SCxGaFWfHe0Pbeq8PrkpGtPxA9/hdkI3s9YzqeMlrq5RdJ+QfYZ/S93jMX+tAyqgW0z5c9ppD+vkGUw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
],
"dependencies": {
"pure-rand": "^6.0.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -1344,6 +1366,21 @@
"node": ">= 6"
}
},
"node_modules/pure-rand": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz",
"integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
]
},
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",

View File

@@ -12,6 +12,7 @@
"@hirosystems/clarinet-sdk": "^1.1.0",
"@stacks/transactions": "^6.9.0",
"chokidar-cli": "^3.0.0",
"fast-check": "^3.15.1",
"typescript": "^5.2.2",
"vite": "^4.4.9",
"vitest": "^0.34.4",

View File

@@ -0,0 +1,131 @@
import fc from "fast-check";
import { expect, it } from "vitest";
import { Cl } from "@stacks/transactions";
it("should return correct reward-cycle-to-burn-height", () => {
fc.assert(
fc.property(
fc.constantFrom(...simnet.getAccounts().values()),
fc.nat(),
(account: string, reward_cycle: number) => {
// Arrange
const { result: pox_4_info } = simnet.callReadOnlyFn(
"pox-4",
"get-pox-info",
[],
account,
);
const first_burnchain_block_height =
// @ts-ignore
pox_4_info.value.data[
"first-burnchain-block-height"];
const reward_cycle_length =
// @ts-ignore
pox_4_info.value.data[
"reward-cycle-length"];
// Act
const { result: actual } = simnet.callReadOnlyFn(
"signers-voting",
"reward-cycle-to-burn-height",
[Cl.uint(reward_cycle)],
account,
);
// Assert
const expected = (reward_cycle * Number(reward_cycle_length.value)) +
Number(first_burnchain_block_height.value);
expect(actual).toBeUint(expected);
},
),
{ numRuns: 250 },
);
});
it("should return correct burn-height-to-reward-cycle", () => {
fc.assert(
fc.property(
fc.constantFrom(...simnet.getAccounts().values()),
fc.nat(),
(account: string, height: number) => {
// Arrange
const { result: pox_4_info } = simnet.callReadOnlyFn(
"pox-4",
"get-pox-info",
[],
account,
);
const first_burnchain_block_height =
// @ts-ignore
pox_4_info.value.data[
"first-burnchain-block-height"];
const reward_cycle_length =
// @ts-ignore
pox_4_info.value.data[
"reward-cycle-length"];
// Act
const { result: actual } = simnet.callReadOnlyFn(
"signers-voting",
"burn-height-to-reward-cycle",
[Cl.uint(height)],
account,
);
// Assert
const expected = Math.floor(
(height - Number(first_burnchain_block_height.value)) /
Number(reward_cycle_length.value),
);
expect(actual).toBeUint(expected);
},
),
{ numRuns: 250 },
);
});
it("should return correct is-in-prepare-phase", () => {
fc.assert(
fc.property(
fc.constantFrom(...simnet.getAccounts().values()),
fc.nat(),
(account: string, height: number) => {
// Arrange
const { result: pox_4_info } = simnet.callReadOnlyFn(
"pox-4",
"get-pox-info",
[],
account,
);
const first_burnchain_block_height =
// @ts-ignore
pox_4_info.value.data[
"first-burnchain-block-height"];
const prepare_cycle_length =
// @ts-ignore
pox_4_info.value.data[
"prepare-cycle-length"];
const reward_cycle_length =
// @ts-ignore
pox_4_info.value.data[
"reward-cycle-length"];
// Act
const { result: actual } = simnet.callReadOnlyFn(
"signers-voting",
"is-in-prepare-phase",
[Cl.uint(height)],
account,
);
// Assert
const expected = ((height - Number(first_burnchain_block_height.value) +
Number(prepare_cycle_length.value)) %
Number(reward_cycle_length.value)) <
Number(prepare_cycle_length.value);
expect(actual).toBeBool(expected);
},
),
{ numRuns: 250 },
);
});