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

pox-4: disallow stacking during prepare phase #4094

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions contrib/core-contract-tests/Clarinet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ costs_version = 1
[contracts.bns]
path = "../../stackslib/src/chainstate/stacks/boot/bns.clar"
depends_on = []

[contracts.pox-4]
path = "../../stackslib/src/chainstate/stacks/boot/pox-4.clar"
depends_on = []
clarity = 2
epoch = 2.4

[contracts.pox-helper]
path = "./contracts/pox/pox-helper.clar"
depends_on = []
clarity = 2
epoch = 2.4
3 changes: 3 additions & 0 deletions contrib/core-contract-tests/contracts/pox/pox-helper.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

(define-read-only (get-bbh)
burn-block-height)
8 changes: 4 additions & 4 deletions contrib/core-contract-tests/tests/bns/name_register.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ describe("name revealing workflow", () => {
Cl.tuple({
owner: Cl.standardPrincipal(bob),
["zonefile-hash"]: Cl.bufferFromUtf8(cases[0].zonefile),
["lease-ending-at"]: Cl.some(Cl.uint(16)),
["lease-started-at"]: Cl.uint(6),
["lease-ending-at"]: Cl.some(Cl.uint(simnet.blockHeight + 10)),
["lease-started-at"]: Cl.uint(simnet.blockHeight),
})
);
});
Expand Down Expand Up @@ -589,8 +589,8 @@ describe("register a name again before and after expiration", () => {
Cl.tuple({
owner: Cl.standardPrincipal(charlie),
["zonefile-hash"]: Cl.bufferFromAscii("CHARLIE"),
["lease-ending-at"]: Cl.some(Cl.uint(5029)),
["lease-started-at"]: Cl.uint(5019),
["lease-ending-at"]: Cl.some(Cl.uint(simnet.blockHeight + 10)),
["lease-started-at"]: Cl.uint(simnet.blockHeight),
})
);
});
Expand Down
40 changes: 20 additions & 20 deletions contrib/core-contract-tests/tests/bns/name_register_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Clarinet.test({
],
cases[0].nameOwner.address),
]);
assertEquals(block.height, 2);
assertEquals(block.height, 3);
block.receipts[0].result
.expectErr()
.expectInt(1005);
Expand All @@ -119,7 +119,7 @@ Clarinet.test({
],
cases[1].namespaceOwner.address),
]);
assertEquals(block.height, 3);
assertEquals(block.height, 4);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -136,7 +136,7 @@ Clarinet.test({
],
cases[1].namespaceOwner.address),
]);
assertEquals(block.height, 4);
assertEquals(block.height, 5);
block.receipts[0].result
.expectOk()
.expectBool(true);
Expand All @@ -158,7 +158,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 5);
assertEquals(block.height, 6);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -174,7 +174,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 6);
assertEquals(block.height, 7);
block.receipts[0].result
.expectErr()
.expectInt(2004);
Expand All @@ -195,7 +195,7 @@ Clarinet.test({
],
cases[0].namespaceOwner.address),
]);
assertEquals(block.height, 7);
assertEquals(block.height, 8);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -212,7 +212,7 @@ Clarinet.test({
],
cases[0].namespaceOwner.address),
]);
assertEquals(block.height, 8);
assertEquals(block.height, 9);
block.receipts[0].result
.expectOk()
.expectBool(true);
Expand All @@ -225,7 +225,7 @@ Clarinet.test({
],
cases[0].namespaceOwner.address),
]);
assertEquals(block.height, 9);
assertEquals(block.height, 10);
block.receipts[0].result
.expectOk()
.expectBool(true);
Expand All @@ -244,7 +244,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 10);
assertEquals(block.height, 11);
block.receipts[0].result
.expectErr()
.expectInt(2001);
Expand All @@ -266,7 +266,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 11);
assertEquals(block.height, 12);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -282,7 +282,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 12);
assertEquals(block.height, 13);
block.receipts[0].result
.expectErr()
.expectInt(2007);
Expand All @@ -304,7 +304,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 13);
assertEquals(block.height, 14);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -320,7 +320,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 14);
assertEquals(block.height, 15);
block.receipts[0].result
.expectErr()
.expectInt(2022);
Expand All @@ -342,7 +342,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 15);
assertEquals(block.height, 16);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -358,7 +358,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 16);
assertEquals(block.height, 17);
block.receipts[0].result
.expectOk()
.expectBool(true);
Expand Down Expand Up @@ -388,7 +388,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 17);
assertEquals(block.height, 18);
block.receipts[0].result
.expectErr()
.expectInt(2004);
Expand All @@ -412,7 +412,7 @@ Clarinet.test({
],
charlie.address),
]);
assertEquals(block.height, 18);
assertEquals(block.height, 19);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -428,7 +428,7 @@ Clarinet.test({
],
charlie.address),
]);
assertEquals(block.height, 19);
assertEquals(block.height, 20);
block.receipts[0].result
.expectErr()
.expectInt(2004);
Expand All @@ -452,7 +452,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 20);
assertEquals(block.height, 21);
block.receipts[0].result
.expectOk()
.expectUint(144 + block.height - 1);
Expand All @@ -468,7 +468,7 @@ Clarinet.test({
],
bob.address),
]);
assertEquals(block.height, 21);
assertEquals(block.height, 22);
block.receipts[0].result
.expectErr()
.expectInt(3001);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Cl } from "@stacks/transactions";
import { beforeEach, describe, expect, it } from "vitest";
import { createHash } from "node:crypto";

