|
1 | | -# Generate a Signer Signature |
2 | | - |
3 | | -Stacking transactions require a **signer key signature** to prove that the controller of the signer key authorizes the stacking operation. This page covers what a signer signature is, what data it contains, and all the ways to generate one. |
4 | | - |
5 | | -Both [solo stacking](solo-stacking.md) and delegated stacking ([Stack with a Pool](stack-with-a-pool.md) / [Operate a Pool](operate-a-pool.md)) reference this page. Generate your signature here before making stacking transactions. |
6 | | - |
7 | | -*** |
8 | | - |
9 | | -## Overview |
| 1 | +--- |
| 2 | +description: >- |
| 3 | + How to produce the one-time signer-key grant that ties your signer to a |
| 4 | + signer-manager contract under PoX-5. |
| 5 | +--- |
10 | 6 |
|
11 | | -Signer signatures are created using your signer key. They demonstrate that the controller of that signer key is allowing a stacker (or pool operator) to use their signing key in a stacking transaction. Because signer keys must be unique across the network, this also prevents other stackers from using someone else's key. |
| 7 | +# Generate a Signer Signature |
12 | 8 |
|
13 | 9 | {% hint style="info" %} |
14 | | -The current pox-4 contract address can be found at [https://api.mainnet.hiro.so/v2/pox](https://api.mainnet.hiro.so/v2/pox). You can view the contract in the [Stacks Explorer](https://explorer.hiro.so/?chain=mainnet). |
| 10 | +Updated for Stacks 4.x and PoX-5. |
15 | 11 | {% endhint %} |
16 | 12 |
|
17 | | -### Fields Passed in Stacking Transactions |
18 | | - |
19 | | -When making stacking transactions, you need to provide these signature-related fields: |
20 | | - |
21 | | -{% stepper %} |
22 | | -{% step %} |
23 | | -#### signer-key |
24 | | - |
25 | | -The public key that corresponds to the `stacks_private_key` your signer is using. |
26 | | -{% endstep %} |
| 13 | +PoX-5 uses a one-time **signer-key grant**. You generate one grant per signer-manager contract and submit it once. Every staking call routed through that manager then relies on it until you revoke it. There is nothing to generate per transaction, per cycle, or per staker. |
27 | 14 |
|
28 | | -{% step %} |
29 | | -#### signer-signature |
| 15 | +The grant ties three things together: your signer node, the signer-manager contract that stakers stake to, and the pox-5 contract. Without an active grant, `stake` and every related call against your manager fails with `ERR_SIGNER_KEY_GRANT_NOT_FOUND` (u17). |
30 | 16 |
|
31 | | -A signature that demonstrates you actually control your `signer-key`. |
32 | | -{% endstep %} |
| 17 | +### Generate the grant |
33 | 18 |
|
34 | | -{% step %} |
35 | | -#### max-amount |
| 19 | +Run this on the signer host, where the private key already lives: |
36 | 20 |
|
37 | | -The maximum amount of uSTX (1 STX = 1,000,000 uSTX) that can be locked in the transaction that uses this signature. For example, if calling `stack-increase`, this dictates the maximum amount of uSTX that can be used to add more locked STX. |
38 | | -{% endstep %} |
39 | | - |
40 | | -{% step %} |
41 | | -#### auth-id |
42 | | - |
43 | | -A random integer that prevents the same signature from being reused, similar to how nonces are used with transactions. Must be less than 14 characters. |
44 | | -{% endstep %} |
45 | | -{% endstepper %} |
46 | | - |
47 | | -### Signature Message Contents |
48 | | - |
49 | | -The signer signature's message hash is created using the following data: |
| 21 | +```sh |
| 22 | +stacks-signer generate-staking-signature \ |
| 23 | + --config /etc/stacks-signer/config.toml \ |
| 24 | + --signer-manager <manager-principal> \ |
| 25 | + --auth-id <unique-id> \ |
| 26 | + --json |
| 27 | +``` |
50 | 28 |
|
51 | | -* **`method`**: the stacking function that is allowed to use this signature. Valid options: |
52 | | - * `stack-stx`: for solo stacking |
53 | | - * `stack-extend`: for extending a solo stacking lock |
54 | | - * `stack-increase`: for increasing a solo stacking position |
55 | | - * `agg-commit`: for `stack-aggregation-commit-indexed` (pool operators) |
56 | | - * `agg-increase`: for `stack-aggregation-increase` (pool operators) |
57 | | -* **`max-amount`**: the maximum uSTX allowed (described above) |
58 | | -* **`auth-id`**: the random integer (described above) |
59 | | -* **`period`**: a value between 1 and 12 indicating how many cycles the stacker is allowed to lock for. For `agg-commit`, this must equal 1. |
60 | | -* **`reward-cycle`**: the reward cycle in which the stacking transaction can be confirmed. See the important note below about how this differs between solo and delegated stacking. |
61 | | -* **`pox-address`**: the Bitcoin address allowed for receiving rewards |
62 | | -* **`config`**: the signer configuration file path where the `stacks_private_key` is located (used by the CLI for signing) |
| 29 | +Adjust the config path to match your setup. Replace `<manager-principal>` with your signer-manager's contract principal. Replace `<unique-id>` with a uint you have not used before for this signer key and manager pair. |
63 | 30 |
|
64 | | -{% hint style="warning" %} |
65 | | -**reward-cycle differences:** |
66 | | -* For **solo stacking** operations (`stack-stx`, `stack-extend`, `stack-increase`): set this to the **current** reward cycle. |
67 | | -* For **`stack-aggregation-commit-indexed`**: set this to the **target** reward cycle (typically current cycle + 1, or a future cycle you are committing to). This is because pool operators can commit for future cycles, not just the next one. |
68 | | -{% endhint %} |
| 31 | +The command prints JSON. It contains your public key and the signature. It does not contain your private key, which is what makes it safe to move to whatever you use to build the next transaction. |
69 | 32 |
|
70 | | -{% hint style="warning" %} |
71 | | -Every field in the signature must **exactly match** the corresponding fields in your stacking transaction. A mismatch will cause the transaction to fail. |
| 33 | +{% hint style="danger" %} |
| 34 | +Never paste a signer private key or seed phrase into a website, a chat, or an email. Generate the grant on the signer host. |
72 | 35 | {% endhint %} |
73 | 36 |
|
74 | | -*** |
75 | | - |
76 | | -## Generating Signatures |
| 37 | +For testing, the [signer key helper](https://stx.fan/signer/02-signer-key-helper.html) produces the same JSON offline. It is useful for seeing how the grant is built. For a real deployment, prefer the CLI so the private key never leaves the host. |
77 | 38 |
|
78 | | -You have several options for generating signer signatures. Choose the one that best fits your setup. |
| 39 | +### Submit it on-chain |
79 | 40 |
|
80 | | -### Using the stacks-signer CLI |
| 41 | +The grant is signed off-chain. It is submitted on-chain by the signer-manager contract itself, in two calls: |
81 | 42 |
|
82 | | -If you have your signer configured and running, you can use the `stacks-signer` CLI to generate signatures. You can SSH into your running signer or use the CLI locally with a matching configuration file. |
| 43 | +1. `grant-signer-key` records the grant. |
| 44 | +2. `register-signer` binds the manager to the key. |
83 | 45 |
|
84 | | -{% hint style="info" %} |
85 | | -Having a matching configuration file is important to ensure the signer public key in your stacking transactions is the same as in your hosted signer. |
86 | | -{% endhint %} |
| 46 | +Both require `contract-caller` to equal the signer-manager. An account cannot submit either on the contract's behalf. Trying fails with `ERR_UNAUTHORIZED_SIGNER_REGISTRATION` (u26). |
87 | 47 |
|
88 | | -```bash |
89 | | -stacks-signer generate-stacking-signature \ |
90 | | - --method stack-stx \ |
91 | | - --max-amount 1000000000000 \ |
92 | | - --auth-id 71948271489 \ |
93 | | - --period 1 \ |
94 | | - --reward-cycle 100 \ |
95 | | - --pox-address bc1... \ |
96 | | - --config ./config.toml \ |
97 | | - --json |
98 | | -``` |
| 48 | +The reference signer-manager wraps both into a single `register-self` entrypoint, so in practice this is one transaction. See [Deploy a Signer Manager Contract](https://docs.stacks.co/operate/deploy-a-signer-manager-contract). |
99 | 49 |
|
100 | | -* `--json` optionally outputs the result in JSON format |
| 50 | +The [register-self page](https://stx.fan/signer/04-register-self.html) takes a completed grant and builds that transaction for you. |
101 | 51 |
|
102 | | -You can generate a random 32-bit integer for `auth-id` with: |
| 52 | +### What the grant contains |
103 | 53 |
|
104 | | -```bash |
105 | | -python3 -c 'import secrets; print(secrets.randbits(32))' |
106 | | -``` |
| 54 | +The signed message carries two application fields and nothing else: |
107 | 55 |
|
108 | | -The CLI outputs a JSON object: |
109 | | - |
110 | | -```json |
111 | | -{ |
112 | | - "authId": "71948271489", |
113 | | - "maxAmount": "1000000000000", |
114 | | - "method": "stack-stx", |
115 | | - "period": 1, |
116 | | - "poxAddress": "bc1...", |
117 | | - "rewardCycle": 100, |
118 | | - "signerKey": "03a3...", |
119 | | - "signerSignature": "bbbbbbbbbbb" |
120 | | -} |
| 56 | +```clarity |
| 57 | +message: { topic: "grant-authorization", signer-manager: <principal>, auth-id: <uint> } |
| 58 | +domain: { name: "pox-5-signer", version: "1.0.0", chain-id: <uint> } |
121 | 59 | ``` |
122 | 60 |
|
123 | | -Use this JSON when making stacking transactions. This output can be pasted directly into Leather Earn. |
| 61 | +* **`signer-manager`** is the contract principal the key is bound to. Every staker who later stakes through that manager relies on this one grant. |
| 62 | +* **`auth-id`** is a replay guard. The tuple `(signer-key, signer-manager, auth-id)` can be consumed exactly once. Reusing it fails with `ERR_SIGNER_KEY_GRANT_USED` (u12). Pick a fresh value to issue a new grant. |
124 | 63 |
|
125 | | -{% hint style="info" %} |
126 | | -The address you use for stacking transactions may differ from your signer address. See [Key and Address Rotation](key-and-address-rotation.md) for more details on the relationship between signer keys and pool operator keys. |
127 | | -{% endhint %} |
| 64 | +There is no `max-amount`, `period`, `reward-cycle`, or `pox-addr` field. The grant is not scoped to a single call. |
128 | 65 |
|
129 | | -### Using stacks.js |
| 66 | +### One grant covers every entrypoint |
130 | 67 |
|
131 | | -The [@stacks/stacking](https://www.npmjs.com/package/@stacks/stacking) NPM package provides a `signPoxSignature` function to generate signer signatures programmatically. |
| 68 | +| Entrypoint | Needs an active grant? | |
| 69 | +| -------------------------- | --------------------------------------------------------------------------------------- | |
| 70 | +| `stake` | Yes | |
| 71 | +| `stake-update` | Yes, the same grant | |
| 72 | +| `register-for-bond` | Yes, the same grant | |
| 73 | +| `update-bond-registration` | Yes. Rotating to a different signer-manager requires that manager to hold its own grant | |
| 74 | +| `register-signer` | No. Gated on `contract-caller == signer-manager` instead | |
132 | 75 |
|
133 | | -More information and code samples can be found on [Hiro's Nakamoto docs](https://docs.hiro.so/nakamoto/stacks-js). |
| 76 | +Lock period is bounded by `MAX_NUM_CYCLES` (96) at the `stake` and `register-for-bond` level, not by anything in the grant. |
134 | 77 |
|
135 | | -### Using Degen Lab's stacking.tools |
| 78 | +### Revoke a grant |
136 | 79 |
|
137 | | -Degen Lab provides a [signature generation tool](https://signature.stacking.tools/) that generates signatures using their signer. This is the quickest and simplest option. Visit the tool and enter the relevant parameters. |
| 80 | +`revoke-signer-grant` removes the binding. It takes `(signer-manager, signer-key)` in that order. |
138 | 81 |
|
139 | | -### Using Leather Earn |
| 82 | +It must be sent directly by the Stacks principal derived from the signer key. No SIP-018 message is needed, and it cannot be forwarded through another contract. Calling it from any other principal fails with `ERR_UNAUTHORIZED` (u1). |
140 | 83 |
|
141 | | -{% hint style="info" %} |
142 | | -At the time of writing, this has only been tested using the [Leather](https://leather.io/) wallet. |
143 | | -{% endhint %} |
144 | | - |
145 | | -Visit [earn.leather.io](https://earn.leather.io/) to generate a signer key signature. Make sure you're connected to the correct network. |
146 | | - |
147 | | -To generate a signer key signature, log into Leather with the same secret key used to generate your signer key (not your pool operator address). Then click the "Signer key signature" link at the bottom of the page. |
148 | | - |
149 | | -The fields are: |
| 84 | +Revoking is not a kill switch. It stops the manager accepting new stake. Existing positions are left intact and wind down as their locks expire. |
150 | 85 |
|
151 | | -* **Reward cycle**: |
152 | | - * For solo stacking transactions: must equal the current reward cycle. The field defaults to the current cycle. |
153 | | - * For `stack-aggregation-commit-indexed`: must equal the cycle used in that function's "reward cycle" argument. Typically current\_cycle + 1. |
154 | | -* **Bitcoin address**: the PoX reward address |
155 | | -* **Topic**: the stacking function that will use this signature |
156 | | -* **Max amount**: max amount of STX that can be used. Defaults to "max possible amount". |
157 | | -* **Auth ID**: defaults to a random integer |
158 | | -* **Duration**: must match the number of cycles used in the stacking transaction. For `stack-aggregation-commit-indexed`, use "1". |
| 86 | +### Doing it with the SDK |
159 | 87 |
|
160 | | -Click "generate signature" to popup a Leather signing window. After signing, Leather Earn will display your signer key and signature. You can click the "copy" icon next to "signer details to share with stackers" to copy a JSON string that can be pasted directly into the stacking transaction form. |
| 88 | +`@stacks/bitcoin-staking` exposes the same flow for tooling: |
161 | 89 |
|
162 | | -### Using a Hardware or Software Wallet |
| 90 | +```typescript |
| 91 | +import { |
| 92 | + signSignerGrant, |
| 93 | + fetchEligibleGrantSignerKey, |
| 94 | + buildGrantSignerKey, |
| 95 | +} from '@stacks/bitcoin-staking'; |
163 | 96 |
|
164 | | -If your signer is configured with a `stacks_private_key`, you can use that key in a wallet to generate stacking signatures. |
| 97 | +const signerSignature = signSignerGrant({ signerManager, authId, chainId, privateKey }); |
165 | 98 |
|
166 | | -If you used [@stacks/cli](https://docs.hiro.so/get-started/command-line-interface) to generate the key, the CLI also outputs a mnemonic (seed phrase) that can be imported into a wallet. Because the Stacks CLI uses the standard derivation path, any Stacks wallet will default to the same private key when imported. |
| 99 | +const eligible = await fetchEligibleGrantSignerKey({ |
| 100 | + signerKey, signerManager, authId, signerSignature, network, |
| 101 | +}); |
167 | 102 |
|
168 | | -#### Setting up a wallet for signature generation |
169 | | - |
170 | | -{% stepper %} |
171 | | -{% step %} |
172 | | -#### Generate the keypair and configure signer |
| 103 | +const tx = await buildGrantSignerKey({ |
| 104 | + signerKey, signerManager, authId, signerSignature, publicKey, fee, nonce, network, |
| 105 | +}); |
| 106 | +``` |
173 | 107 |
|
174 | | -1. Use `@stacks/cli` to generate the keychain and private key. |
175 | | - * When using a hardware wallet, it's typically better to generate the mnemonic on the device itself. However, the signer software needs the raw private key, which hardware wallets don't export by design. |
176 | | -2. Take the `privateKey` from the CLI output and add it to your signer's configuration. |
177 | | -3. Take the mnemonic (24 words) and either: |
178 | | - * Set up a new hardware wallet with this mnemonic, or |
179 | | - * Store it securely (e.g., in a password manager). Import it into Leather or XVerse when you need to generate signatures. |
180 | | -{% endstep %} |
| 108 | +The package also provides `buildSignerGrantMessage`, `computeSignerGrantHash`, and `verifySignerGrant` as pure functions, plus `fetchVerifySignerKeyGrant` and `fetchSignerKeyGrantUsed` for reading on-chain state. |
181 | 109 |
|
182 | | -{% step %} |
183 | | -#### Generate signatures when needed |
| 110 | +### What changed from PoX-4 |
184 | 111 |
|
185 | | -1. Set up your wallet with your signer key's private key: |
186 | | - * Set up Leather with a Ledger hardware wallet, or |
187 | | - * Import your mnemonic into Leather, XVerse, or another Stacks wallet |
188 | | -2. Open an app with stacking signature functionality (e.g., Leather Earn) |
189 | | -3. Connect your wallet (sign in) |
190 | | -4. Enter your PoX address and submit. The app will prompt you to sign |
191 | | -5. Confirm the signature (if using a Ledger, confirm on the device) |
192 | | -6. The app displays your signer key and signature |
193 | | -7. Use the signer key and signature in your stacking transaction |
194 | | -{% endstep %} |
195 | | -{% endstepper %} |
| 112 | +If you ran a signer before Epoch 4.0, you generated a fresh signature for every call, scoped to that call: |
196 | 113 |
|
197 | | -*** |
| 114 | +```sh |
| 115 | +# PoX-4. No longer valid. |
| 116 | +stacks-signer generate-stacking-signature \ |
| 117 | + --method stack-stx --max-amount 1000000000000 --auth-id 71948271489 \ |
| 118 | + --period 1 --reward-cycle 100 --pox-address bc1... \ |
| 119 | + --config ./config.toml --json |
| 120 | +``` |
198 | 121 |
|
199 | | -## Signature Requirements by Function |
| 122 | +Three things changed. The command was renamed from `generate-stacking-signature` to `generate-staking-signature`. Five scoping flags collapsed into a single `--signer-manager`. And the result is reusable, so you run it once per manager rather than once per transaction. |
200 | 123 |
|
201 | | -| Function | Method | Period | Reward Cycle | |
202 | | -|----------|--------|--------|-------------| |
203 | | -| `stack-stx` | `stack-stx` | Lock period (1–12) | Current cycle | |
204 | | -| `stack-extend` | `stack-extend` | Extend count (1–12) | Current cycle | |
205 | | -| `stack-increase` | `stack-increase` | Current lock period | Current cycle | |
206 | | -| `stack-aggregation-commit-indexed` | `agg-commit` | 1 | Target cycle (e.g., current + 1) | |
207 | | -| `stack-aggregation-increase` | `agg-increase` | 1 | Target cycle | |
| 124 | +The old per-function signature table no longer applies. There are no `--method`, `--max-amount`, `--period`, `--reward-cycle`, or `--pox-address` flags, because a grant authorises a signer-manager rather than a specific call. |
0 commit comments