Skip to content

Commit

Permalink
Merge branch 'develop' into feat/header-signer-signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
hstove committed Jun 3, 2024
2 parents 55567d7 + aede203 commit 1d7c5bd
Show file tree
Hide file tree
Showing 35 changed files with 940 additions and 297 deletions.
1 change: 1 addition & 0 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
- tests::nakamoto_integrations::vote_for_aggregate_key_burn_op
- tests::nakamoto_integrations::follower_bootup
- tests::nakamoto_integrations::forked_tenure_is_ignored
- tests::nakamoto_integrations::nakamoto_attempt_time
- tests::signer::v0::block_proposal_rejection
- tests::signer::v0::miner_gather_signatures
- tests::signer::v1::dkg
Expand Down
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 1d7c5bd

Please sign in to comment.