test(pox-4-tests): Add StackAggregationCommitCommand authorization-based

This commit is contained in:
BowTiedRadone
2024-04-03 00:34:11 +03:00
committed by Nikos Baxevanis
parent ed447864b7
commit 43c39605f5
6 changed files with 164 additions and 0 deletions

View File

@@ -88,6 +88,7 @@ it("statefully interacts with PoX-4", async () => {
isStacking: false,
hasDelegated: false,
lockedAddresses: [],
amountToCommit: 0,
poolMembers: [],
delegatedTo: "",
delegatedMaxAmount: 0,

View File

@@ -50,6 +50,7 @@ export type Wallet = {
isStacking: boolean;
hasDelegated: boolean;
lockedAddresses: StxAddress[];
amountToCommit: number;
poolMembers: StxAddress[];
delegatedTo: StxAddress;
delegatedMaxAmount: number;

View File

@@ -11,6 +11,7 @@ import { RevokeDelegateStxCommand } from "./pox_RevokeDelegateStxCommand";
import { AllowContractCallerCommand } from "./pox_AllowContractCallerCommand";
import { DelegateStackIncreaseCommand } from "./pox_DelegateStackIncreaseCommand";
import { DelegateStackExtendCommand } from "./pox_DelegateStackExtendCommand";
import { StackAggregationCommitCommand } from "./pox_StackAggregationCommitCommand";
export function PoxCommands(
wallets: Map<StxAddress, Wallet>,
@@ -74,6 +75,24 @@ export function PoxCommands(
r.amount,
)
),
// StackAggregationCommitCommand
fc.record({
wallet: fc.constantFrom(...wallets.values()),
authId: fc.nat(),
currentCycle: fc.constant(currentCycle(network)),
}).map((
r: {
wallet: Wallet;
authId: number;
currentCycle: number;
},
) =>
new StackAggregationCommitCommand(
r.wallet,
r.authId,
r.currentCycle,
)
),
// RevokeDelegateStxCommand
fc.record({
wallet: fc.constantFrom(...wallets.values()),

View File

@@ -103,11 +103,13 @@ export class DelegateStackIncreaseCommand implements PoxCommand {
// Get the Stacker's wallet from the model and update it with the new state.
const stackerWallet = model.wallets.get(this.stacker.stxAddress)!;
const operatorWallet = model.wallets.get(this.operator.stxAddress)!
// Update model so that we know this stacker has increased the stacked amount.
// Update locked and unlocked fields in the model.
stackerWallet.amountLocked = newTotalLocked;
stackerWallet.amountUnlocked = stackerWallet.amountUnlocked -
this.increaseBy;
operatorWallet.amountToCommit += this.increaseBy
// 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.

View File

@@ -157,6 +157,7 @@ export class DelegateStackStxCommand implements PoxCommand {
// the stacker's funds are locked when calling delegate-stack-extend,
// delegate-stack-increase
operatorWallet.lockedAddresses.push(stackerWallet.stxAddress);
operatorWallet.amountToCommit += Number(this.amountUstx)
// 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.

View File

@@ -0,0 +1,140 @@
import {
logCommand,
PoxCommand,
Real,
Stub,
Wallet,
} from "./pox_CommandModel.ts";
import { poxAddressToTuple } from "@stacks/stacking";
import { expect } from "vitest";
import { Cl } from "@stacks/transactions";
/**
* The `StackAggregationCommitCommand` allows an operator commits partially
* stacked STX and allocate a new PoX reward address slot. This allows a
* stacker to lock fewer STX than the minimal threshold in multiple transactions,
* so long as:
* 1. The pox-addr is the same.
* 2. This "commit" transaction is called _before_ the PoX anchor block.
*
* Constraints for running this command include:
* - The Operator must have locked STX on behalf of at least one stacker.
* - The total amount previously locked by the Operator on behalf of the
* stackers has to be greater than the uSTX threshold.
* - All of the Stackers must have delegated to the same pox address.
*/
export class StackAggregationCommitCommand implements PoxCommand {
readonly operator: Wallet;
readonly authId: number;
readonly currentCycle: number;
/**
* Constructs a `StackAggregationCommitCommand` to lock uSTX for stacking.
*
* @param operator - Represents the `Operator`'s wallet.
* @param authId - Unique `auth-id` for the authorization.
* @param currentCycle - The current reward cycle.
*/
constructor(
operator: Wallet,
authId: number,
currentCycle: number,
) {
this.operator = operator;
this.authId = authId;
this.currentCycle = currentCycle;
}
check(model: Readonly<Stub>): boolean {
// Constraints for running this command include:
// - The Operator must have locked STX on behalf of at least one stacker.
// - The total amount previously locked by the Operator on behalf of the
// stackers has to be greater than the uSTX threshold.
// - All of the Stackers must have delegated to the same pox address.
let sameDelegatedPoxAddrAllStackers = true;
const firstDelegatedPoxAddress = this.operator.lockedAddresses.length > 0
? model.wallets.get(this.operator.lockedAddresses[0])!.delegatedPoxAddress
: this.operator.btcAddress;
this.operator.lockedAddresses.forEach((stacker) => {
if (
model.wallets.get(stacker)!.delegatedPoxAddress !==
firstDelegatedPoxAddress
) sameDelegatedPoxAddrAllStackers = false;
});
return this.operator.lockedAddresses.length > 0 &&
this.operator.amountToCommit >= model.stackingMinimum &&
sameDelegatedPoxAddrAllStackers;
}
run(model: Stub, real: Real): void {
model.trackCommandRun(this.constructor.name);
const committedAmount = this.operator.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(this.currentCycle + 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(this.operator.amountToCommit),
// (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(this.currentCycle + 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));
const operatorWallet = model.wallets.get(this.operator.stxAddress)!;
operatorWallet.amountToCommit -= committedAmount;
// 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.
logCommand(`${this.operator.label}`, "stack-aggregation-commit", 'amount committed', committedAmount.toString());
}
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.operator.label} stack-aggregation-commit auth-id ${this.authId} for reward cycle ${this.currentCycle}`;
}
}