Skip to content

Commit f276cd7

Browse files
werner-stacksgitbook-bot
authored andcommitted
GITBOOK: Rewrite Generate a Signer Signature around the one-time signer-key grant
1 parent ee45f73 commit f276cd7

1 file changed

Lines changed: 79 additions & 162 deletions

File tree

Lines changed: 79 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,124 @@
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+
---
106

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
128

139
{% 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.
1511
{% endhint %}
1612

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.
2714

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).
3016

31-
A signature that demonstrates you actually control your `signer-key`.
32-
{% endstep %}
17+
### Generate the grant
3318

34-
{% step %}
35-
#### max-amount
19+
Run this on the signer host, where the private key already lives:
3620

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+
```
5028

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.
6330

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.
6932

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.
7235
{% endhint %}
7336

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.
7738

78-
You have several options for generating signer signatures. Choose the one that best fits your setup.
39+
### Submit it on-chain
7940

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:
8142

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.
8345

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).
8747

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).
9949

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.
10151

102-
You can generate a random 32-bit integer for `auth-id` with:
52+
### What the grant contains
10353

104-
```bash
105-
python3 -c 'import secrets; print(secrets.randbits(32))'
106-
```
54+
The signed message carries two application fields and nothing else:
10755

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> }
12159
```
12260

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.
12463

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.
12865

129-
### Using stacks.js
66+
### One grant covers every entrypoint
13067

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 |
13275

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.
13477

135-
### Using Degen Lab's stacking.tools
78+
### Revoke a grant
13679

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.
13881

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).
14083

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.
15085

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
15987

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:
16189

162-
### Using a Hardware or Software Wallet
90+
```typescript
91+
import {
92+
signSignerGrant,
93+
fetchEligibleGrantSignerKey,
94+
buildGrantSignerKey,
95+
} from '@stacks/bitcoin-staking';
16396

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 });
16598

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+
});
167102

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+
```
173107

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.
181109

182-
{% step %}
183-
#### Generate signatures when needed
110+
### What changed from PoX-4
184111

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:
196113

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+
```
198121

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.
200123

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

Comments
 (0)