Skip to content

Commit 956adf9

Browse files
werner-stacksgitbook-bot
authored andcommitted
GITBOOK: Add a stacks-cli deploy flow, and point the grant guide at it
1 parent 352be27 commit 956adf9

2 files changed

Lines changed: 157 additions & 3 deletions

File tree

‎docs/operate/deploy-a-signer-manager-contract.md‎

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: >-
77
# Deploy a Signer Manager Contract
88

99
{% hint style="info" %}
10-
All Clarity references on this page are pinned to [`pox-5.clar` at `stacks-core` 4.0.1](https://github.com/stacks-network/stacks-core/blob/4.0.1/stackslib/src/chainstate/stacks/boot/pox-5.clar) and to a [pinned mainnet build of the reference signer-manager](https://github.com/stx-labs/signer-sidekick/blob/214c67eae2f1ce1c3c818ab6528ce4f2e1bdc22a/contracts/reference-manager/generated/mainnet/signer-manager.clar).
10+
All Clarity references on this page are pinned to [`pox-5.clar` at `stacks-core` 4.0.1](https://github.com/stacks-network/stacks-core/blob/4.0.1/stackslib/src/chainstate/stacks/boot/pox-5.clar) and to a [pinned mainnet build of the reference signer-manager](https://github.com/stx-labs/signer-sidekick/blob/11f8ff79e309db14357c4adfbbe31e1aeb7cd17e/contracts/reference-manager/generated/mainnet/signer-manager.clar).
1111
{% endhint %}
1212

1313
### Who needs this page
@@ -68,10 +68,14 @@ Your contract must also gate `validate-stake!` so only pox-5 can call it. The re
6868

6969
### Deploy your contract
7070

71-
Deploy a Clarity contract implementing `signer-manager-trait`. The [pinned mainnet reference manager](https://github.com/stx-labs/signer-sidekick/blob/214c67eae2f1ce1c3c818ab6528ce4f2e1bdc22a/contracts/reference-manager/generated/mainnet/signer-manager.clar) is the recommended starting point. It is generated for mainnet, so its pox-5 and sBTC principals are already correct.
71+
Deploy whichever signer-manager you have reviewed. The [reference implementation](https://github.com/stx-labs/signer-sidekick/blob/11f8ff79e309db14357c4adfbbe31e1aeb7cd17e/contracts/reference-manager/generated/mainnet/signer-manager.clar) is the recommended starting point today, and more will follow, including [one with a lower fee ceiling](https://explorer.hiro.so/txid/SPMPMA1V6P430M8C91QS1G9XJ95S59JS1TZFZ4Q4.fastpool-max500-signer-manager?chain=mainnet\&tab=sourceCode) (`MAX_FEE_BIPS u500`).
72+
73+
The fee ceiling and the admin model are properties of the contract you deploy, not of pox-5, and they cannot be changed afterwards.
74+
75+
Mainnet and testnet builds are not interchangeable. The embedded pox-5 boot principal and the sBTC contract addresses differ, so a mainnet build fails analysis on testnet.
7276

7377
{% hint style="danger" %}
74-
Do not deploy the `stacks-core` test fixture under `contrib/core-contract-tests/`. Its pox-5 and sBTC principals do not match mainnet. Verify the canonical hash of whatever source you deploy against the expected value before broadcasting, and confirm the deployed principal on-chain afterwards. A transaction ID confirms submission, not success.
78+
Do not deploy the `stacks-core` test fixture under `contrib/core-contract-tests/`. Its pox-5 and sBTC principals do not match mainnet. Confirm the deployed principal on-chain afterwards. A transaction ID confirms submission, not success.
7579
{% endhint %}
7680

7781
The reference manager's deployer becomes its first admin automatically (`(map-set admins tx-sender true)`).
@@ -82,6 +86,150 @@ The reference manager's deployer becomes its first admin automatically (`(map-se
8286
Ledger Stacks App versions through `0.26.17` cannot sign a deployment payload that carries an explicit Clarity version, so leave any "force Clarity 6 payload" option switched off. With the version omitted, the node defaults the contract to Clarity 6, which it does since Epoch 4.0 activated. The Leather extension omits the version for you from version `6.107.0` onward. Stacks App `0.27.x` is expected to remove the restriction.
8387
{% endhint %}
8488

89+
### How to deploy with stacks-cli
90+
91+
`stacks-cli` is the binary from `stacks-core`. It ships in the node image alongside `stacks-signer`, so there is nothing to build.
92+
93+
You need a reviewed signer-manager `.clar` file, a hot key to deploy with, and a cold key to hand admin control to. **Fund the cold key too**: it signs the last step and pays that fee itself.
94+
95+
The CLI cannot sign with a hardware wallet or a multisig, so the deploying key is always hot and the rotation in steps 4 and 5 is part of the deployment rather than an optional hardening pass.
96+
97+
Examples use `docker`. For Fedora, RHEL and immutable Linux, see the note at the end of this section.
98+
99+
#### Setup
100+
101+
```bash
102+
IMG=ghcr.io/stacks-network/stacks-core:4.0.1@sha256:9fbe2a3b3b7dba73eec883873a5cf91f4b2dbfa28fa51751851e5586ec4791b6
103+
read -rs STX_SK && export STX_SK
104+
```
105+
106+
`read -rs` takes the key without echoing it and keeps it out of shell history.
107+
108+
Both variables live only in the current shell. Re-run this block in a new terminal. If you forget, the failure gives no hint: with `$IMG` empty, the runtime reads the next word as the image name and reports `short-name resolution enforced but cannot prompt without a TTY`.
109+
110+
#### How broadcasting works
111+
112+
Every command below writes a signed transaction as hex to stdout and stops. The CLI never broadcasts. Each step therefore ends the same way:
113+
114+
```bash
115+
python3 -c "import binascii;open('tx.bin','wb').write(binascii.unhexlify(open('tx.hex').read().strip()))"
116+
curl -sS -X POST -H "Content-Type: application/octet-stream" --data-binary @tx.bin \
117+
https://api.mainnet.hiro.so/v2/transactions # or your own node RPC
118+
```
119+
120+
A transaction ID confirms submission, not success. Confirm each step landed before starting the next, and increment the nonce as you go.
121+
122+
#### 1. Deploy
123+
124+
```bash
125+
docker run --rm -e STX_SK -v ./signer-manager.clar:/tmp/sm.clar:ro $IMG \
126+
stacks-cli publish "$STX_SK" <fee> <nonce> <contract-name> /tmp/sm.clar > tx.hex
127+
```
128+
129+
Five positionals in that order. Add `--testnet` for the default testnet, or `--testnet=0x<chain-id>` for a custom one.
130+
131+
Broadcast, then confirm the contract published and that its Clarity version is 6.
132+
133+
#### 2. Generate the signer-key grant
134+
135+
Run this on the signer host, where the key and config already live.
136+
137+
```bash
138+
docker run --rm -v /etc/stacks-signer/config.toml:/tmp/signer.toml:ro $IMG \
139+
stacks-signer generate-staking-signature \
140+
--config /tmp/signer.toml \
141+
--signer-manager <deployer-address>.<contract-name> \
142+
--auth-id <unique-uint> \
143+
--json
144+
```
145+
146+
Output is JSON containing `signerKey`, `signerSignature` and `authId`. It contains no private key, which is what makes it safe to move to wherever you build the next transaction.
147+
148+
`auth-id` is a replay guard. The tuple of signer key, signer manager and auth-id can be consumed once.
149+
150+
**The grant is bound to the network in your config.** There is no chain-id flag; it comes from the `network` field. A grant generated against the wrong network aborts at the next step with `ERR_INVALID_SIGNATURE_PUBKEY (u14)`, which reads as a wrong-key problem and sends you looking in the wrong place.
151+
152+
#### 3. Register the manager against the signer key
153+
154+
Admin-only, so run it while the deploying key is still the admin. The three values come from step 2.
155+
156+
```bash
157+
docker run --rm -e STX_SK $IMG \
158+
stacks-cli contract-call "$STX_SK" <fee> <nonce> \
159+
<deployer-address> <contract-name> register-self \
160+
-e \'<deployer-address>.<contract-name> \
161+
-e 0x<signerKey> \
162+
-e u<authId> \
163+
-e 0x<signerSignature> > tx.hex
164+
```
165+
166+
The first argument is a trait reference, passed as the contract's own principal.
167+
168+
#### 4. Add the cold admin, from the hot deploy key
169+
170+
```bash
171+
docker run --rm -e STX_SK $IMG \
172+
stacks-cli contract-call "$STX_SK" <fee> <nonce> \
173+
<deployer-address> <contract-name> update-admin \
174+
-e \'SP<COLD-ADMIN-PRINCIPAL> -e true > tx.hex
175+
```
176+
177+
Arguments come in flag and value pairs, `-e` to evaluate or `-x` for a hex-serialized Clarity value. The backslash-escaped apostrophe is the form the tool's own help uses for a principal literal.
178+
179+
#### 5. Remove the hot key, from the cold admin
180+
181+
Load the cold key into `STX_SK` and repeat with `false`. This proves the cold key can sign and retires the hot key in one call.
182+
183+
```bash
184+
docker run --rm -e STX_SK $IMG \
185+
stacks-cli contract-call "$STX_SK" <fee> <nonce> \
186+
<deployer-address> <contract-name> update-admin \
187+
-e \'SP<HOT-DEPLOYER-PRINCIPAL> -e false > tx.hex
188+
```
189+
190+
**Keep the signer key.** Rotating admin does not transfer everything. Revoking a grant is authorised by the signer key, not by admin status, so a cold admin cannot revoke.
191+
192+
There is no on-chain guard against removing the last admin. `(update-admin tx-sender false)` as the only admin permanently bricks every admin function, including `withdraw-fees`, and any accrued sBTC becomes unreachable. The ordering above is what protects against it: a wallet that is not an admin fails with `ERR_UNAUTHORIZED_ADMIN (u1002)` and changes nothing, so whoever succeeds at the removal is demonstrably not the last admin.
193+
194+
#### Revoking, from the CLI
195+
196+
Not part of setup. This call goes to `pox-5`, not to your manager, and is authorised by the signer key rather than by admin status.
197+
198+
```bash
199+
docker run --rm -e STX_SK $IMG \
200+
stacks-cli contract-call "$STX_SK" <fee> <nonce> \
201+
SP000000000000000000002Q6VF78 pox-5 revoke-signer-grant \
202+
-e \'<deployer-address>.<contract-name> \
203+
-e 0x<signerKey> > tx.hex
204+
```
205+
206+
Argument order is `signer-manager` then `signer-key`, the reverse of `grant-signer-key`. Getting it backwards aborts with `ERR_UNAUTHORIZED (u1)`, which looks like a permissions problem.
207+
208+
**The registration is expected to persist.** `get-signer-info` still resolves your manager to that signer key after a successful revoke. Check grant state, not registration. See [Revoke a grant](deploy-a-signer-manager-contract.md#revoke-a-grant-revoke-signer-grant) below for why.
209+
210+
#### On Fedora, RHEL and immutable Linux
211+
212+
Substitute `podman` for `docker`. The commands are otherwise identical, with one exception.
213+
214+
SELinux relabels bind mounts, so the two steps that mount a file, the deploy and the grant, need `z`:
215+
216+
```bash
217+
podman run --rm -e STX_SK -v ./signer-manager.clar:/tmp/sm.clar:ro,z $IMG \
218+
stacks-cli publish "$STX_SK" <fee> <nonce> <contract-name> /tmp/sm.clar > tx.hex
219+
```
220+
221+
Without it the container cannot read the file and the tool reports `IO error reading CLI input: Permission denied (os error 13)`.
222+
223+
#### Which key does which call
224+
225+
`register-self`, `update-admin`, `update-fees`, `withdraw-fees` and `sweep-fee-refunds` are admin-gated. `revoke-signer-grant` is gated on the signer key. In a normal setup those are different keys and different people.
226+
227+
Admin calls cannot be proxied: `authorize-admin` asserts `contract-caller` equals `tx-sender`, so an admin action must be a direct top-level call from the admin principal. A CLI-signed transaction satisfies this; a call routed through another contract does not.
228+
229+
#### Why there is no Clarity version flag
230+
231+
`publish` emits payload type `0x01` with no version byte, and the node applies its current default, which is Clarity 6 since Epoch 4.0.
232+
85233
### Register the signer key: `register-signer`
86234

87235
After deployment, the signer-manager contract itself calls `pox-5::register-signer` to bind itself to a signer key.

‎docs/operate/stacking-stx/generate-signer-signature.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ PoX-5 uses a one-time **signer-key grant**. You generate one grant per signer-ma
1414

1515
The grant is what connects 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).
1616

17+
{% hint style="info" %}
18+
Doing the whole setup from the command line? [Deploy a Signer Manager Contract](../deploy-a-signer-manager-contract.md) runs this step in sequence with the deploy, registration and admin rotation.
19+
{% endhint %}
20+
1721
### Generate the grant
1822

1923
Run this on the signer host, where the private key already lives:
@@ -63,6 +67,8 @@ domain: { name: "pox-5-signer", version: "1.0.0", chain-id: <uint> }
6367

6468
Those two fields are the whole scope. A grant authorises a signer-manager and stands until you revoke it.
6569

70+
The `chain-id` in the domain comes from the `network` field in your signer config. A grant generated against the wrong network fails at `grant-signer-key` with `ERR_INVALID_SIGNATURE_PUBKEY` (u14), which reads as a wrong-key problem rather than a wrong-network one.
71+
6672
### One grant covers every entrypoint
6773

6874
| Entrypoint | Needs an active grant? |

0 commit comments

Comments
 (0)