const accounts = simnet.getAccounts();
const alice = accounts.get("wallet_1")!;
const bob = accounts.get("wallet_2")!;
const charlie = accounts.get("wallet_3")!;

function expectBurnBlockHeight(height: number) {
const { result: bbh } = simnet.callReadOnlyFn(
"pox-helper",
"get-bbh",
[],
alice
);
expect(bbh).toBeUint(height);
}

describe("test pox prepare phase check", () => {
it("should return true during prepare phase (1000 - 1049)", () => {
let { result } = simnet.callReadOnlyFn(
"pox-4",
"check-prepare-phase",
[Cl.uint(999)],
alice
);
expect(result).toBeBool(false);

({ result } = simnet.callReadOnlyFn(
"pox-4",
"check-prepare-phase",
[Cl.uint(1000)],
alice
));
expect(result).toBeBool(true);

({ result } = simnet.callReadOnlyFn(
"pox-4",
"check-prepare-phase",
[Cl.uint(1049)],
alice
));
expect(result).toBeBool(true);

({ result } = simnet.callReadOnlyFn(
"pox-4",
"check-prepare-phase",
[Cl.uint(1050)],
alice
));
expect(result).toBeBool(false);
});
});
6 changes: 2 additions & 4 deletions stackslib/src/chainstate/stacks/boot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,8 @@ lazy_static! {
format!("{}\n{}", BOOT_CODE_POX_MAINNET_CONSTS, POX_3_BODY);
pub static ref POX_3_TESTNET_CODE: String =
format!("{}\n{}", BOOT_CODE_POX_TESTNET_CONSTS, POX_3_BODY);
pub static ref POX_4_MAINNET_CODE: String =
format!("{}\n{}", BOOT_CODE_POX_MAINNET_CONSTS, POX_4_BODY);
pub static ref POX_4_TESTNET_CODE: String =
format!("{}\n{}", BOOT_CODE_POX_TESTNET_CONSTS, POX_4_BODY);
pub static ref POX_4_MAINNET_CODE: String = format!("{}", POX_4_BODY);
pub static ref POX_4_TESTNET_CODE: String = format!("{}", POX_4_BODY);
pub static ref BOOT_CODE_COST_VOTING_TESTNET: String = make_testnet_cost_voting();
pub static ref STACKS_BOOT_CODE_MAINNET: [(&'static str, &'static str); 6] = [
("pox", &BOOT_CODE_POX_MAINNET),
Expand Down
37 changes: 31 additions & 6 deletions stackslib/src/chainstate/stacks/boot/pox-4.clar
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,19 @@
(define-constant ERR_DELEGATION_WRONG_REWARD_SLOT 29)
(define-constant ERR_STACKING_IS_DELEGATED 30)
(define-constant ERR_STACKING_NOT_DELEGATED 31)
(define-constant ERR_STACKING_DURING_PREPARE_PHASE 32)

;; PoX disabling threshold (a percent)
(define-constant POX_REJECTION_FRACTION u25)

;; Valid values for burnchain address versions.
;; These first four correspond to address hash modes in Stacks 2.1,
;; and are defined in pox-mainnet.clar and pox-testnet.clar (so they
;; cannot be defined here again).
;; (define-constant ADDRESS_VERSION_P2PKH 0x00)
;; (define-constant ADDRESS_VERSION_P2SH 0x01)
;; (define-constant ADDRESS_VERSION_P2WPKH 0x02)
;; (define-constant ADDRESS_VERSION_P2WSH 0x03)
(define-constant ADDRESS_VERSION_NATIVE_P2WPKH 0x04)
(define-constant ADDRESS_VERSION_NATIVE_P2WSH 0x05)
(define-constant ADDRESS_VERSION_NATIVE_P2TR 0x06)
;; (define-constant ADDRESS_VERSION_NATIVE_P2WPKH 0x04)
;; (define-constant ADDRESS_VERSION_NATIVE_P2WSH 0x05)
;; (define-constant ADDRESS_VERSION_NATIVE_P2TR 0x06)
;; Keep these constants in lock-step with the address version buffs above
;; Maximum value of an address version as a uint
(define-constant MAX_ADDRESS_VERSION u6)
Expand All @@ -52,6 +50,21 @@
;; (0x05 and 0x06 have 32-byte hashbytes)
(define-constant MAX_ADDRESS_VERSION_BUFF_32 u6)

;; PoX mainnet constants
;; Min/max number of reward cycles uSTX can be locked for
(define-constant MIN_POX_REWARD_CYCLES u1)
(define-constant MAX_POX_REWARD_CYCLES u12)

;; Default length of the PoX registration window, in burnchain blocks.
(define-constant PREPARE_CYCLE_LENGTH (if is-in-mainnet u100 u50))

;; Default length of the PoX reward cycle, in burnchain blocks.
(define-constant REWARD_CYCLE_LENGTH (if is-in-mainnet u2100 u1050))

;; Stacking thresholds
(define-constant STACKING_THRESHOLD_25 (if is-in-mainnet u20000 u8000))
(define-constant STACKING_THRESHOLD_100 (if is-in-mainnet u5000 u2000))

;; Data vars that store a copy of the burnchain configuration.
;; Implemented as data-vars, so that different configurations can be
;; used in e.g. test harnesses.
Expand Down Expand Up @@ -508,6 +521,14 @@
(and (>= lock-period MIN_POX_REWARD_CYCLES)
(<= lock-period MAX_POX_REWARD_CYCLES)))

;; Is the given burn block height in the prepare phase?
friedger marked this conversation as resolved.
Show resolved Hide resolved
;; This computes `((height - first-burnchain-block-height) + pox-prepare-cycle-length) % pox-reward-cycle-length) < pox-prepare-cycle-length`.
(define-read-only (check-prepare-phase (height uint))
(let ((prepare-cycle-length (var-get pox-prepare-cycle-length)))
(< (mod (+ (- height (var-get first-burnchain-block-height)) prepare-cycle-length)
(var-get pox-reward-cycle-length))
prepare-cycle-length)))

;; Evaluate if a participant can stack an amount of STX for a given period.
;; This method is designed as a read-only method so that it can be used as
;; a set of guard conditions and also as a read-only RPC call that can be
Expand Down Expand Up @@ -545,6 +566,10 @@
(asserts! (check-pox-lock-period num-cycles)
(err ERR_STACKING_INVALID_LOCK_PERIOD))

;; stacking must not happen during prepare phase
(asserts! (not (check-prepare-phase burn-block-height))
(err ERR_STACKING_DURING_PREPARE_PHASE))

;; address version must be valid
(asserts! (check-pox-addr-version (get version pox-addr))
(err ERR_STACKING_INVALID_POX_ADDRESS))
Expand Down