From 697d883138f5bc8e4194541c9dc3342fbed027bc Mon Sep 17 00:00:00 2001 From: janniks Date: Fri, 19 Apr 2024 17:40:30 +0700 Subject: [PATCH 1/2] docs: update readme --- packages/stacking/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/stacking/README.md b/packages/stacking/README.md index 03046a87b..e1cf3ee93 100644 --- a/packages/stacking/README.md +++ b/packages/stacking/README.md @@ -80,6 +80,8 @@ const maxAmount = 100000000000n; // the auth id of the signer (a random id to not allow reusing the signature) const authId = 702; +const signerPrivateKey = makeRandomPrivKey(); // replace with your signer private key + const signature = client.signPoxSignature({ topic: 'stack-stx', // the topic of the transaction poxAddress, @@ -92,7 +94,7 @@ const signature = client.signPoxSignature({ ``` > [!WARNING] -> Make sure to replace `signerPrivateKey` with the signer private key of your setup. +> Make sure to replace `signerPrivateKey` with the signer private key of your setup and keep it private. ### Topics From 0c37b3f209705a4aac45c7a5dbdd76a4b8b26c94 Mon Sep 17 00:00:00 2001 From: janniks Date: Fri, 19 Apr 2024 19:42:54 +0700 Subject: [PATCH 2/2] feat: add verifySignerKeySignature read-only to StackingClient --- packages/stacking/src/index.ts | 53 +++++++++++++++ packages/stacking/tests/apiMockingHelpers.ts | 13 ++++ packages/stacking/tests/stacking-2.5.test.ts | 70 ++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 packages/stacking/tests/stacking-2.5.test.ts diff --git a/packages/stacking/src/index.ts b/packages/stacking/src/index.ts index 9675b7290..f8710d85c 100644 --- a/packages/stacking/src/index.ts +++ b/packages/stacking/src/index.ts @@ -30,6 +30,7 @@ import { principalCV, principalToString, someCV, + stringAsciiCV, uintCV, validateStacksAddress, } from '@stacks/transactions'; @@ -1541,6 +1542,58 @@ export class StackingClient { }); } + /** + * Call the `verify-signer-key-sig` read-only function on the PoX contract. + * @returns (async) a boolean indicating if the signature is valid + */ + async verifySignerKeySignature({ + topic, + poxAddress, + rewardCycle, + period, + signerSignature, + signerKey, + amount, + maxAmount, + authId, + }: { + topic: string; + poxAddress: string; + rewardCycle: number; + period: number; + signerSignature?: string; + signerKey: string; + amount: IntegerType; + maxAmount: IntegerType; + authId: IntegerType; + }): Promise { + const poxInfo = await this.getPoxInfo(); + + const [contractAddress, contractName] = this.parseContractId(poxInfo.contract_id); + const functionName = 'verify-signer-key-sig'; + + const functionArgs = [ + poxAddressToTuple(poxAddress), + uintCV(rewardCycle), + stringAsciiCV(topic), + uintCV(period), + signerSignature ? someCV(bufferCV(hexToBytes(signerSignature))) : noneCV(), + bufferCV(hexToBytes(signerKey)), + uintCV(amount), + uintCV(maxAmount), + uintCV(authId), + ]; + + return callReadOnlyFunction({ + contractAddress, + contractName, + functionName, + functionArgs, + network: this.network, + senderAddress: this.address, + }).then(responseCV => responseCV.type === ClarityType.ResponseOk); + } + /** * @returns {Promise} that resolves to the contract id (address and name) to use for stacking */ diff --git a/packages/stacking/tests/apiMockingHelpers.ts b/packages/stacking/tests/apiMockingHelpers.ts index 862850c50..a99a237fb 100644 --- a/packages/stacking/tests/apiMockingHelpers.ts +++ b/packages/stacking/tests/apiMockingHelpers.ts @@ -3,6 +3,18 @@ import { StacksTestnet } from '@stacks/network'; import { MockResponseInitFunction } from 'jest-fetch-mock'; import { PoxInfo, StackingClient } from '../src'; +// NOTES +// Capture traffic via the fetchWrapper +// ```ts +// const body = await fetchResult.clone().text(); +// const path = new URL(typeof input === 'string' ? input : input?.url).pathname; +// fs.appendFileSync('./network.txt', `'${path}': \`${body}\`,\n`); +// ``` +// Disable mocking for individual tests locally +// ```ts +// fetchMock.disableMocks(); +// ``` + // Helpers for creating tests with unmocked environments ======================= const MATCHER = { @@ -128,6 +140,7 @@ export const V2_FEES = `{"estimated_cost":{"write_length":1138,"write_count":10, export const V2_POX_MAINNET_POX_2 = `{"contract_id":"SP000000000000000000002Q6VF78.pox-2","pox_activation_threshold_ustx":68809653919448,"first_burnchain_block_height":666050,"current_burnchain_block_height":788791,"prepare_phase_block_length":100,"reward_phase_block_length":2000,"reward_slots":4000,"rejection_fraction":25,"total_liquid_supply_ustx":1376193078388960,"current_cycle":{"id":58,"min_threshold_ustx":90000000000,"stacked_ustx":225878587321503,"is_pox_active":false},"next_cycle":{"id":59,"min_threshold_ustx":90000000000,"min_increment_ustx":68809653919,"stacked_ustx":195129336384433,"prepare_phase_start_block_height":789850,"blocks_until_prepare_phase":1059,"reward_phase_start_block_height":789950,"blocks_until_reward_phase":1159,"ustx_until_pox_rejection":344048269597225},"min_amount_ustx":90000000000,"prepare_cycle_length":100,"reward_cycle_id":58,"reward_cycle_length":2100,"rejection_votes_left_required":344048269597225,"next_reward_cycle_in":1159,"contract_versions":[{"contract_id":"SP000000000000000000002Q6VF78.pox","activation_burnchain_block_height":666050,"first_reward_cycle_id":0},{"contract_id":"SP000000000000000000002Q6VF78.pox-2","activation_burnchain_block_height":781552,"first_reward_cycle_id":56}]}`; export const V2_POX_REGTEST_POX_3 = `{"contract_id":"ST000000000000000000002AMW42H.pox-3","pox_activation_threshold_ustx":600059459544055,"first_burnchain_block_height":0,"current_burnchain_block_height":408,"prepare_phase_block_length":1,"reward_phase_block_length":4,"reward_slots":8,"rejection_fraction":3333333333333333,"total_liquid_supply_ustx":60005945954405576,"current_cycle":{"id":81,"min_threshold_ustx":1875190000000000,"stacked_ustx":1875190000000000,"is_pox_active":false},"next_cycle":{"id":82,"min_threshold_ustx":1875190000000000,"min_increment_ustx":7500743244300,"stacked_ustx":1875190000000000,"prepare_phase_start_block_height":409,"blocks_until_prepare_phase":1,"reward_phase_start_block_height":410,"blocks_until_reward_phase":2,"ustx_until_pox_rejection":12734475822413576000},"min_amount_ustx":1875190000000000,"prepare_cycle_length":1,"reward_cycle_id":81,"reward_cycle_length":5,"rejection_votes_left_required":12734475822413576000,"next_reward_cycle_in":2,"contract_versions":[{"contract_id":"ST000000000000000000002AMW42H.pox","activation_burnchain_block_height":0,"first_reward_cycle_id":0},{"contract_id":"ST000000000000000000002AMW42H.pox-2","activation_burnchain_block_height":107,"first_reward_cycle_id":22},{"contract_id":"ST000000000000000000002AMW42H.pox-3","activation_burnchain_block_height":111,"first_reward_cycle_id":23}]}`; export const V2_POX_INTERFACE_POX_3 = `{"functions":[{"name":"add-pox-addr-to-ith-reward-cycle","access":"private","args":[{"name":"cycle-index","type":"uint128"},{"name":"params","type":{"tuple":[{"name":"amount-ustx","type":"uint128"},{"name":"first-reward-cycle","type":"uint128"},{"name":"i","type":"uint128"},{"name":"num-cycles","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-set-indexes","type":{"list":{"type":"uint128","length":12}}},{"name":"stacker","type":{"optional":"principal"}}]}}],"outputs":{"type":{"tuple":[{"name":"amount-ustx","type":"uint128"},{"name":"first-reward-cycle","type":"uint128"},{"name":"i","type":"uint128"},{"name":"num-cycles","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-set-indexes","type":{"list":{"type":"uint128","length":12}}},{"name":"stacker","type":{"optional":"principal"}}]}}},{"name":"add-pox-addr-to-reward-cycles","access":"private","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"first-reward-cycle","type":"uint128"},{"name":"num-cycles","type":"uint128"},{"name":"amount-ustx","type":"uint128"},{"name":"stacker","type":"principal"}],"outputs":{"type":{"response":{"ok":{"list":{"type":"uint128","length":12}},"error":"int128"}}}},{"name":"add-pox-partial-stacked","access":"private","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"first-reward-cycle","type":"uint128"},{"name":"num-cycles","type":"uint128"},{"name":"amount-ustx","type":"uint128"}],"outputs":{"type":"bool"}},{"name":"add-pox-partial-stacked-to-ith-cycle","access":"private","args":[{"name":"cycle-index","type":"uint128"},{"name":"params","type":{"tuple":[{"name":"amount-ustx","type":"uint128"},{"name":"num-cycles","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"}]}}],"outputs":{"type":{"tuple":[{"name":"amount-ustx","type":"uint128"},{"name":"num-cycles","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"}]}}},{"name":"append-reward-cycle-pox-addr","access":"private","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"},{"name":"amount-ustx","type":"uint128"},{"name":"stacker","type":{"optional":"principal"}}],"outputs":{"type":"uint128"}},{"name":"fold-unlock-reward-cycle","access":"private","args":[{"name":"set-index","type":"uint128"},{"name":"data-res","type":{"response":{"ok":{"tuple":[{"name":"cycle","type":"uint128"},{"name":"first-unlocked-cycle","type":"uint128"},{"name":"stacker","type":"principal"}]},"error":"int128"}}}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"cycle","type":"uint128"},{"name":"first-unlocked-cycle","type":"uint128"},{"name":"stacker","type":"principal"}]},"error":"int128"}}}},{"name":"handle-unlock","access":"private","args":[{"name":"user","type":"principal"},{"name":"amount-locked","type":"uint128"},{"name":"cycle-to-unlock","type":"uint128"}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"increase-reward-cycle-entry","access":"private","args":[{"name":"reward-cycle-index","type":"uint128"},{"name":"updates","type":{"optional":{"tuple":[{"name":"add-amount","type":"uint128"},{"name":"first-cycle","type":"uint128"},{"name":"reward-cycle","type":"uint128"},{"name":"stacker","type":"principal"}]}}}],"outputs":{"type":{"optional":{"tuple":[{"name":"add-amount","type":"uint128"},{"name":"first-cycle","type":"uint128"},{"name":"reward-cycle","type":"uint128"},{"name":"stacker","type":"principal"}]}}}},{"name":"inner-stack-aggregation-commit","access":"private","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":{"response":{"ok":"uint128","error":"int128"}}}},{"name":"allow-contract-caller","access":"public","args":[{"name":"caller","type":"principal"},{"name":"until-burn-ht","type":{"optional":"uint128"}}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"delegate-stack-extend","access":"public","args":[{"name":"stacker","type":"principal"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"extend-count","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"stacker","type":"principal"},{"name":"unlock-burn-height","type":"uint128"}]},"error":"int128"}}}},{"name":"delegate-stack-increase","access":"public","args":[{"name":"stacker","type":"principal"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"increase-by","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"stacker","type":"principal"},{"name":"total-locked","type":"uint128"}]},"error":"int128"}}}},{"name":"delegate-stack-stx","access":"public","args":[{"name":"stacker","type":"principal"},{"name":"amount-ustx","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"start-burn-ht","type":"uint128"},{"name":"lock-period","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"lock-amount","type":"uint128"},{"name":"stacker","type":"principal"},{"name":"unlock-burn-height","type":"uint128"}]},"error":"int128"}}}},{"name":"delegate-stx","access":"public","args":[{"name":"amount-ustx","type":"uint128"},{"name":"delegate-to","type":"principal"},{"name":"until-burn-ht","type":{"optional":"uint128"}},{"name":"pox-addr","type":{"optional":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}}}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"disallow-contract-caller","access":"public","args":[{"name":"caller","type":"principal"}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"reject-pox","access":"public","args":[],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"revoke-delegate-stx","access":"public","args":[],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"set-burnchain-parameters","access":"public","args":[{"name":"first-burn-height","type":"uint128"},{"name":"prepare-cycle-length","type":"uint128"},{"name":"reward-cycle-length","type":"uint128"},{"name":"rejection-fraction","type":"uint128"},{"name":"begin-2-1-reward-cycle","type":"uint128"}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"stack-aggregation-commit","access":"public","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"stack-aggregation-commit-indexed","access":"public","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":{"response":{"ok":"uint128","error":"int128"}}}},{"name":"stack-aggregation-increase","access":"public","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"},{"name":"reward-cycle-index","type":"uint128"}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"stack-extend","access":"public","args":[{"name":"extend-count","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"stacker","type":"principal"},{"name":"unlock-burn-height","type":"uint128"}]},"error":"int128"}}}},{"name":"stack-increase","access":"public","args":[{"name":"increase-by","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"stacker","type":"principal"},{"name":"total-locked","type":"uint128"}]},"error":"int128"}}}},{"name":"stack-stx","access":"public","args":[{"name":"amount-ustx","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"start-burn-ht","type":"uint128"},{"name":"lock-period","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"lock-amount","type":"uint128"},{"name":"stacker","type":"principal"},{"name":"unlock-burn-height","type":"uint128"}]},"error":"int128"}}}},{"name":"burn-height-to-reward-cycle","access":"read_only","args":[{"name":"height","type":"uint128"}],"outputs":{"type":"uint128"}},{"name":"can-stack-stx","access":"read_only","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"amount-ustx","type":"uint128"},{"name":"first-reward-cycle","type":"uint128"},{"name":"num-cycles","type":"uint128"}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"check-caller-allowed","access":"read_only","args":[],"outputs":{"type":"bool"}},{"name":"check-pox-addr-hashbytes","access":"read_only","args":[{"name":"version","type":{"buffer":{"length":1}}},{"name":"hashbytes","type":{"buffer":{"length":32}}}],"outputs":{"type":"bool"}},{"name":"check-pox-addr-version","access":"read_only","args":[{"name":"version","type":{"buffer":{"length":1}}}],"outputs":{"type":"bool"}},{"name":"check-pox-lock-period","access":"read_only","args":[{"name":"lock-period","type":"uint128"}],"outputs":{"type":"bool"}},{"name":"current-pox-reward-cycle","access":"read_only","args":[],"outputs":{"type":"uint128"}},{"name":"get-allowance-contract-callers","access":"read_only","args":[{"name":"sender","type":"principal"},{"name":"calling-contract","type":"principal"}],"outputs":{"type":{"optional":{"tuple":[{"name":"until-burn-ht","type":{"optional":"uint128"}}]}}}},{"name":"get-check-delegation","access":"read_only","args":[{"name":"stacker","type":"principal"}],"outputs":{"type":{"optional":{"tuple":[{"name":"amount-ustx","type":"uint128"},{"name":"delegated-to","type":"principal"},{"name":"pox-addr","type":{"optional":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}}},{"name":"until-burn-ht","type":{"optional":"uint128"}}]}}}},{"name":"get-delegation-info","access":"read_only","args":[{"name":"stacker","type":"principal"}],"outputs":{"type":{"optional":{"tuple":[{"name":"amount-ustx","type":"uint128"},{"name":"delegated-to","type":"principal"},{"name":"pox-addr","type":{"optional":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}}},{"name":"until-burn-ht","type":{"optional":"uint128"}}]}}}},{"name":"get-num-reward-set-pox-addresses","access":"read_only","args":[{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":"uint128"}},{"name":"get-partial-stacked-by-cycle","access":"read_only","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"},{"name":"sender","type":"principal"}],"outputs":{"type":{"optional":{"tuple":[{"name":"stacked-amount","type":"uint128"}]}}}},{"name":"get-pox-info","access":"read_only","args":[],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"current-rejection-votes","type":"uint128"},{"name":"first-burnchain-block-height","type":"uint128"},{"name":"min-amount-ustx","type":"uint128"},{"name":"prepare-cycle-length","type":"uint128"},{"name":"rejection-fraction","type":"uint128"},{"name":"reward-cycle-id","type":"uint128"},{"name":"reward-cycle-length","type":"uint128"},{"name":"total-liquid-supply-ustx","type":"uint128"}]},"error":"none"}}}},{"name":"get-pox-rejection","access":"read_only","args":[{"name":"stacker","type":"principal"},{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":{"optional":{"tuple":[{"name":"amount","type":"uint128"}]}}}},{"name":"get-reward-set-pox-address","access":"read_only","args":[{"name":"reward-cycle","type":"uint128"},{"name":"index","type":"uint128"}],"outputs":{"type":{"optional":{"tuple":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"stacker","type":{"optional":"principal"}},{"name":"total-ustx","type":"uint128"}]}}}},{"name":"get-reward-set-size","access":"read_only","args":[{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":"uint128"}},{"name":"get-stacker-info","access":"read_only","args":[{"name":"stacker","type":"principal"}],"outputs":{"type":{"optional":{"tuple":[{"name":"delegated-to","type":{"optional":"principal"}},{"name":"first-reward-cycle","type":"uint128"},{"name":"lock-period","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-set-indexes","type":{"list":{"type":"uint128","length":12}}}]}}}},{"name":"get-stacking-minimum","access":"read_only","args":[],"outputs":{"type":"uint128"}},{"name":"get-total-pox-rejection","access":"read_only","args":[{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":"uint128"}},{"name":"get-total-ustx-stacked","access":"read_only","args":[{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":"uint128"}},{"name":"is-pox-active","access":"read_only","args":[{"name":"reward-cycle","type":"uint128"}],"outputs":{"type":"bool"}},{"name":"minimal-can-stack-stx","access":"read_only","args":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"amount-ustx","type":"uint128"},{"name":"first-reward-cycle","type":"uint128"},{"name":"num-cycles","type":"uint128"}],"outputs":{"type":{"response":{"ok":"bool","error":"int128"}}}},{"name":"next-cycle-rejection-votes","access":"read_only","args":[],"outputs":{"type":"uint128"}},{"name":"reward-cycle-to-burn-height","access":"read_only","args":[{"name":"cycle","type":"uint128"}],"outputs":{"type":"uint128"}}],"variables":[{"name":"ADDRESS_VERSION_NATIVE_P2TR","type":{"buffer":{"length":1}},"access":"constant"},{"name":"ADDRESS_VERSION_NATIVE_P2WPKH","type":{"buffer":{"length":1}},"access":"constant"},{"name":"ADDRESS_VERSION_NATIVE_P2WSH","type":{"buffer":{"length":1}},"access":"constant"},{"name":"ADDRESS_VERSION_P2PKH","type":{"buffer":{"length":1}},"access":"constant"},{"name":"ADDRESS_VERSION_P2SH","type":{"buffer":{"length":1}},"access":"constant"},{"name":"ADDRESS_VERSION_P2WPKH","type":{"buffer":{"length":1}},"access":"constant"},{"name":"ADDRESS_VERSION_P2WSH","type":{"buffer":{"length":1}},"access":"constant"},{"name":"ERR_DELEGATION_EXPIRES_DURING_LOCK","type":"int128","access":"constant"},{"name":"ERR_DELEGATION_NO_REWARD_SLOT","type":"int128","access":"constant"},{"name":"ERR_DELEGATION_POX_ADDR_REQUIRED","type":"int128","access":"constant"},{"name":"ERR_DELEGATION_TOO_MUCH_LOCKED","type":"int128","access":"constant"},{"name":"ERR_DELEGATION_WRONG_REWARD_SLOT","type":"int128","access":"constant"},{"name":"ERR_INVALID_START_BURN_HEIGHT","type":"int128","access":"constant"},{"name":"ERR_NOT_ALLOWED","type":"int128","access":"constant"},{"name":"ERR_NOT_CURRENT_STACKER","type":"int128","access":"constant"},{"name":"ERR_STACKING_ALREADY_DELEGATED","type":"int128","access":"constant"},{"name":"ERR_STACKING_ALREADY_REJECTED","type":"int128","access":"constant"},{"name":"ERR_STACKING_ALREADY_STACKED","type":"int128","access":"constant"},{"name":"ERR_STACKING_CORRUPTED_STATE","type":"int128","access":"constant"},{"name":"ERR_STACKING_EXPIRED","type":"int128","access":"constant"},{"name":"ERR_STACKING_INSUFFICIENT_FUNDS","type":"int128","access":"constant"},{"name":"ERR_STACKING_INVALID_AMOUNT","type":"int128","access":"constant"},{"name":"ERR_STACKING_INVALID_LOCK_PERIOD","type":"int128","access":"constant"},{"name":"ERR_STACKING_INVALID_POX_ADDRESS","type":"int128","access":"constant"},{"name":"ERR_STACKING_IS_DELEGATED","type":"int128","access":"constant"},{"name":"ERR_STACKING_NOT_DELEGATED","type":"int128","access":"constant"},{"name":"ERR_STACKING_NO_SUCH_PRINCIPAL","type":"int128","access":"constant"},{"name":"ERR_STACKING_PERMISSION_DENIED","type":"int128","access":"constant"},{"name":"ERR_STACKING_POX_ADDRESS_IN_USE","type":"int128","access":"constant"},{"name":"ERR_STACKING_STX_LOCKED","type":"int128","access":"constant"},{"name":"ERR_STACKING_THRESHOLD_NOT_MET","type":"int128","access":"constant"},{"name":"ERR_STACKING_UNREACHABLE","type":"int128","access":"constant"},{"name":"ERR_STACK_EXTEND_NOT_LOCKED","type":"int128","access":"constant"},{"name":"ERR_STACK_INCREASE_NOT_LOCKED","type":"int128","access":"constant"},{"name":"MAX_ADDRESS_VERSION","type":"uint128","access":"constant"},{"name":"MAX_ADDRESS_VERSION_BUFF_20","type":"uint128","access":"constant"},{"name":"MAX_ADDRESS_VERSION_BUFF_32","type":"uint128","access":"constant"},{"name":"MAX_POX_REWARD_CYCLES","type":"uint128","access":"constant"},{"name":"MIN_POX_REWARD_CYCLES","type":"uint128","access":"constant"},{"name":"POX_REJECTION_FRACTION","type":"uint128","access":"constant"},{"name":"PREPARE_CYCLE_LENGTH","type":"uint128","access":"constant"},{"name":"REWARD_CYCLE_LENGTH","type":"uint128","access":"constant"},{"name":"STACKING_THRESHOLD_100","type":"uint128","access":"constant"},{"name":"STACKING_THRESHOLD_25","type":"uint128","access":"constant"},{"name":"configured","type":"bool","access":"variable"},{"name":"first-2-1-reward-cycle","type":"uint128","access":"variable"},{"name":"first-burnchain-block-height","type":"uint128","access":"variable"},{"name":"pox-prepare-cycle-length","type":"uint128","access":"variable"},{"name":"pox-rejection-fraction","type":"uint128","access":"variable"},{"name":"pox-reward-cycle-length","type":"uint128","access":"variable"}],"maps":[{"name":"allowance-contract-callers","key":{"tuple":[{"name":"contract-caller","type":"principal"},{"name":"sender","type":"principal"}]},"value":{"tuple":[{"name":"until-burn-ht","type":{"optional":"uint128"}}]}},{"name":"delegation-state","key":{"tuple":[{"name":"stacker","type":"principal"}]},"value":{"tuple":[{"name":"amount-ustx","type":"uint128"},{"name":"delegated-to","type":"principal"},{"name":"pox-addr","type":{"optional":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}}},{"name":"until-burn-ht","type":{"optional":"uint128"}}]}},{"name":"logged-partial-stacked-by-cycle","key":{"tuple":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"},{"name":"sender","type":"principal"}]},"value":{"tuple":[{"name":"stacked-amount","type":"uint128"}]}},{"name":"partial-stacked-by-cycle","key":{"tuple":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-cycle","type":"uint128"},{"name":"sender","type":"principal"}]},"value":{"tuple":[{"name":"stacked-amount","type":"uint128"}]}},{"name":"reward-cycle-pox-address-list","key":{"tuple":[{"name":"index","type":"uint128"},{"name":"reward-cycle","type":"uint128"}]},"value":{"tuple":[{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"stacker","type":{"optional":"principal"}},{"name":"total-ustx","type":"uint128"}]}},{"name":"reward-cycle-pox-address-list-len","key":{"tuple":[{"name":"reward-cycle","type":"uint128"}]},"value":{"tuple":[{"name":"len","type":"uint128"}]}},{"name":"reward-cycle-total-stacked","key":{"tuple":[{"name":"reward-cycle","type":"uint128"}]},"value":{"tuple":[{"name":"total-ustx","type":"uint128"}]}},{"name":"stacking-rejection","key":{"tuple":[{"name":"reward-cycle","type":"uint128"}]},"value":{"tuple":[{"name":"amount","type":"uint128"}]}},{"name":"stacking-rejectors","key":{"tuple":[{"name":"reward-cycle","type":"uint128"},{"name":"stacker","type":"principal"}]},"value":{"tuple":[{"name":"amount","type":"uint128"}]}},{"name":"stacking-state","key":{"tuple":[{"name":"stacker","type":"principal"}]},"value":{"tuple":[{"name":"delegated-to","type":{"optional":"principal"}},{"name":"first-reward-cycle","type":"uint128"},{"name":"lock-period","type":"uint128"},{"name":"pox-addr","type":{"tuple":[{"name":"hashbytes","type":{"buffer":{"length":32}}},{"name":"version","type":{"buffer":{"length":1}}}]}},{"name":"reward-set-indexes","type":{"list":{"type":"uint128","length":12}}}]}}],"fungible_tokens":[],"non_fungible_tokens":[],"epoch":"Epoch24","clarity_version":"Clarity2"}`; +export const V2_POX_REGTEST_POX_4 = `{"contract_id":"ST000000000000000000002AMW42H.pox-4","pox_activation_threshold_ustx":1000057600592055,"first_burnchain_block_height":0,"current_burnchain_block_height":227,"prepare_phase_block_length":5,"reward_phase_block_length":15,"reward_slots":30,"rejection_fraction":null,"total_liquid_supply_ustx":100005760059205579,"current_cycle":{"id":11,"min_threshold_ustx":833390000000000,"stacked_ustx":3750255000000000,"is_pox_active":true},"next_cycle":{"id":12,"min_threshold_ustx":833390000000000,"min_increment_ustx":12500720007400,"stacked_ustx":3750255000000000,"prepare_phase_start_block_height":235,"blocks_until_prepare_phase":8,"reward_phase_start_block_height":240,"blocks_until_reward_phase":13,"ustx_until_pox_rejection":null},"epochs":[{"epoch_id":"Epoch10","start_height":0,"end_height":0,"block_limit":{"write_length":0,"write_count":0,"read_length":0,"read_count":0,"runtime":0},"network_epoch":0},{"epoch_id":"Epoch20","start_height":0,"end_height":102,"block_limit":{"write_length":150000000,"write_count":50000,"read_length":1000000000,"read_count":50000,"runtime":100000000000},"network_epoch":0},{"epoch_id":"Epoch2_05","start_height":102,"end_height":103,"block_limit":{"write_length":150000000,"write_count":50000,"read_length":1000000000,"read_count":50000,"runtime":100000000000},"network_epoch":5},{"epoch_id":"Epoch21","start_height":103,"end_height":105,"block_limit":{"write_length":150000000,"write_count":50000,"read_length":1000000000,"read_count":50000,"runtime":100000000000},"network_epoch":6},{"epoch_id":"Epoch22","start_height":105,"end_height":106,"block_limit":{"write_length":150000000,"write_count":50000,"read_length":1000000000,"read_count":50000,"runtime":100000000000},"network_epoch":7},{"epoch_id":"Epoch23","start_height":106,"end_height":107,"block_limit":{"write_length":150000000,"write_count":50000,"read_length":1000000000,"read_count":50000,"runtime":100000000000},"network_epoch":8},{"epoch_id":"Epoch24","start_height":107,"end_height":132,"block_limit":{"write_length":150000000,"write_count":50000,"read_length":1000000000,"read_count":50000,"runtime":100000000000},"network_epoch":9},{"epoch_id":"Epoch25","start_height":132,"end_height":152,"block_limit":{"write_length":15000000,"write_count":15000,"read_length":100000000,"read_count":15000,"runtime":5000000000},"network_epoch":10},{"epoch_id":"Epoch30","start_height":152,"end_height":9223372036854775807,"block_limit":{"write_length":15000000,"write_count":15000,"read_length":100000000,"read_count":15000,"runtime":5000000000},"network_epoch":11}],"min_amount_ustx":833390000000000,"prepare_cycle_length":5,"reward_cycle_id":11,"reward_cycle_length":20,"rejection_votes_left_required":null,"next_reward_cycle_in":13,"contract_versions":[{"contract_id":"ST000000000000000000002AMW42H.pox","activation_burnchain_block_height":0,"first_reward_cycle_id":0},{"contract_id":"ST000000000000000000002AMW42H.pox-2","activation_burnchain_block_height":104,"first_reward_cycle_id":6},{"contract_id":"ST000000000000000000002AMW42H.pox-3","activation_burnchain_block_height":107,"first_reward_cycle_id":6},{"contract_id":"ST000000000000000000002AMW42H.pox-4","activation_burnchain_block_height":132,"first_reward_cycle_id":7}]}`; // === MOCK MAPS =============================================================== export const MOCK_POX_3_REGTEST = { diff --git a/packages/stacking/tests/stacking-2.5.test.ts b/packages/stacking/tests/stacking-2.5.test.ts new file mode 100644 index 000000000..3b8424110 --- /dev/null +++ b/packages/stacking/tests/stacking-2.5.test.ts @@ -0,0 +1,70 @@ +import { getPublicKeyFromPrivate, publicKeyToBtcAddress } from '@stacks/encryption'; +import { StacksMocknet } from '@stacks/network'; +import { makeRandomPrivKey } from '@stacks/transactions'; +import { StackingClient } from '../src'; +import { V2_POX_REGTEST_POX_4, setApiMocks } from './apiMockingHelpers'; + +beforeEach(() => { + jest.resetModules(); + fetchMock.resetMocks(); +}); + +describe('pox-4', () => { + test('verify-signer-key-sig', async () => { + const network = new StacksMocknet(); + const address = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6'; + const client = new StackingClient(address, network); + + const signerPrivateKey = makeRandomPrivKey(); + const signerKey = getPublicKeyFromPrivate(signerPrivateKey.data); + + const signature = client.signPoxSignature({ + topic: 'stack-stx', + period: 2, + rewardCycle: 42, + poxAddress: publicKeyToBtcAddress(signerKey), + maxAmount: 10_000_000_000, + authId: 4, + + signerPrivateKey, + }); + + setApiMocks({ + '/v2/pox': V2_POX_REGTEST_POX_4, + '/v2/contracts/call-read/ST000000000000000000002AMW42H/pox-4/verify-signer-key-sig': `{"okay":true,"result":"0x0703"}`, + }); + const result = await client.verifySignerKeySignature({ + amount: 9_000_000_000, + + topic: 'stack-stx', + period: 2, + rewardCycle: 42, + poxAddress: publicKeyToBtcAddress(signerKey), + maxAmount: 10_000_000_000, + authId: 4, + + signerSignature: signature, + signerKey, + }); + expect(result).toBe(true); + + setApiMocks({ + '/v2/pox': V2_POX_REGTEST_POX_4, + '/v2/contracts/call-read/ST000000000000000000002AMW42H/pox-4/verify-signer-key-sig': `{"okay":true,"result":"0x080000000000000000000000000000000023"}`, + }); + const invalid = await client.verifySignerKeySignature({ + amount: 9_000_000_000, + + topic: 'stack-stx', + period: 3, // period doesn't match signature + rewardCycle: 42, + poxAddress: publicKeyToBtcAddress(signerKey), + maxAmount: 10_000_000_000, + authId: 4, + + signerSignature: signature, + signerKey: signerKey, + }); + expect(invalid).toBe(false); + }); +});