From 1476a00b042fbb4b3847994a7a325451fb71b91c Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 23 Mar 2026 10:28:45 +0100 Subject: [PATCH 1/5] First version --- docs/operate/.gitbook.yaml | 16 +- docs/operate/SUMMARY.md | 8 +- docs/operate/run-a-signer/README.md | 49 +- .../best-practices-to-run-a-signer.md | 97 +++ .../run-a-signer/how-to-read-signer-logs.md | 2 +- .../run-a-signer/opsec-best-practices.md | 2 +- .../operate/run-a-signer/signer-quickstart.md | 32 +- docs/operate/stacking-stx/README.md | 117 +--- .../stacking-stx/generate-signer-signature.md | 207 ++++++ .../stacking-stx/key-and-address-rotation.md | 105 +++ ...-stacked-position.md => operate-a-pool.md} | 498 +++++++------- .../stacking-stx/operate-a-stacking-pool.md | 587 ----------------- docs/operate/stacking-stx/solo-stack.md | 614 ------------------ docs/operate/stacking-stx/solo-stacking.md | 388 +++++++++++ .../operate/stacking-stx/stack-with-a-pool.md | 169 ++++- docs/operate/stacking-stx/stop-stacking.md | 67 -- 16 files changed, 1231 insertions(+), 1727 deletions(-) create mode 100644 docs/operate/stacking-stx/generate-signer-signature.md create mode 100644 docs/operate/stacking-stx/key-and-address-rotation.md rename docs/operate/stacking-stx/{increase-stacked-position.md => operate-a-pool.md} (50%) delete mode 100644 docs/operate/stacking-stx/operate-a-stacking-pool.md delete mode 100644 docs/operate/stacking-stx/solo-stack.md create mode 100644 docs/operate/stacking-stx/solo-stacking.md delete mode 100644 docs/operate/stacking-stx/stop-stacking.md diff --git a/docs/operate/.gitbook.yaml b/docs/operate/.gitbook.yaml index f81466218e..b8bd269079 100644 --- a/docs/operate/.gitbook.yaml +++ b/docs/operate/.gitbook.yaml @@ -24,8 +24,16 @@ redirects: guides-and-tutorials/best-practices-to-snapshot-the-chainstate: snapshot-the-chainstate.md # guides-and-tutorials/stack-stx to operate/stacking-stx redirects - guides-and-tutorials/stack-stx/increase-stacking: stacking-stx/increase-stacked-position.md - guides-and-tutorials/stack-stx/operate-a-pool: stacking-stx/operate-a-stacking-pool.md + guides-and-tutorials/stack-stx/increase-stacking: stacking-stx/solo-stacking.md + guides-and-tutorials/stack-stx/operate-a-pool: stacking-stx/operate-a-pool.md guides-and-tutorials/stack-stx/stack-with-a-pool: stacking-stx/stack-with-a-pool.md - guides-and-tutorials/stack-stx/stacking-flow: stacking-stx/solo-stack.md - guides-and-tutorials/stack-stx/stop-stacking: stacking-stx/stop-stacking.md + guides-and-tutorials/stack-stx/stacking-flow: stacking-stx/solo-stacking.md + guides-and-tutorials/stack-stx/stop-stacking: stacking-stx/solo-stacking.md + + # Old stacking-stx paths to new structure + stacking-stx/solo-stack: stacking-stx/solo-stacking.md + stacking-stx/operate-a-stacking-pool: stacking-stx/operate-a-pool.md + stacking-stx/stack-with-a-pool: stacking-stx/stack-with-a-pool.md + stacking-stx/delegated-stacking: stacking-stx/stack-with-a-pool.md + stacking-stx/increase-stacked-position: stacking-stx/solo-stacking.md + stacking-stx/stop-stacking: stacking-stx/solo-stacking.md diff --git a/docs/operate/SUMMARY.md b/docs/operate/SUMMARY.md index 8fe636b10a..483eb9189d 100644 --- a/docs/operate/SUMMARY.md +++ b/docs/operate/SUMMARY.md @@ -25,8 +25,8 @@ * [Best Practices for Running a sBTC Signer](run-a-sbtc-signer/best-practices-for-running-an-sbtc-signer.md) * [Snapshot the Chainstate](snapshot-the-chainstate.md) * [Stacking STX](stacking-stx/README.md) - * [Solo Stack](stacking-stx/solo-stack.md) - * [Operate a Stacking Pool](stacking-stx/operate-a-stacking-pool.md) + * [Solo Stacking](stacking-stx/solo-stacking.md) * [Stack with a Pool](stacking-stx/stack-with-a-pool.md) - * [Increase Stacked Position](stacking-stx/increase-stacked-position.md) - * [Stop Stacking](stacking-stx/stop-stacking.md) + * [Operate a Pool](stacking-stx/operate-a-pool.md) + * [Generate a Signer Signature](stacking-stx/generate-signer-signature.md) + * [Key and Address Rotation](stacking-stx/key-and-address-rotation.md) diff --git a/docs/operate/run-a-signer/README.md b/docs/operate/run-a-signer/README.md index 1115162fea..83ba0559c8 100644 --- a/docs/operate/run-a-signer/README.md +++ b/docs/operate/run-a-signer/README.md @@ -4,9 +4,9 @@ ### How to Use This Guide -If you are not familiar with the concept of signing and stacking, and how they work together, be sure to check out the [Stackers and Signing concept guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing). +This guide is a step-by-step walkthrough for setting up and running a signer. It covers only the signer infrastructure — the signer software and the Stacks node it connects to. -This guide is a step-by-step walkthrough for setting up and running a signer. If you need to troubleshoot your signer setup, see the Signer Troubleshooting section. If you need to Stack your STX, or have questions about how that process works, check out the Stack STX guide. +If you are not familiar with the concept of signing, be sure to check out the [Stackers and Signing concept guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing). ### Background and High-Level Process @@ -18,7 +18,6 @@ This doc provides instructions to set up both using either Docker or the release * Docker and basic knowledge of pulling and running images * Basic knowledge of [Stacks accounts](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/network-fundamentals/wallets-and-accounts) -* Basic knowledge of [stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) and the stacking flow {% stepper %} {% step %} @@ -27,7 +26,7 @@ This doc provides instructions to set up both using either Docker or the release Quick reference of major setup steps prior to launching a signer. * Ensure your system meets the [minimum system requirements](./#minimum-system-requirements). -* Acquire Docker and basic knowledge of Stacks accounts, stacking, and the Nakamoto stacking flow (links above). +* Acquire Docker and basic knowledge of Stacks accounts (links above). {% endstep %} {% step %} @@ -84,15 +83,6 @@ Quick reference of major setup steps prior to launching a signer. * Check Stacks node logs for successful connection to the signer. * Confirm the node is syncing Bitcoin headers properly. {% endstep %} - -{% step %} -#### Signer Checklist — Setup Stacks Accounts - -* Set up a pool operator wallet in a Stacks wallet (e.g., Leather or Xverse). -* Fund the pool operator wallet with sufficient STX for transaction fees. -* Share the pool operator wallet’s STX address with delegating parties. -* Fund your signer's STX wallet with enough STX to cover transaction fees (recommend at least 100–200 STX). -{% endstep %} {% endstepper %} ### Minimum System Requirements @@ -163,8 +153,6 @@ docker run -d \ --config /config.toml ``` -Hint about platform mismatch: - {% hint style="info" %} If you get an error about the manifest not found or the image platform not matching the host platform, you probably are running on an architecture other than x64. Add `--platform=linux/amd64` to the command (for example, on M1 Mac). {% endhint %} @@ -216,10 +204,10 @@ You may also see a warning like: WARN [1712003997.160121] [stacks-signer/src/runloop.rs:247] [signer_runloop] Signer is not registered for reward cycle 556. Waiting for confirmed registration... ``` -This means your signer is running and awaiting registration; proceed to set up the Stacks node and begin stacking. +This means your signer is running and awaiting registration; proceed to set up the Stacks node and then begin stacking. {% hint style="warning" %} -Even after you Stack, you may still see messages saying the signer is not registered for the current or next reward cycle. This is normal until the prepare phase for your chosen reward cycle; assuming you meet the stacking minimum, the signer will be registered during that phase. +You may see messages saying the signer is not registered for the current or next reward cycle. This is normal until the prepare phase for your chosen reward cycle; assuming you meet the stacking minimum, the signer will be registered during that phase. {% endhint %} *** @@ -335,29 +323,6 @@ You may see many logs while syncing; refer to How to Read the Signer Logs if con *** -## Setup Your Stacks Accounts - -{% hint style="info" %} -For more on stacking and signing relationship, see the [Stack STX](broken-reference/) guide. -{% endhint %} - -As a signer you’ll manage two Stacks accounts: - -1. A “pool operator” wallet, which commits delegated STX to your signer -2. Your signer’s wallet - -{% hint style="warning" %} -For testing, make sure you are using testnet (not mainnet). Testnet STX can be [requested from a faucet](https://explorer.hiro.so/sandbox/faucet?chain=testnet). -{% endhint %} - -### Setup Your Pool Operator Wallet - -Set up a pool operator wallet using any Stacks wallet, such as [Leather](https://leather.io/) or [Xverse](https://www.xverse.app/). You may generate a new account or use an existing one. Leather supports Ledger hardware wallets if you prefer. - -Fund the wallet with enough STX to cover transaction fees (testnet: faucet at https://explorer.hiro.so/sandbox/faucet?chain=testnet). - -Share this wallet’s STX address with parties that will delegate STX to you. For improved UX, you might use the helper contract allowing a BTC address for stackers ([pox4-pools](https://explorer.hiro.so/txid/SP001SFSMC2ZY76PD4M68P3WGX154XCH7NE3TYMX.pox4-pools?chain=mainnet)) and add your pool to [earn.leather.io](https://earn.leather.io/). - -*** +## Next Steps: Stacking -If you need more detailed troubleshooting or further setup examples (config snippets, sample signer-config.toml or node-config.toml), let me know which files or examples you'd like converted or added. +Once your signer and Stacks node are running and verified, the next step is to stack STX to register your signer for a reward cycle. See the [Stacking STX](../stacking-stx/) guide for complete instructions on solo stacking, delegated stacking, generating signer signatures, and managing your keys. diff --git a/docs/operate/run-a-signer/best-practices-to-run-a-signer.md b/docs/operate/run-a-signer/best-practices-to-run-a-signer.md index 02895ef1e5..8ae0eccfef 100644 --- a/docs/operate/run-a-signer/best-practices-to-run-a-signer.md +++ b/docs/operate/run-a-signer/best-practices-to-run-a-signer.md @@ -96,6 +96,103 @@ Restart the Stacks node so it runs with the enabled `event_observer`. * Ensure that multiple, trusted users can manage and maintain signer instances. * Where feasible, users should span different timezones. +### Auto-restart configuration + +Configure your signer and Stacks node to automatically restart on failure to minimize downtime. + +#### Docker + +Use the `--restart` flag when running containers to enable automatic restarts: + +```bash +docker run -d \ + --restart unless-stopped \ + -v $STX_SIGNER_CONFIG:/config.toml \ + -v $STX_SIGNER_DATA:/var/stacks \ + -p 30000:30000 \ + -e RUST_BACKTRACE=full \ + -e BLOCKSTACK_DEBUG=0 \ + --name stacks-signer \ + $IMG:$VER \ + stacks-signer run \ + --config /config.toml +``` + +The `unless-stopped` policy restarts the container automatically if it crashes or the host reboots, but not if you explicitly stop it. Apply the same policy to your Stacks node container. + +#### systemd + +If running as a binary, create a systemd service unit to handle automatic restarts. Example for the signer: + +{% code title="/etc/systemd/system/stacks-signer.service" %} +```ini +[Unit] +Description=Stacks Signer +After=network.target +StartLimitBurst=3 +StartLimitIntervalSec=300 +ConditionFileIsExecutable=/usr/local/bin/stacks-signer +ConditionPathExists=/etc/stacks/signer +ConditionFileNotEmpty=/etc/stacks/signer/signer-config.toml + +[Service] +ExecStart=/usr/local/bin/stacks-signer run --config /etc/stacks/signer/signer-config.toml +User=stacks-signer +Group=stacks-signer +Type=simple +Restart=on-failure +RestartSec=10 +TimeoutStopSec=600 +KillSignal=SIGTERM + +[Install] +WantedBy=multi-user.target +``` +{% endcode %} + +Example for the Stacks node: + +{% code title="/etc/systemd/system/stacks-node.service" %} +```ini +[Unit] +Description=Stacks Node +After=network.target +StartLimitBurst=3 +StartLimitIntervalSec=300 +ConditionFileIsExecutable=/usr/local/bin/stacks-node +ConditionFileNotEmpty=/etc/stacks/node/node-config.toml + +[Service] +ExecStart=/usr/local/bin/stacks-node start --config /etc/stacks/node/node-config.toml +User=stacks-node +Group=stacks-node +Type=simple +Restart=on-failure +RestartSec=10 +TimeoutStopSec=600 +KillSignal=SIGTERM + +[Install] +WantedBy=multi-user.target +``` +{% endcode %} + +Enable and start the services: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable stacks-signer.service stacks-node.service +sudo systemctl start stacks-signer.service +sudo systemctl start stacks-node.service +``` + +Check their status at any time: + +```bash +sudo systemctl status stacks-signer.service +sudo systemctl status stacks-node.service +``` + ### Backup signer keys in cold-storage * Keep an offline, secure backup of all Signer private keys (e.g., hardware security modules or encrypted storage devices). diff --git a/docs/operate/run-a-signer/how-to-read-signer-logs.md b/docs/operate/run-a-signer/how-to-read-signer-logs.md index 2f74b2fd13..28cb2d985f 100644 --- a/docs/operate/run-a-signer/how-to-read-signer-logs.md +++ b/docs/operate/run-a-signer/how-to-read-signer-logs.md @@ -43,7 +43,7 @@ WARN [1712003997.160121] [stacks-signer/src/runloop.rs:247] [signer_runloop] Sig Action: * If you want the signer to participate, either delegate to it or stack on your own for an upcoming reward cycle. -* For more details on stacking and registration, see the How to Stack doc: ../stack-stx/stacking-flow.md +* For more details on stacking and registration, see the [Stacking STX](../stacking-stx/) guide. ## Informational diff --git a/docs/operate/run-a-signer/opsec-best-practices.md b/docs/operate/run-a-signer/opsec-best-practices.md index 2f2f8da234..b39eb2002c 100644 --- a/docs/operate/run-a-signer/opsec-best-practices.md +++ b/docs/operate/run-a-signer/opsec-best-practices.md @@ -53,7 +53,7 @@ ConditionPathExists=/etc/stacks/signer ConditionFileNotEmpty=/etc/stacks/signer/signer-config.toml [Service] -ExecStart=/usr/local/bin/stacks-signer run --config /home/etc/stacks/signer/signer-config.toml +ExecStart=/usr/local/bin/stacks-signer run --config /etc/stacks/signer/signer-config.toml User={{ svc_user }} Group={{ svc_user }} Type=simple diff --git a/docs/operate/run-a-signer/signer-quickstart.md b/docs/operate/run-a-signer/signer-quickstart.md index eedcf5b34c..294e00d24d 100644 --- a/docs/operate/run-a-signer/signer-quickstart.md +++ b/docs/operate/run-a-signer/signer-quickstart.md @@ -422,38 +422,14 @@ If the outputs of the previous commands are correct, you can proceed and start t {% endstep %} {% step %} -#### Generate your signer signature - -In order to stack, you'll need your signer signature. The fields required are further explained in the [Generate a signer key signature](https://docs.stacks.co/guides-and-tutorials/stack-stx/stacking-flow#step-2-generate-a-signer-key-signature) guide. - -The command to generate a signature looks like this: - -```bash -~/stacks-signer/stacks-signer generate-stacking-signature \ - --method stack-stx \ - --max-amount 1000000000000 \ - --auth-id 195591226970828652622091037492597751808 \ - --period 12 \ - --reward-cycle 100 \ - --pox-address 19tg... \ - --config ~/stacks-signer/signer-config.toml \ - --json -``` - -The generated JSON can be then copy-pasted directly in the [Leather Earn](https://earn.leather.io/) website mentioned in the next step. -{% endstep %} - -{% step %} -#### Start stacking - -The simplest route is to solo stack. You can do that by using [Leather Earn](https://earn.leather.io/). Click on the 'Stack Independently' button and follow the instructions there. +#### Monitoring -If you would like to learn more about solo stacking or running a pool operator, take a look at the [Stack STX](https://docs.stacks.co/guides-and-tutorials/stack-stx) guide. +If you would like to learn more about monitoring your signer and its corresponding node, you can check the [How to Monitor a Signer](how-to-monitor-signer.md) guide. {% endstep %} {% step %} -#### Monitoring +#### Next Steps: Stacking -If you would like to learn more about monitoring your signer and its corresponding node, you can check the [How to Monitor a Signer](https://docs.stacks.co/guides-and-tutorials/running-a-signer/how-to-monitor-signer) guide. +Once your signer and Stacks node are running and verified, the next step is to stack STX to register your signer for a reward cycle. See the [Stacking STX](../stacking-stx/) guide for complete instructions on solo stacking, delegated stacking, generating signer signatures, and managing your keys. {% endstep %} {% endstepper %} diff --git a/docs/operate/stacking-stx/README.md b/docs/operate/stacking-stx/README.md index befd4f3fcf..d540941180 100644 --- a/docs/operate/stacking-stx/README.md +++ b/docs/operate/stacking-stx/README.md @@ -1,128 +1,55 @@ # Stacking STX -Stacking is an essential component of Stacks. +Stacking is the process of locking STX tokens to support the network's consensus and earn BTC rewards. If you aren't familiar with how stacking works, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) and [Stackers and Signing](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing) concept guides first. -There are three different ways you can potentially stack your STX tokens and we have a dedicated guide for each of these scenarios. - -If you aren't familiar with how stacking works, especially as it relates to signing after the Nakamoto upgrade, be sure to check out the following concept guides: - -* [Stackers and signing](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing) -* [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) - -In Nakamoto, stacking flows have significant changes in comparison to previous versions of Stacks. Because Nakamoto requires stackers to run a signer, which validates blocks produced by Stacks miners, stackers need to provide new information when making Stacking transactions. - -These changes affect both solo Stacking and delegated Stacking. This document outlines the new flows for solo stacking. The next doc outlines the flow and steps for operating a pool. - -The following sections will walk you through how to begin operating as a solo stacker. - -Stacking utilizes the `pox-4` contract. There is a detailed [walkthrough of the stacking contract](/broken/spaces/GVj1Z9vMuEOMe7oH7Wnq/pages/fc4fa1c229d8cb4deedf49de6dc1de0dc0b1ed72) that you can look at to see what functions are being called at each phase and some common errors you may encounter. This will be especially useful for pool operators who need to call these functions. - -This doc is also useful if you run into errors when calling stacking functions, as it both walks through several common error scenarios and walks through each function call so you can more easily trace what might be happening. - -Before we get into the step-by-step of how to actually stack, it's important to make sure you have an understanding of the different roles, processes and functions involved in Stacking. +Stacking utilizes the `pox-4` contract. You can view it on the [Explorer](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) and review the detailed [stacking contract walkthrough](/broken/spaces/GVj1Z9vMuEOMe7oH7Wnq/pages/fc4fa1c229d8cb4deedf49de6dc1de0dc0b1ed72) to understand what each function does. ### Definitions and Roles -* **Stacker**: an entity locking their STX to receive PoX rewards. This is a broad term including solo Stackers and Stackers who use pools. -* **Solo stacker**: an entity that locks their own STX and runs a signer. They don’t receive delegation. -* **Delegator**: a stacker who locks their STX by delegating to a pool operator that runs a signer. They don’t run the signer. -* **Pool operator**: an entity that runs a Signer and allows others to delegate their STX to them. A pool operator doesn’t need to Stack their own STX, but they can. They will also run a signer, but the pool operator and signer address may be different -* **Signer**: an entity that runs the stacks-signer software and participates in block validation. This can be either a solo Stacker or an entity receiving delegated STX. Depending on context, this may also refer to the signer software that validates blocks. +* **Stacker**: an entity locking their STX to earn PoX rewards. This is a broad term including solo stackers and delegators. +* **Solo stacker**: an entity that locks their own STX and runs a signer (or collaborates with one). They don't receive delegation. +* **Delegator**: a stacker who delegates their STX to a pool operator. They don't run a signer. +* **Pool operator**: an entity that accepts delegated STX and manages the stacking process. A pool operator runs a signer (or collaborates with one). The pool operator and signer address may be different. +* **Signer**: an entity running the stacks-signer software that participates in block validation. {% hint style="info" %} -It's important to understand that in the context of the pool operator and signer, these are likely the same _entity_ but may not be the same Stacks address. - -This distinction will be discussed further as we cover the step-by-step process below. +The pool operator and signer are often the same entity but may use different Stacks addresses. This distinction is covered in the [Key and Address Rotation](key-and-address-rotation.md) guide. {% endhint %} -Below are the primary ways you can stack: +### Guides {% stepper %} {% step %} -#### Solo stacking - -If you meet the minimum and want to [solo stack](solo-stack.md), you will either need to run a signer, collaborate with an existing one, or use [stacking.tools](https://stacking.tools/). This guide will walk you through all options. -{% endstep %} - -{% step %} -#### Operate a pool +#### [Solo Stacking](solo-stacking.md) -You can also [operate a pool](operate-a-stacking-pool.md) and have others delegate their STX to you. If you are a pool operator, you will need to run a signer, collaborate with an existing one, or use [stacking.tools](https://stacking.tools/). +If you meet the minimum STX threshold and want to stack independently. Covers how to start stacking, extend your lock period, increase your position, and stop. {% endstep %} {% step %} -#### Stack with a pool +#### [Stack with a Pool](stack-with-a-pool.md) -If you do not meet the minimum amount of STX to solo stack, you can [delegate your STX to a pool operator ](stack-with-a-pool.md)and have them stack on your behalf. The minimum stacking amount is dynamic and can be found by visiting the https://api.hiro.so/v2/pox endpoint and looking at the `min_threshold_ustx` field. Note it is denoted in uSTX (1 STX = 1,000,000 uSTX). This is the most common stacking scenario. +If you want to delegate your STX to a pool operator. Covers how to delegate, increase your delegation, revoke, and stop stacking. {% endstep %} -{% endstepper %} - -As you read through these, it may be helpful to follow along with the functions in the [pox-4 contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) to get an idea of what each function is doing. - -### Relationship between manual stacking transactions and the running signer - -This section describes the various transactions that signer entities need to make in order to be registered as a signer for a certain reward cycle. The order of operations between the automated signer and the stacking transactions that need to be done “manually” is important for ensuring that a signer is fully set up for a certain reward cycle. - -#### Prerequisite: ensure the signer is hosted and running - -It's important to emphasize the importance of getting the signer running in a hosted environment before making Stacking transactions. If the signer doesn’t do that, they run the risk of being registered as a signer without their signer software being ready to run DKG and other important consensus mechanisms. - -Some of the important things to double check to ensure the signer is “running” are: -* The signer software is configured with a private key that the user can access (either through SSH or other means). This is important because their signer needs to utilize this private key to generate signer key signatures that are used in Stacking transactions. -* The signer software is properly configured to make RPC calls to a Stacks node. This refers to the `endpoint` signer configuration field. If properly configured, there should be logs in the Stacks node that show the RPC calls being made from the signer. -* The stacks node is properly configured to send events to the signer. This refers to the \[`event_observers`] field in the Stacks Node’s configuration. If properly configured, the signer should have logs indicating that it’s receiving events from the Stacks node. - -### How a signer becomes registered in the signer set - -Each of the stacking transactions described above are done “manually”. More specifically, this means that none of these transactions are executed automatically by the signer software. The transactions must be done “out of band”. - -In order for a signer to actually be registered in a reward cycle, there need to be manual transactions made in the `pox-4` contract. While the signer software is running, it is continually polling the Stacks node and asking “am I a signer in reward cycle N?”. - -If these manual transactions are confirmed, and the signer has enough STX associated with the signer’s public key, the signer will be registered as a signer in the signer set. - -#### Solo stacking - -The workflow for solo stackers is simpler, because there are less stacking transactions that need to be made. - -For solo stacking, the only transaction that needs to be made is `stack-stx`. Included in this transaction’s payload is the signer’s public key. - -In order for the signer to be registered in reward cycle N+1, the `stack-stx` transaction must be confirmed during the first 2000 blocks of reward cycle N. The last 100 blocks of cycle N (the “prepare phase”) is where DKG occurs. - -The start of the prepare phase is when Stacks nodes determine the official signer set of the next reward cycle. - -#### Delegated Stacking - -The workflow for delegated signers is more complex, because it requires more transactions. - -This workflow is explained more in detail in the [operate a pool](operate-a-stacking-pool.md) guide, but the high-level workflow is: - -{% stepper %} {% step %} -#### Stackers delegate their STX to a pool operator +#### [Operate a Pool](operate-a-pool.md) -Stackers delegate their STX to a pool operator. +If you want to accept delegated STX and manage the stacking process. Covers how to accept delegations, commit aggregated STX, and increase committed amounts. {% endstep %} {% step %} -#### The pool operator approves specific stackers +#### [Generate a Signer Signature](generate-signer-signature.md) -The pool operator makes `delegate-stack-stx` transactions to “approve” specific stackers. This needs to be called for every individual stacker that delegates to them. +Both solo stacking and delegated stacking require signer signatures. This guide covers all signature generation methods: stacks-signer CLI, stacks.js, Leather Earn, Degen Lab's stacking.tools, and hardware wallets. {% endstep %} {% step %} -#### The pool operator commits delegated STX +#### [Key and Address Rotation](key-and-address-rotation.md) -The pool operator makes a `stack-aggregation-commit` transaction to “commit” all of its delegated STX up to this point. +How to rotate your signer key, Bitcoin reward address, and pool operator key. Includes recommendations for key separation and security. {% endstep %} {% endstepper %} -Similar to solo stacking, these steps must be made before the prepare phase of an upcoming reward cycle. - -### Once a signer is registered in the signer set - -During the prepare phase before a reward cycle, Stacks nodes automatically determine the signer set for the upcoming cycle. When this occurs, the Stacks nodes make an “internal” transaction to update the `.signers` contract with the list of signers. - -The signer software is continuously polling the Stacks node to see if it is registered for a cycle. If the signer software finds that it is registered (by matching its public key to the signers stored in the `signers` contract) it begins performing its duties as a signer. - -During the prepare phase, the signers perform DKG through StackerDB messages. Once an aggregate public key is determined, the signer automatically makes a `vote-for-aggregate-key` transaction. No out-of-band action is needed to be taken for this to occur. +{% hint style="info" %} +The minimum stacking threshold is dynamic and can be found at the [pox endpoint](https://api.mainnet.hiro.so/v2/pox) under `min_threshold_ustx` (1 STX = 1,000,000 uSTX). +{% endhint %} diff --git a/docs/operate/stacking-stx/generate-signer-signature.md b/docs/operate/stacking-stx/generate-signer-signature.md new file mode 100644 index 0000000000..f00e5131c8 --- /dev/null +++ b/docs/operate/stacking-stx/generate-signer-signature.md @@ -0,0 +1,207 @@ +# Generate a Signer Signature + +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. + +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. + +*** + +## Overview + +Signer signatures are created using a particular 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. + +{% hint style="info" %} +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). +{% endhint %} + +### Fields Passed in Stacking Transactions + +When making stacking transactions, you need to provide these signature-related fields: + +{% stepper %} +{% step %} +#### signer-key + +The public key that corresponds to the `stacks_private_key` your signer is using. +{% endstep %} + +{% step %} +#### signer-signature + +A signature that demonstrates you actually control your `signer-key`. +{% endstep %} + +{% step %} +#### max-amount + +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. +{% endstep %} + +{% step %} +#### auth-id + +A random integer that prevents re-use of a particular signature, similar to how nonces are used with transactions. Must be less than 14 characters. +{% endstep %} +{% endstepper %} + +### Signature Message Contents + +The signer signature's message hash is created using the following data: + +* **`method`**: the stacking function that is allowed to use this signature. Valid options: + * `stack-stx` — for solo stacking + * `stack-extend` — for extending a solo stacking lock + * `stack-increase` — for increasing a solo stacking position + * `agg-commit` — for `stack-aggregation-commit-indexed` (pool operators) + * `agg-increase` — for `stack-aggregation-increase` (pool operators) +* **`max-amount`**: the maximum uSTX allowed (described above) +* **`auth-id`**: the random integer (described above) +* **`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. +* **`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. +* **`pox-address`**: the Bitcoin address allowed for receiving rewards +* **`config`**: the signer configuration file path where the `stacks_private_key` is located (used by the CLI for signing) + +{% hint style="warning" %} +**reward-cycle differences:** +* For **solo stacking** operations (`stack-stx`, `stack-extend`, `stack-increase`): set this to the **current** reward cycle. +* 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. +{% endhint %} + +{% hint style="warning" %} +Every field in the signature must **exactly match** the corresponding fields in your stacking transaction. A mismatch will cause the transaction to fail. +{% endhint %} + +*** + +## Generating Signatures + +You have several options for generating signer signatures. Choose the one that best fits your setup. + +### Using the stacks-signer CLI + +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. + +{% hint style="info" %} +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. +{% endhint %} + +```bash +stacks-signer generate-stacking-signature \ + --method stack-stx \ + --max-amount 1000000000000 \ + --auth-id 71948271489 \ + --period 1 \ + --reward-cycle 100 \ + --pox-address bc1... \ + --config ./config.toml \ + --json +``` + +* `--json` optionally outputs the result in JSON format + +You can generate a random 32-bit integer for `auth-id` with: + +```bash +python3 -c 'import secrets; print(secrets.randbits(32))' +``` + +The CLI outputs a JSON object: + +```json +{ + "authId": "71948271489", + "maxAmount": "1000000000000", + "method": "stack-stx", + "period": 1, + "poxAddress": "bc1...", + "rewardCycle": 100, + "signerKey": "03a3...", + "signerSignature": "bbbbbbbbbbb" +} +``` + +Use this JSON when making stacking transactions. This output can be pasted directly into Leather Earn. + +{% hint style="info" %} +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. +{% endhint %} + +### Using stacks.js + +The [@stacks/stacking](https://www.npmjs.com/package/@stacks/stacking) NPM package provides a `signPoxSignature` function to generate signer signatures programmatically. + +More information and code samples can be found on [Hiro's Nakamoto docs](https://docs.hiro.so/nakamoto/stacks-js). + +### Using Degen Lab's stacking.tools + +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. + +### Using Leather Earn + +{% hint style="info" %} +At the time of writing, this has only been tested using the [Leather](https://leather.io/) wallet. +{% endhint %} + +Visit [earn.leather.io](https://earn.leather.io/) to generate a signer key signature. Make sure you're connected to the correct network. + +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. + +The fields are: + +* **Reward cycle**: + * For solo stacking transactions: must equal the current reward cycle. The field defaults to the current cycle. + * For `stack-aggregation-commit-indexed`: must equal the cycle used in that function's "reward cycle" argument. Typically current\_cycle + 1. +* **Bitcoin address**: the PoX reward address +* **Topic**: the stacking function that will use this signature +* **Max amount**: max amount of STX that can be used. Defaults to "max possible amount". +* **Auth ID**: defaults to a random integer +* **Duration**: must match the number of cycles used in the stacking transaction. For `stack-aggregation-commit-indexed`, use "1". + +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. + +### Using a Hardware or Software Wallet + +If your signer is configured with a `stacks_private_key`, you can use that key in a wallet to generate stacking signatures. + +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. + +#### Setting up a wallet for signature generation + +{% stepper %} +{% step %} +#### Generate the keypair and configure signer + +1. Use `@stacks/cli` to generate the keychain and private key. + * 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. +2. Take the `privateKey` from the CLI output and add it to your signer's configuration. +3. Take the mnemonic (24 words) and either: + * Set up a new hardware wallet with this mnemonic, or + * Store it securely (e.g., in a password manager). Import it into Leather or XVerse when you need to generate signatures. +{% endstep %} + +{% step %} +#### Generate signatures when needed + +1. Set up your wallet with your signer key's private key: + * Set up Leather with a Ledger hardware wallet, or + * Import your mnemonic into Leather, XVerse, or another Stacks wallet +2. Open an app with stacking signature functionality (e.g., Leather Earn) +3. Connect your wallet (sign in) +4. Enter your PoX address and submit — the app will prompt you to sign +5. Confirm the signature (if using a Ledger, confirm on the device) +6. The app displays your signer key and signature +7. Use the signer key and signature in your stacking transaction +{% endstep %} +{% endstepper %} + +*** + +## Signature Requirements by Function + +| Function | Method | Period | Reward Cycle | +|----------|--------|--------|-------------| +| `stack-stx` | `stack-stx` | Lock period (1–12) | Current cycle | +| `stack-extend` | `stack-extend` | Extend count (1–12) | Current cycle | +| `stack-increase` | `stack-increase` | Current lock period | Current cycle | +| `stack-aggregation-commit-indexed` | `agg-commit` | 1 | Target cycle (e.g., current + 1) | +| `stack-aggregation-increase` | `agg-increase` | 1 | Target cycle | diff --git a/docs/operate/stacking-stx/key-and-address-rotation.md b/docs/operate/stacking-stx/key-and-address-rotation.md new file mode 100644 index 0000000000..10e6bcecfb --- /dev/null +++ b/docs/operate/stacking-stx/key-and-address-rotation.md @@ -0,0 +1,105 @@ +# Key and Address Rotation + +This guide covers how and when to rotate your signer key, Bitcoin reward address, and pool operator key. Understanding the differences between these keys and the constraints around rotating them is important for long-term operations. + +*** + +## Definitions + +* **Signer key**: the cryptographic key used by the signer software to participate in block validation and DKG. Configured as `stacks_private_key` in the signer configuration. +* **Bitcoin reward address (PoX address)**: the BTC address where stacking rewards are sent. +* **Pool operator key**: the STX address used by a pool operator to make stacking transactions (`delegate-stack-stx`, `stack-aggregation-commit-indexed`, etc.). This is separate from the signer key. + +{% hint style="info" %} +The signer key and pool operator key may belong to the same entity, but they should be **separate keys**. See the pool operator key section below for why. +{% endhint %} + +*** + +## Rotate a Signer Key + +You can rotate your signer key without needing to stop stacking. This is done through specific stacking function calls that accept a new signer key as a parameter. + +### Solo stackers + +When calling `stack-extend`, you can pass a new `signer-key`. The new key will be used for the extended cycles. You will also need a new [signer signature](generate-signer-signature.md) generated with the new key. + +When calling `stack-increase`, you can also pass a new `signer-key`. + +{% hint style="warning" %} +You cannot rotate your signer key mid-cycle. The rotation takes effect in the next cycle when the new stacking parameters are applied. +{% endhint %} + +### Pool operators + +When calling `stack-aggregation-commit-indexed` for a new reward cycle, you can pass a new `signer-key`. This associates the new key with the pool for that cycle. + +{% hint style="info" %} +The pox-4 contract is designed to support rotating the signer key without needing your stackers to un-stack and re-stack. This is one of the key advantages of keeping the signer key separate from the pool operator key. +{% endhint %} + +### After rotating + +After rotating your signer key, you must also update your signer software configuration to use the new `stacks_private_key`. Restart the signer software to apply the change. + +Make sure the new signer is running before the prepare phase of the cycle where the new key takes effect. + +*** + +## Rotate a Bitcoin Reward Address + +You can change the Bitcoin address where you receive stacking rewards when making certain stacking function calls. + +### Solo stackers + +* **`stack-extend`**: accepts a `pox-addr` parameter. You can pass a new BTC address, and rewards for the extended cycles will be sent there. +* **`stack-stx`**: when starting a new stacking position (after a previous one has unlocked), you can specify any BTC address. + +### Pool operators + +* **`stack-aggregation-commit-indexed`**: accepts a `pox-addr` parameter. You can use a different BTC address for each reward cycle you commit to. +* **`delegate-stack-stx`**: accepts a `pox-addr` parameter. If the delegator specified a required BTC address in their `delegate-stx` call, you must use that address. + +{% hint style="info" %} +Changing the BTC address does not affect previously committed reward cycles. The new address only applies to newly committed cycles. +{% endhint %} + +*** + +## Pool Operator Key + +The pool operator key (the STX address used for making stacking transactions) **cannot be rotated** without delegators needing to un-stack and re-delegate to the new address. + +This is because the `delegate-stx` function records the specific pool operator address that the delegator authorizes. If the pool operator changes their address, all existing delegations are no longer valid for the new address. + +### Why this matters + +If your pool operator key is compromised, every delegator must: + +1. Wait for their current lock period to expire +2. Call `revoke-delegate-stx` to cancel the old delegation +3. Call `delegate-stx` with the new pool operator address + +This is disruptive and time-consuming, which is why it is **strongly recommended** to keep the pool operator key separate from the signer key. + +### Recommendations + +{% stepper %} +{% step %} +#### Use separate keys + +Keep your signer key and pool operator key separate. The signer key can be rotated through stacking transactions, while the pool operator key should be treated as a long-lived identity. +{% endstep %} + +{% step %} +#### Secure the pool operator key + +Since the pool operator key is harder to rotate, secure it with a hardware wallet or other cold-storage mechanism. The benefit of a separate pool operator key is that it can easily be used in existing wallets, including hardware wallets like Ledger. +{% endstep %} + +{% step %} +#### Limit signer key exposure + +The signer key is stored on a server running the signer software. Rotate it periodically and follow the [OpSec Best Practices](../run-a-signer/opsec-best-practices.md) to minimize the risk of compromise. +{% endstep %} +{% endstepper %} diff --git a/docs/operate/stacking-stx/increase-stacked-position.md b/docs/operate/stacking-stx/operate-a-pool.md similarity index 50% rename from docs/operate/stacking-stx/increase-stacked-position.md rename to docs/operate/stacking-stx/operate-a-pool.md index ec2caf8029..be8c0fdd23 100644 --- a/docs/operate/stacking-stx/increase-stacked-position.md +++ b/docs/operate/stacking-stx/operate-a-pool.md @@ -1,204 +1,248 @@ -# Increase Stacked Position +# Operate a Pool -This guide explains how to increase your stacked STX position. The process depends on your role: +This guide covers how to operate a stacking pool — accepting delegated STX from stackers, committing them to reward cycles, and managing the pool's stacked position. -* **Solo Stackers** use the `stack-increase` function. -* **Delegators** must first revoke their current delegation using `revoke-delegate-stx` and then re-delegate with a higher amount to the same pool operator using `delegate-stx`. -* **Pool Operators** increase their delegators' locked amount by calling `delegate-stack-increase` and then stacking the increased amount with either `stack-aggregation-commit-indexed` (if not already committed) or `stack-aggregation-increase` (if the commit has already been made). +{% hint style="info" %} +This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. +{% endhint %} + +If you want to delegate your STX to a pool instead, see the [Stack with a Pool](stack-with-a-pool.md) guide. + +*** + +## Prerequisites + +1. **A running signer** or a signer you are collaborating with. See the [Run a Signer](../run-a-signer/) guide. +2. **A pool operator wallet** — a separate STX address used to manage delegations and make stacking transactions. This is different from your signer key. See [Key and Address Rotation](key-and-address-rotation.md) for why this separation matters. +3. **Funding** — your pool operator wallet needs enough STX to cover transaction fees. + +{% hint style="info" %} +As a pool operator, you should have two separate accounts: + +* **Pool operator account** — used for all stacking operations (`delegate-stack-stx`, `stack-aggregation-commit-indexed`, etc.). +* **Signer account** — the key used to configure your signer. The signer public key is what you pass into the aggregation commit function. + +This separation is recommended because you can rotate a signer key without disrupting delegations, but you cannot rotate a pool operator key without delegators needing to un-stack and re-delegate. See [Key and Address Rotation](key-and-address-rotation.md) for more details. +{% endhint %} -## Solo Stackers +Set up your pool operator wallet using any Stacks wallet such as [Leather](https://leather.io/) or [Xverse](https://www.xverse.app/). Share this wallet's STX address with parties that will delegate to you. -Solo stackers can add more STX to their active stacking position by calling the `stack-increase` function. The new amount takes effect from the next stacking cycle. +For improved UX, you might use the helper contract [pox4-pools](https://explorer.hiro.so/txid/SP001SFSMC2ZY76PD4M68P3WGX154XCH7NE3TYMX.pox4-pools?chain=mainnet) and add your pool to [earn.leather.io](https://earn.leather.io/). + +{% hint style="info" %} +There are several ways to make stacking transactions. This guide covers using [Leather Earn](https://earn.leather.io/), which is the simplest option. You can also call the functions directly from the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet), or use the [@stacks/stacking](https://github.com/stx-labs/stacks.js/tree/main/packages/stacking) NPM package. +{% endhint %} -The `stack-increase` function locks an additional amount of STX from your account. Your account must be actively stacking and not delegating, and you must have enough unlocked STX to cover the increase. +*** + +## Accept Delegations + +After a delegator calls `delegate-stx` (see [Stack with a Pool](stack-with-a-pool.md)), you as the pool operator call `delegate-stack-stx` to commit each delegator's STX. This must be called for every individual stacker.
Function source code ```clojure -;; Increase the number of STX locked. -;; *New in Stacks 2.1* -;; This method locks up an additional amount of STX from `tx-sender`'s, indicated -;; by `increase-by`. The `tx-sender` must already be Stacking & must not be -;; straddling more than one signer-key for the cycles effected. -;; Refer to `verify-signer-key-sig` for more information on the authorization parameters -;; included here. -(define-public (stack-increase - (increase-by uint) - (signer-sig (optional (buff 65))) - (signer-key (buff 33)) - (max-amount uint) - (auth-id uint)) - (let ((stacker-info (stx-account tx-sender)) - (amount-stacked (get locked stacker-info)) - (amount-unlocked (get unlocked stacker-info)) - (unlock-height (get unlock-height stacker-info)) - (cur-cycle (current-pox-reward-cycle)) - (first-increased-cycle (+ cur-cycle u1)) - (stacker-state (unwrap! (map-get? stacking-state - { stacker: tx-sender }) - (err ERR_STACK_INCREASE_NOT_LOCKED))) - (cur-pox-addr (get pox-addr stacker-state)) - (cur-period (get lock-period stacker-state))) - ;; tx-sender must be currently locked - (asserts! (> amount-stacked u0) - (err ERR_STACK_INCREASE_NOT_LOCKED)) - ;; must be called with positive `increase-by` - (asserts! (>= increase-by u1) - (err ERR_STACKING_INVALID_AMOUNT)) - ;; stacker must have enough stx to lock - (asserts! (>= amount-unlocked increase-by) - (err ERR_STACKING_INSUFFICIENT_FUNDS)) +;; As a delegate, stack the given principal's STX using partial-stacked-by-cycle +;; Once the delegate has stacked > minimum, the delegate should call stack-aggregation-commit +(define-public (delegate-stack-stx (stacker principal) + (amount-ustx uint) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (start-burn-ht uint) + (lock-period uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht))) + (unlock-burn-height (reward-cycle-to-burn-height (+ (current-pox-reward-cycle) u1 lock-period)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + ;; must be called directly by the tx-sender or by an allowed contract-caller (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - ;; stacker must be directly stacking - (asserts! (> (len (get reward-set-indexes stacker-state)) u0) - (err ERR_STACKING_IS_DELEGATED)) - ;; stacker must not be delegating - (asserts! (is-none (get delegated-to stacker-state)) - (err ERR_STACKING_IS_DELEGATED)) - - ;; Validate that amount is less than or equal to `max-amount` - (asserts! (>= max-amount (+ increase-by amount-stacked)) (err ERR_SIGNER_AUTH_AMOUNT_TOO_HIGH)) - - ;; Verify signature from delegate that allows this sender for this cycle - (try! (consume-signer-key-authorization cur-pox-addr cur-cycle "stack-increase" cur-period signer-sig signer-key increase-by max-amount auth-id)) - - ;; update reward cycle amounts - (asserts! (is-some (fold increase-reward-cycle-entry - (get reward-set-indexes stacker-state) - (some { first-cycle: first-increased-cycle, - reward-cycle: (get first-reward-cycle stacker-state), - stacker: tx-sender, - add-amount: increase-by, - signer-key: signer-key }))) - (err ERR_INVALID_INCREASE)) - ;; NOTE: stacking-state map is unchanged: it does not track amount-stacked in PoX-4 - (ok { stacker: tx-sender, total-locked: (+ amount-stacked increase-by)}))) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; stacker must have delegated to the caller + (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED)))) + ;; must have delegated to tx-sender + (asserts! (is-eq (get delegated-to delegation-info) tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; must have delegated enough stx + (asserts! (>= (get amount-ustx delegation-info) amount-ustx) + (err ERR_DELEGATION_TOO_MUCH_LOCKED)) + ;; if pox-addr is set, must be equal to pox-addr + (asserts! (match (get pox-addr delegation-info) + specified-pox-addr (is-eq pox-addr specified-pox-addr) + true) + (err ERR_DELEGATION_POX_ADDR_REQUIRED)) + ;; delegation must not expire before lock period + (asserts! (match (get until-burn-ht delegation-info) + until-burn-ht (>= until-burn-ht + unlock-burn-height) + true) + (err ERR_DELEGATION_EXPIRES_DURING_LOCK)) + ) + + ;; stacker principal must not be stacking + (asserts! (is-none (get-stacker-info stacker)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance stacker) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; ensure that stacking can be performed + (try! (minimal-can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked via partial stacking + ;; before it can be included in the reward set, this must be committed! + (add-pox-partial-stacked pox-addr first-reward-cycle lock-period amount-ustx) + + ;; add stacker record + (map-set stacking-state + { stacker: stacker } + { pox-addr: pox-addr, + first-reward-cycle: first-reward-cycle, + reward-set-indexes: (list), + lock-period: lock-period, + delegated-to: (some tx-sender) }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: stacker, + lock-amount: amount-ustx, + unlock-burn-height: unlock-burn-height }))) ```
-Arguments: +The arguments are: -* Increase by: the amount of uSTX to add to your lock amount. -* Signer public key: the public key used for signing. This can stay the same, or you can use a new key. -* Signer signature: a signature proving control of your signing key. -* Max Amount: used to validate the signer signature provided. It represents the maximum number of uSTX (1 STX = 1,000,000 uSTX) that can be stacked in this transaction. -* Auth Id: used to validate the signer signature provided. It is a random integer that prevents re-use of this particular signer signature. +* **Stacker**: the STX address of the delegator +* **Amount**: denoted in uSTX (1 STX = 1,000,000 uSTX) +* **Pox Address**: the BTC address where you will receive rewards. If the delegator specified a BTC address in their `delegate-stx` call, you must use that same address. +* **Start burn height**: the current BTC block height (add 1 or 2 to the current height when initiating the transaction). The delegation will not actively be stacked at this height, but at whatever reward cycle is committed in the aggregation commit step. +* **Lock period**: number of cycles to lock for (maximum 12). If the delegator specified an expiration burn height, the lock period cannot extend past that. -## Delegators +This step also lets you choose which stackers to accept. For closed pools, only call this function for approved stackers. -Delegators have to increase their delegated amount in two steps. +You can repeat this for multiple stackers before proceeding to the commit step. -{% stepper %} -{% step %} -#### Revoke Your Current Delegation +*** + +## Commit Delegated STX -Before increasing your delegation, cancel your current delegation through the `revoke-delegate-stx` function, so that you can delegate an increased amount of STX afterwards. +Once the total delegated STX exceeds the minimum stacking threshold, call `stack-aggregation-commit-indexed` to commit the pool's aggregate balance to a reward cycle. This is when you provide your signer key and signature. + +{% hint style="info" %} +The minimum stacking threshold can be found at [https://api.hiro.so/v2/pox](https://api.hiro.so/v2/pox) under `min_threshold_ustx` (1 STX = 1,000,000 uSTX). +{% endhint %} + +{% hint style="info" %} +Use `stack-aggregation-commit-indexed` instead of the legacy `stack-aggregation-commit`. The indexed version returns the reward index, which is required if you later need to call `stack-aggregation-increase`. +{% endhint %}
Function source code +Note that `stack-aggregation-commit-indexed` wraps `inner-stack-aggregation-commit`. The inner function is shown here. + ```clojure -;; Revokes the delegation to the current stacking pool. -;; New in pox-4: Fails if the delegation was already revoked. -;; Returns the last delegation state. -(define-public (revoke-delegate-stx) - (let ((last-delegation-state (get-check-delegation tx-sender))) +;; Commit partially stacked STX and allocate a new PoX reward address slot. +;; This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions, +;; so long as: 1. The pox-addr is the same. +;; 2. This "commit" transaction is called _before_ the PoX anchor block. +;; This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold, +;; but does not require it be all locked up within a single transaction +;; +;; Returns (ok uint) on success, where the given uint is the reward address's index in the list of reward +;; addresses allocated in this reward cycle. This index can then be passed to `stack-aggregation-increase` +;; to later increment the STX this PoX address represents, in amounts less than the stacking minimum. +;; +;; *New in Stacks 2.1.* +(define-private (inner-stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) ;; must be called directly by the tx-sender or by an allowed contract-caller (asserts! (check-caller-allowed) (err ERR_STACKING_PERMISSION_DENIED)) - (asserts! (is-some last-delegation-state) (err ERR_DELEGATION_ALREADY_REVOKED)) - (asserts! (map-delete delegation-state { stacker: tx-sender }) (err ERR_DELEGATION_ALREADY_REVOKED)) - (ok last-delegation-state))) + (let ((amount-ustx (get stacked-amount partial-stacked))) + (try! (consume-signer-key-authorization pox-addr reward-cycle "agg-commit" u1 signer-sig signer-key amount-ustx max-amount auth-id)) + (try! (can-stack-stx pox-addr amount-ustx reward-cycle u1)) + ;; Add the pox addr to the reward cycle, and extract the index of the PoX address + ;; so the delegator can later use it to call stack-aggregation-increase. + (let ((add-pox-addr-info + (add-pox-addr-to-ith-reward-cycle + u0 + { pox-addr: pox-addr, + first-reward-cycle: reward-cycle, + num-cycles: u1, + reward-set-indexes: (list), + stacker: none, + signer: signer-key, + amount-ustx: amount-ustx, + i: u0 })) + (pox-addr-index (unwrap-panic + (element-at (get reward-set-indexes add-pox-addr-info) u0)))) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok pox-addr-index))))) ```
-{% endstep %} -{% step %} -#### Delegate with a Higher Amount +The arguments are: -After revoking, call the `delegate-stx` function with your new, higher amount. This function does not directly delegate the STX, but rather allows the pool operator to issue the stacking lock on behalf of the user calling this function. +* **Pox Address**: the BTC address to receive rewards +* **Reward-cycle**: the future reward cycle to commit for +* **Signer public key**: the public key of your signer (this is how you associate your pool operator address with your signer) +* **Signer signature**: a signature proving control of your signing key (see [Generate a Signer Signature](generate-signer-signature.md)) +* **Max Amount**: used to validate the signer signature +* **Auth Id**: used to validate the signer signature -
+{% hint style="info" %} +The `delegate-stack-stx` function sets the stacker's first reward cycle to the _next_ reward cycle. When generating your signature and calling `stack-aggregation-commit-indexed`, make sure the reward cycles match. -Function source code +For example, if you are in cycle 557 when calling `delegate-stack-stx`, pass cycle 558 or higher in both your signature and your `stack-aggregation-commit-indexed` transaction. -```clojure -;; Delegate to `delegate-to` the ability to stack from a given address. -;; This method _does not_ lock the funds, rather, it allows the delegate -;; to issue the stacking lock. -;; The caller specifies: -;; * amount-ustx: the total amount of ustx the delegate may be allowed to lock -;; * until-burn-ht: an optional burn height at which this delegation expires -;; * pox-addr: an optional address to which any rewards *must* be sent -(define-public (delegate-stx (amount-ustx uint) - (delegate-to principal) - (until-burn-ht (optional uint)) - (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) }))) - - (begin - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) +For solo stacking methods, you use the current reward cycle in the signature. For `stack-aggregation-commit-indexed`, you use the target reward cycle because you can commit for future cycles, not just N+1. +{% endhint %} - ;; delegate-stx no longer requires the delegator to not currently - ;; be stacking. - ;; delegate-stack-* functions assert that - ;; 1. users can't swim in two pools at the same time. - ;; 2. users can't switch pools without cool down cycle. - ;; Other pool admins can't increase or extend. - ;; 3. users can't join a pool while already directly stacking. - - ;; pox-addr, if given, must be valid - (match pox-addr - address - (asserts! (check-pox-addr-version (get version address)) - (err ERR_STACKING_INVALID_POX_ADDRESS)) - true) - - ;; tx-sender must not be delegating - (asserts! (is-none (get-check-delegation tx-sender)) - (err ERR_STACKING_ALREADY_DELEGATED)) - - ;; add delegation record - (map-set delegation-state - { stacker: tx-sender } - { amount-ustx: amount-ustx, - delegated-to: delegate-to, - until-burn-ht: until-burn-ht, - pox-addr: pox-addr }) - - (ok true))) -``` +Once this succeeds, your pool is eligible for reward slots in that cycle. All steps up to this point must be completed before the prepare phase begins. -
+### Using Leather Earn -Arguments: +Pool operators can log in to Leather Earn and visit [https://earn.leather.io/pool-admin](https://earn.leather.io/pool-admin) to manage pool operations: -* Amount: Denoted in uSTX (1 STX = 1,000,000 uSTX). -* Delegate to: the STX address of the pool operator they're delegating to. -* Until burn height: optional BTC block height when the delegation expires. -* Pox Address: optional BTC address that, if specified, the signer must use to accept this delegation. +* **delegate-stack-stx**: After a user delegates, call this for each individual stacker. +* **stack-aggregation-commit**: Enter the reward cycle, BTC address, signer public key, signer key signature, Auth ID, and Max amount. This must be done for every individual reward cycle where the pool will be acting as a signer. -{% hint style="info" %} -Make sure the revocation is successful before initiating a new delegation. Otherwise, the `delegate-stx` transaction will fail. -{% endhint %} -{% endstep %} -{% endstepper %} +*** -## Pool Operators +## Increase Committed Amount -Pool operators can increase the total stacking amount through a two-step process. First, update the delegation's locked amount with `delegate-stack-increase`. Then, stack the increased amount by committing it in a future cycle, or increasing an already committed position. +Even after committing, you can increase the total committed STX when new delegations are received. -### Increase the Locked Amount +{% stepper %} +{% step %} +#### Update the delegator's locked amount -The `delegate-stack-increase` function allows a pool operator to add more STX to the existing locked position for a given delegator. It performs necessary checks and updates the delegation state with the increased amount. +Call `delegate-stack-increase` for each delegator whose locked amount needs to increase.
@@ -298,99 +342,22 @@ The `delegate-stack-increase` function allows a pool operator to add more STX to Arguments: -* Stacker: the STX address of the delegator. -* Pox Address: the BTC address of the pool operator where they will receive the BTC rewards. If the delegator has set his own BTC address in the `delegate-stx` call, this address will have to be the same one. -* Increase by: the amount of uSTX to add to the delegator's locked amount. - -## Stack the Increased Amount - -Once the locked amount is updated, the operator must commit the change. There are two functions that can be used to stack the increased amount: - -{% stepper %} -{% step %} -**If the Commit Has Not Yet Been Made: stack-aggregation-commit-indexed** - -This function stacks the total locked amount for an upcoming reward cycle. Note that the `stack-aggregation-commit-indexed` function wraps the `inner-stack-aggregation-commit` function. The wrapped inner function is included here. - -
- -Function source code - -```clojure -;; Commit partially stacked STX and allocate a new PoX reward address slot. -;; This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions, -;; so long as: 1. The pox-addr is the same. -;; 2. This "commit" transaction is called _before_ the PoX anchor block. -;; This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold, -;; but does not require it be all locked up within a single transaction -;; -;; Returns (ok uint) on success, where the given uint is the reward address's index in the list of reward -;; addresses allocated in this reward cycle. This index can then be passed to `stack-aggregation-increase` -;; to later increment the STX this PoX address represents, in amounts less than the stacking minimum. -;; -;; *New in Stacks 2.1.* -(define-private (inner-stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) - (reward-cycle uint) - (signer-sig (optional (buff 65))) - (signer-key (buff 33)) - (max-amount uint) - (auth-id uint)) - (let ((partial-stacked - ;; fetch the partial commitments - (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) - (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - (let ((amount-ustx (get stacked-amount partial-stacked))) - (try! (consume-signer-key-authorization pox-addr reward-cycle "agg-commit" u1 signer-sig signer-key amount-ustx max-amount auth-id)) - (try! (can-stack-stx pox-addr amount-ustx reward-cycle u1)) - ;; Add the pox addr to the reward cycle, and extract the index of the PoX address - ;; so the delegator can later use it to call stack-aggregation-increase. - (let ((add-pox-addr-info - (add-pox-addr-to-ith-reward-cycle - u0 - { pox-addr: pox-addr, - first-reward-cycle: reward-cycle, - num-cycles: u1, - reward-set-indexes: (list), - stacker: none, - signer: signer-key, - amount-ustx: amount-ustx, - i: u0 })) - (pox-addr-index (unwrap-panic - (element-at (get reward-set-indexes add-pox-addr-info) u0)))) - - ;; don't update the stacking-state map, - ;; because it _already has_ this stacker's state - ;; don't lock the STX, because the STX is already locked - ;; - ;; clear the partial-stacked state, and log it - (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) - (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) - (ok pox-addr-index))))) -``` - -
- -Arguments: - -* Pox Address: the BTC address to receive rewards. -* Reward-cycle: a reward cycle in the future. -* Signer public key: the public key of your signer. -* Signer signature: a signature proving control of your signing key. -* Max Amount: used to validate the signer signature provided. -* Auth Id: used to validate the signer signature provided. +* **Stacker**: the STX address of the delegator +* **Pox Address**: the BTC address for rewards. Must match the delegator's specified address if one was set. +* **Increase by**: the amount of uSTX to add to the delegator's locked amount {% endstep %} {% step %} -**If the Commit Has Already Been Made: stack-aggregation-increase** +#### Commit or increase the stacked amount + +After updating locked amounts, you must commit the change: -If you have previously committed an amount, you can further increase the stacked position by calling `stack-aggregation-increase`. This function adds an additional amount of STX to the already committed delegation. +* **If you have not yet committed** for the given cycle: call `stack-aggregation-commit-indexed` (see above). +* **If you have already committed**: call `stack-aggregation-increase` with the index returned from the first commit and a new signature.
-Function source code +stack-aggregation-increase source code ```clojure ;; Commit partially stacked STX to a PoX address which has already received some STX (more than the Stacking min). @@ -452,7 +419,6 @@ If you have previously committed an amount, you can further increase the stacked { pox-addr: pox-addr, total-ustx: increased-ustx, stacker: none, - ;; TODO: this must be authorized with a signature, or tx-sender allowance! signer: (get signer existing-entry) }) ;; update the total ustx in this cycle @@ -460,10 +426,6 @@ If you have previously committed an amount, you can further increase the stacked { reward-cycle: reward-cycle } { total-ustx: total-ustx }) - ;; don't update the stacking-state map, - ;; because it _already has_ this stacker's state - ;; don't lock the STX, because the STX is already locked - ;; ;; clear the partial-stacked state, and log it (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) @@ -472,22 +434,46 @@ If you have previously committed an amount, you can further increase the stacked
-Arguments: +Arguments for `stack-aggregation-increase`: -* Pox Address: the BTC address to receive rewards. -* Reward Cycle: a reward cycle in the future. -* Reward Cycle Index: the index returned by `stack-aggregation-commit-indexed`. -* Signer signature: a signature proving control of your signing key. -* Signer public key: the public key of your signer. -* Max Amount: used to validate the signer signature provided. -* Auth Id: used to validate the signer signature provided. +* **Pox Address**: the BTC address to receive rewards +* **Reward Cycle**: a reward cycle in the future +* **Reward Cycle Index**: the index returned by `stack-aggregation-commit-indexed` {% endstep %} {% endstepper %} {% hint style="warning" %} -* Sequential Process: First call `delegate-stack-increase` to update the locked amount of a delegation. Then, commit the change: - * Use `stack-aggregation-commit-indexed` if this is the first commit in the given cycle. - * Use `stack-aggregation-increase` if you have already committed in the cycle you want to increase. +This is a sequential process. First call `delegate-stack-increase`, then commit the change: +* Use `stack-aggregation-commit-indexed` if this is the first commit for that cycle. +* Use `stack-aggregation-increase` if you have already committed. -Failing to commit (or properly increase after a commit) will result in the increased delegation not taking effect in upcoming stacking cycles. +Failing to commit (or properly increase after a commit) will result in the increased delegation not taking effect. {% endhint %} + +*** + +## How Signer Registration Works for Pools + +The delegated stacking workflow requires multiple transactions to register a signer: + +{% stepper %} +{% step %} +#### Stackers delegate their STX + +Stackers call `delegate-stx` to give the pool operator permission. +{% endstep %} + +{% step %} +#### Pool operator accepts delegations + +The pool operator calls `delegate-stack-stx` for each individual stacker. +{% endstep %} + +{% step %} +#### Pool operator commits + +The pool operator calls `stack-aggregation-commit-indexed` to commit all delegated STX. This is where the signer key is associated with the pool. +{% endstep %} +{% endstepper %} + +All steps must be completed before the prepare phase of the target reward cycle. During the prepare phase, DKG occurs and the signer is automatically registered — no manual action needed beyond monitoring. diff --git a/docs/operate/stacking-stx/operate-a-stacking-pool.md b/docs/operate/stacking-stx/operate-a-stacking-pool.md deleted file mode 100644 index 76d89d6f72..0000000000 --- a/docs/operate/stacking-stx/operate-a-stacking-pool.md +++ /dev/null @@ -1,587 +0,0 @@ -# Operate a Stacking Pool - -This doc assumes you are familiar with stacking at a conceptual level. If not, you may want to read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide. - -The guide below applies to those who want to operate a pool, meaning they want to have stackers delegate STX tokens to them. If you choose to operate a pool you will either need to run your own signer or collaborate with one. - -### Pool Operator Stacking Flow - -For pool operators, the flow is a bit different than solo stacking. Remember that as a pool operator, other stackers are delegating their STX to you to stack on behalf of them. This additional role adds a couple of extra steps to your stacking flow if operating as a pool operator. - -Similar to the changes to solo Stacking, the big difference for delegation flows is the inclusion of signer keys and signatures. Because pool operators need to make transactions to “finalize” a delegation, these new arguments add new complexities to the stacking flow. - -#### Delegator initiates delegation - -{% hint style="info" %} -This step is not required to apply to pool operators/signers. It is included here to illustrate the end-to-end flow, but if you are operating as a pool operator/signer you will not have to perform this step. Instead, users delegate their STX to you as the pool operator. -{% endhint %} - -The first step, where the delegator sets up their delegation to a pool operator, is to call `delegate-stx`. This function does not directly delegate the STX, but rather allows the pool operator to issue the stacking lock on behalf of the user calling this function. You can think of calling this function as the delegator giving permission to the pool operator to stack on their behalf. - -
- -Function source code - -```clojure -;; Delegate to `delegate-to` the ability to stack from a given address. -;; This method _does not_ lock the funds, rather, it allows the delegate -;; to issue the stacking lock. -;; The caller specifies: -;; * amount-ustx: the total amount of ustx the delegate may be allowed to lock -;; * until-burn-ht: an optional burn height at which this delegation expires -;; * pox-addr: an optional address to which any rewards *must* be sent -(define-public (delegate-stx (amount-ustx uint) - (delegate-to principal) - (until-burn-ht (optional uint)) - (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) }))) - - (begin - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - - ;; delegate-stx no longer requires the delegator to not currently - ;; be stacking. - ;; delegate-stack-* functions assert that - ;; 1. users can't swim in two pools at the same time. - ;; 2. users can't switch pools without cool down cycle. - ;; Other pool admins can't increase or extend. - ;; 3. users can't join a pool while already directly stacking. - - ;; pox-addr, if given, must be valid - (match pox-addr - address - (asserts! (check-pox-addr-version (get version address)) - (err ERR_STACKING_INVALID_POX_ADDRESS)) - true) - - ;; tx-sender must not be delegating - (asserts! (is-none (get-check-delegation tx-sender)) - (err ERR_STACKING_ALREADY_DELEGATED)) - - ;; add delegation record - (map-set delegation-state - { stacker: tx-sender } - { amount-ustx: amount-ustx, - delegated-to: delegate-to, - until-burn-ht: until-burn-ht, - pox-addr: pox-addr }) - - (ok true))) -``` - -
- -The arguments here are unchanged from previous versions of PoX: - -* Amount: Denoted in uSTX (1 STX = 1,000,000 uSTX) -* Delegate to: the STX address of the pool operator they're delegating to. Note that this is different from the “signer key” used. Instead, this is the STX address that is used to make PoX transactions. -* Until burn height: an optional argument representing the BTC block height when the delegation expires. If none is used, the delegation permission expires only when explicitly revoked. -* Pox Address: an optional BTC address that, if specified, the signer must use to accept this delegation - -#### Pool operator “activates” the delegation - -Just as in the older PoX contract, after a delegator calls the `delegate-stx` function, the pool operator calls `delegate-stack-stx` to commit the delegator’s STX. - -
- -Function source code - -```clojure -;; As a delegate, stack the given principal's STX using partial-stacked-by-cycle -;; Once the delegate has stacked > minimum, the delegate should call stack-aggregation-commit -(define-public (delegate-stack-stx (stacker principal) - (amount-ustx uint) - (pox-addr { version: (buff 1), hashbytes: (buff 32) }) - (start-burn-ht uint) - (lock-period uint)) - ;; this stacker's first reward cycle is the _next_ reward cycle - (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) - (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht))) - (unlock-burn-height (reward-cycle-to-burn-height (+ (current-pox-reward-cycle) u1 lock-period)))) - ;; the start-burn-ht must result in the next reward cycle, do not allow stackers - ;; to "post-date" their `stack-stx` transaction - (asserts! (is-eq first-reward-cycle specified-reward-cycle) - (err ERR_INVALID_START_BURN_HEIGHT)) - - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - - ;; stacker must have delegated to the caller - (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED)))) - ;; must have delegated to tx-sender - (asserts! (is-eq (get delegated-to delegation-info) tx-sender) - (err ERR_STACKING_PERMISSION_DENIED)) - ;; must have delegated enough stx - (asserts! (>= (get amount-ustx delegation-info) amount-ustx) - (err ERR_DELEGATION_TOO_MUCH_LOCKED)) - ;; if pox-addr is set, must be equal to pox-addr - (asserts! (match (get pox-addr delegation-info) - specified-pox-addr (is-eq pox-addr specified-pox-addr) - true) - (err ERR_DELEGATION_POX_ADDR_REQUIRED)) - ;; delegation must not expire before lock period - (asserts! (match (get until-burn-ht delegation-info) - until-burn-ht (>= until-burn-ht - unlock-burn-height) - true) - (err ERR_DELEGATION_EXPIRES_DURING_LOCK)) - ) - - ;; stacker principal must not be stacking - (asserts! (is-none (get-stacker-info stacker)) - (err ERR_STACKING_ALREADY_STACKED)) - - ;; the Stacker must have sufficient unlocked funds - (asserts! (>= (stx-get-balance stacker) amount-ustx) - (err ERR_STACKING_INSUFFICIENT_FUNDS)) - - ;; ensure that stacking can be performed - (try! (minimal-can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) - - ;; register the PoX address with the amount stacked via partial stacking - ;; before it can be included in the reward set, this must be committed! - (add-pox-partial-stacked pox-addr first-reward-cycle lock-period amount-ustx) - - ;; add stacker record - (map-set stacking-state - { stacker: stacker } - { pox-addr: pox-addr, - first-reward-cycle: first-reward-cycle, - reward-set-indexes: (list), - lock-period: lock-period, - delegated-to: (some tx-sender) }) - - ;; return the lock-up information, so the node can actually carry out the lock. - (ok { stacker: stacker, - lock-amount: amount-ustx, - unlock-burn-height: unlock-burn-height }))) -``` - -
- -The arguments are: - -* Stacker: the STX address of the delegator -* Amount: denoted in uSTX (1 STX = 1,000,000 uSTX) -* Pox Address: The BTC address of the pool operator where they will receive the BTC rewards. If the delegator has set his own BTC address in the `delegate-stx` call, this address will have to be the same one, otherwise the contract call will fail. -* Start burn height: The BTC block height in which delegation can begin. This field is used to ensure that an old transaction intended for an earlier cycle will fail, and also prevents callers from "post-dating" the call to a future cycle. The best option here is to add 1 or 2 to the current BTC block height when you initiate this transaction. Note that the delegation will not actively be stacked at this block height, but whatever reward cycle is passed in the aggregation commit function (explained below). -* Lock period: number of cycles to lock for. If the delegator provided the until burn height argument, then the end of these cycles cannot be past the expiration provided. The maximum lock period that a pool operator can provide in this function call is 12. - -This step also allows the pool operator to proactively choose which Stackers they’ll accept delegation from. For “closed” pools, the pool operator will only call this function for approved Stackers. It is up to each pool operator who runs a closed pool to implement this process. - -This step can be repeated for multiple Stackers before going to the next step. - -{% hint style="info" %} -If you look at the function source code, you'll see that the `delegate-stack-stx` function sets the stacker's first reward cycle to be the _next_ reward cycle. - -When generating your signature and your `stack-aggregation-commit-indexed` transaction, you'll want to make sure that the reward cycles match. - -So if you are in cycle 557 when you call the `delegate-stack-stx` function, you'll want to pass in cycle 558 or higher when you generate your signature and your `stack-aggregation-commit-indexed` transaction. - -With `stack-aggregation-commit-indexed`, the `reward-cycle` arg is saying "I'm committing these stacks to be stacked in cycle N". But the `delegate-stack-stx` transaction gets you setup for next cycles, aka 558 and higher. - -Also make sure that, when you generate your signature, you use 558 or higher as the reward cycle. In solo stacking methods, you use the current reward cycle in the signature, but not for `stack-aggregation-commit-indexed`. This is because with `stack-aggregation-commit-indexed` you can commit stacks for future cycles, not just the N+1 cycle. -{% endhint %} - -#### Pool operator “commits” delegated STX - -The next step is for the pool operator to call `stack-aggregation-commit-indexed`. - -{% hint style="info" %} -In the contract source code, you'll notice a similarly named function called `stack-aggregation-commit`. This is a legacy function that makes it difficult to increase the stacking amount, as it does not return the reward index of the stacking slot, which is required in order to call the `stack-aggregation-increase` function. We recommend using `stack-aggregation-commit-indexed`. -{% endhint %} - -
- -Function source code - -Note that the `stack-aggregation-commit-indexed` function wraps the `inner-stack-aggregation-commit` function. The wrapped inner function is included here. - -Check out the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) to see the flow of contract calls. - -```clojure -;; Commit partially stacked STX and allocate a new PoX reward address slot. -;; This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions, -;; so long as: 1. The pox-addr is the same. -;; 2. This "commit" transaction is called _before_ the PoX anchor block. -;; This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold, -;; but does not require it be all locked up within a single transaction -;; -;; Returns (ok uint) on success, where the given uint is the reward address's index in the list of reward -;; addresses allocated in this reward cycle. This index can then be passed to `stack-aggregation-increase` -;; to later increment the STX this PoX address represents, in amounts less than the stacking minimum. -;; -;; *New in Stacks 2.1.* -(define-private (inner-stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) - (reward-cycle uint) - (signer-sig (optional (buff 65))) - (signer-key (buff 33)) - (max-amount uint) - (auth-id uint)) - (let ((partial-stacked - ;; fetch the partial commitments - (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) - (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - (let ((amount-ustx (get stacked-amount partial-stacked))) - (try! (consume-signer-key-authorization pox-addr reward-cycle "agg-commit" u1 signer-sig signer-key amount-ustx max-amount auth-id)) - (try! (can-stack-stx pox-addr amount-ustx reward-cycle u1)) - ;; Add the pox addr to the reward cycle, and extract the index of the PoX address - ;; so the delegator can later use it to call stack-aggregation-increase. - (let ((add-pox-addr-info - (add-pox-addr-to-ith-reward-cycle - u0 - { pox-addr: pox-addr, - first-reward-cycle: reward-cycle, - num-cycles: u1, - reward-set-indexes: (list), - stacker: none, - signer: signer-key, - amount-ustx: amount-ustx, - i: u0 })) - (pox-addr-index (unwrap-panic - (element-at (get reward-set-indexes add-pox-addr-info) u0)))) - - ;; don't update the stacking-state map, - ;; because it _already has_ this stacker's state - ;; don't lock the STX, because the STX is already locked - ;; - ;; clear the partial-stacked state, and log it - (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) - (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) - (ok pox-addr-index))))) -``` - -
- -At this point, the STX are committed to the pool operator, and the pool operator has some “aggregate balance” of committed STX. The pool operator is not actually eligible for reward slots and signer initialization until this step is finished. - -The pool operator cannot call this function until the total number of STX committed is larger than the minimum threshold required to Stack. This minimum stacking threshold is a function of the total number of STX stacked divided by the available number of reward slots. - -{% hint style="info" %} -This number varies and can be found by visiting the pox endpoint of Hiro's API at [https://api.hiro.so/v2/pox](https://api.hiro.so/v2/pox) and looking at the `min_threshold_ustx` field. (1 STX = 1,000,000 uSTX). -{% endhint %} - -Once the threshold is reached, the pool operator calls `stack-aggregation-commit-indexed`. This is the point where you as the pool operator must provide your signer key and signer key signature. The arguments are: - -* Pox Address: the BTC address to receive rewards -* Reward-cycle: a reward cycle in the future (see the note above on passing the correct reward cycle) -* Signer public key: the public key of your signer (remember that this may be different than the address you are using to operate the pool, but this step is how you associate the two together) -* Signer signature: A signature proving control of your signing key (details on how to do this are below) -* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX (1 STX = 1,000,000 uSTX) that can be stacked in this transaction. -* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. - -{% hint style="info" %} -In the Definitions and Roles section in the previous document, we described how the pool operator and signer may be the same entity, but not necessarily have the same address. - -Signers who are also pool operators and wish to have STX delegated to them should have a separate keychain associated with their pool operator account in order to make Stacking transactions such as `delegate-stack-stx` and `stack-aggregation-commit-indexed`. - -So, as a signing entity operating a pool, you should have two accounts: - -* Your pool operator account, which you will use to conduct all of the stacking operations we have covered here. -* Your signer account, which is what you used to set up your signer. This signer public key is what you will pass in to the above aggregation commit function, and is also the key you will use when generating your signer signature. - -If you are operating as a signer and a pool operator, you should have a separate key because you might need to rotate your signer key when necessary. - -The PoX contract is designed to support rotating the signer key without needing your Stackers to un-stack and re-stack later. You cannot rotate a pool operator key without needing to wait for all delegated Stackers to un-stack and finally re-stack to the new pool operator address. - -Because of this limitation of being unable to rotate pool operator addresses, it’s highly recommended to have a separate pool operator key. The benefit of a separate pool operator key is that it can easily be used in existing wallets, including hardware wallets. -{% endhint %} - -Once this succeeds, the signer is eligible for reward slots. The number of reward slots depends on the amount of STX committed to this signer. Even if the signer commits more than the “global minimum”, the minimum amount to receive a slot depends on the amount of STX locked for each cycle. - -To act as a signer, each step up to this point must be taken before the prepare phase of the next cycle begins. It is crucial that the signer software is running. - -#### Pool operator increases amount committed - -Even after the signer commits to a certain amount of STX in the previous step, the signer can increase this amount once more delegations are received. The initial steps must be taken for each Stacker (`delegate-stx` and then `delegate-stack-stx`), and then `stack-aggregation-increase` can be called with the index returned from the first `stack-aggregation-commit-indexed` call and a new signature. - -
- -Function source code - -``` -;; Commit partially stacked STX to a PoX address which has already received some STX (more than the Stacking min). -;; This allows a delegator to lock up marginally more STX from new delegates, even if they collectively do not -;; exceed the Stacking minimum, so long as the target PoX address already represents at least as many STX as the -;; Stacking minimum. -;; -;; The `reward-cycle-index` is emitted as a contract event from `stack-aggregation-commit` when the initial STX are -;; locked up by this delegator. It must be passed here to add more STX behind this PoX address. If the delegator -;; called `stack-aggregation-commit` multiple times for the same PoX address, then any such `reward-cycle-index` will -;; work here. -;; -;; *New in Stacks 2.1* -;; -(define-public (stack-aggregation-increase (pox-addr { version: (buff 1), hashbytes: (buff 32) }) - (reward-cycle uint) - (reward-cycle-index uint)) - (let ((partial-stacked - ;; fetch the partial commitments - (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) - (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) - - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - - ;; reward-cycle must be in the future - (asserts! (> reward-cycle (current-pox-reward-cycle)) - (err ERR_STACKING_INVALID_LOCK_PERIOD)) - - (let ((amount-ustx (get stacked-amount partial-stacked)) - ;; reward-cycle must point to an existing record in reward-cycle-total-stacked - ;; infallible; getting something from partial-stacked-by-cycle succeeded so this must succeed - (existing-total (unwrap-panic (map-get? reward-cycle-total-stacked { reward-cycle: reward-cycle }))) - ;; reward-cycle and reward-cycle-index must point to an existing record in reward-cycle-pox-address-list - (existing-entry (unwrap! (map-get? reward-cycle-pox-address-list { reward-cycle: reward-cycle, index: reward-cycle-index }) - (err ERR_DELEGATION_NO_REWARD_SLOT))) - (increased-ustx (+ (get total-ustx existing-entry) amount-ustx)) - (total-ustx (+ (get total-ustx existing-total) amount-ustx))) - - ;; must be stackable - (try! (minimal-can-stack-stx pox-addr total-ustx reward-cycle u1)) - - ;; new total must exceed the stacking minimum - (asserts! (<= (get-stacking-minimum) total-ustx) - (err ERR_STACKING_THRESHOLD_NOT_MET)) - - ;; there must *not* be a stacker entry (since this is a delegator) - (asserts! (is-none (get stacker existing-entry)) - (err ERR_DELEGATION_WRONG_REWARD_SLOT)) - - ;; the given PoX address must match the one on record - (asserts! (is-eq pox-addr (get pox-addr existing-entry)) - (err ERR_DELEGATION_WRONG_REWARD_SLOT)) - - ;; update the pox-address list -- bump the total-ustx - (map-set reward-cycle-pox-address-list - { reward-cycle: reward-cycle, index: reward-cycle-index } - { pox-addr: pox-addr, - total-ustx: increased-ustx, - stacker: none, - ;; TODO: this must be authorized with a signature, or tx-sender allowance! - signer: (get signer existing-entry) }) - - ;; update the total ustx in this cycle - (map-set reward-cycle-total-stacked - { reward-cycle: reward-cycle } - { total-ustx: total-ustx }) - - ;; don't update the stacking-state map, - ;; because it _already has_ this stacker's state - ;; don't lock the STX, because the STX is already locked - ;; - ;; clear the partial-stacked state, and log it - (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) - (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) - (ok true)))) -``` - -
- -*** - -## Step by Step Stacking Guide - -Now that you are familiar with the overall stacking flow and the different roles played, let's dive into the step-by-step guide for actually conducting the stacking process as a pool operator. - -{% hint style="info" %} -There are several ways you can go about stacking. This guide will cover using [Leather Earn](https://earn.leather.io/), which is a stacking web application and the simplest option. - -Additionally, you can choose to call the stacking functions directly from the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) in the explorer. - -The fields and process will be the same, but the UI will differ. - -Finally, you can stack using JS and the [@stacks/stacking](https://github.com/stx-labs/stacks.js/tree/main/packages/stacking) package if you prefer. Again, the functions and parameters will be the same, you will just be writing your JS code directly instead of using a UI. - -If you are interested in using this method, you'll want to follow the [stacking guide](https://docs.hiro.so/stacks.js/guides/how-to-integrate-stacking) created by Hiro, and be sure to integrate the new parameters described in [Hiro's Nakamoto update doc](https://docs.hiro.so/nakamoto/stacks-js). -{% endhint %} - -### Step 1: Run or work with a signer - -This is a necessary prerequisite to stacking as a pool operator. You will either need to run your own signer or work with one and have them conduct step 2 on your behalf and give you their signer signature. - -Running a signer involves setting up a hosted production environment that includes both a Stacks Node and the Stacks Signer. For more information, refer to the [running a signer doc](../run-a-signer/signer-quickstart.md). - -Once the signer software is running, you'll to keep track of the `stacks_private_key` that you used when configuring your signer software. This will be used in subsequent Stacking transactions. - -{% hint style="info" %} -In the note above about pool operator vs signer keys, this corresponds to your signer key, not your pool operator wallet -{% endhint %} - -### Step 2: Generate a signer key signature - -#### Overview of signer keys and signatures - -The main difference with Stacking in Nakamoto is that the Signer needs to include new parameters in their Stacking transactions. These are Clarity transactions that pool operators will call when interacting with the `pox-4.clar` contract. Interacting with that contract is how you as a pool operator actually stack the delegated STX tokens. - -{% hint style="info" %} -The current pox-4 contract address can be found by visiting the following endpoint of the Hiro API: [https://api.hiro.so/v2/pox](https://api.hiro.so/v2/pox). - -You can then visit the [Stacks Explorer](https://explorer.hiro.so/) to view the contract and pass in the contract id. - -You may want to review this contract to familiarize yourself with it. -{% endhint %} - -Here is an overview of the new fields you will need to pass in some of your stacking transactions: - -1. `signer-key`: the public key that corresponds to the `stacks_private_key` your signer is using. -2. `signer-signature`: a signature that demonstrates that you actually control your `signer-key`. Because signer keys need to be unique, this is also a safety check to ensure that other Stackers can’t use someone else’s signer key. -3. `max-amount`: 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-aggregation-increase`, then this parameter dictates the maximum amount of uSTX that can be used to add more locked STX to the already committed position. -4. `auth-id`: a random integer that prevents re-use of a particular signature, similar to how nonces are used with transactions. - -Signer signatures are signatures created using a particular signer key. They demonstrate that the controller of that signer key is allowing a Stacker to use their signer's public key. The signer signature’s message hash is created using the following data: - -* `method`: a string that indicates the Stacking method that is allowed to utilize this signature. The valid options are `stack-stx`, `stack-extend`, `stack-increase`, `agg-commit` (for `stack-aggregation-commit-indexed`) and `agg-increase` (for `stack-aggregation-increase`). -* `max-amount`: described above. -* `auth-id`: described above. -* `period`: a value between 1 and 12, which indicates how long the Stacker is allowed to lock their STX for in this particular Stacking transaction. For `agg-commit`, this must be equal to 1. -* `reward-cycle`: This represents the reward cycle in which the Stacking transaction can be confirmed (for `stack-aggregation-commit-indexed`, this has to be set to 1). -* `pox-address`: The Bitcoin address that is allowed to be used for receiving rewards. This corresponds to the Bitcoin address associated with your signer -* `config`: This represents the signer configuration file path where the `stacks_private_key` is located, and it is used for signing the generated signature. - -Now that we have an overview of role and contents of signatures, let's see how to actually generate them. You have several options available. - -Generating your signature with Degen Lab's stacks.tools - -* Degen Lab has a signature generation tool that will generate a signature using their signer. This is the quickest and simplest option. To generate a signature using this method, all you need to do is visit their [signature tool](https://signature.stacking.tools/) and pass in the relevant information as covered on the page. - -Generating your signature with stacks.js - -* The [@stacks/stacking](https://www.npmjs.com/package/@stacks/stacking) NPM package provides interfaces to generate and use signer signatures. -* There is a function called `signPoxSignature` that will allow you to generate this signature and pass it in to the stacking function. -* More information and code samples can be found on [Hiro's Nakamoto docs](https://docs.hiro.so/nakamoto/stacks-js). - -Generating your signature using the stacks-signer CLI - -* If you already have your signer configured and set up, you can use the `stacks-signer` CLI to generate this signature. You can either SSH into your running signer or use the `stacks-signer` CLI locally. If using the CLI locally, you will need to ensure you have a matching configuration file located on your filesystem. Having a matching configuration file is important to ensure that the signer public key you make in Stacking transactions is the same as in your hosted signer. - -The CLI command is: - -``` -``` - -These arguments match those described in section [Overview of signer keys and signatures](solo-stack.md#overview-of-signer-keys-and-signatures), with the addition of: - -* `--json`, to optionally output the resulting signature in JSON. - -You can use the following command to generate a random `32` bit integer as `auth-id`: - -``` -``` - -Once the `generate-stacking-signature` command is run, the CLI will output a JSON: - -```json -{"authId":"12345","maxAmount":"1234","method":"agg-commit","period":1,"poxAddress":"bc1...","rewardCycle":100,"signerKey":"aaaaaaaa","signerSignature":"bbbbbbbbbbb"} -``` - -You will use the JSON when calling Stacking transactions from your pool operator address as outlined above. Remember that this may be different than your signer address. - -Generating your signature with Leather Earn - -* Leather Earn is a web application that provides an easy-to-use interface for stacking and generating signatures. We'll cover using Leather Earn for stacking at the end of this document, here we will cover how to use it to generate a signature. - -{% hint style="info" %} -At the time of writing, this has only been tested using the [Leather](https://leather.io/) wallet. -{% endhint %} - -You can visit [earn.leather.io](https://earn.leather.io/) to generate a signer key signature. Make sure you’re connected to the correct network.\ -To generate a signer key signature, it’s important that you’ve logged in Leather with the same secret key that was used to generate your signer key, not the account that will serve as your pool operator address. Once you’ve setup that account on Leather, you can log in to Leather Earn.\ -Click the link “Signer key signature” at the bottom of the page. This will open the “generate a signer key signature” page. - -The fields are: - -* Reward cycle: - * For all solo stacking transactions, this must equal the current reward cycle, not the cycle in which they will start stacking. The field defaults to the current reward cycle. - * For stack-aggregation-commit-indexed, this field must equal the cycle used in that function’s “reward cycle” argument. Typically, that equates to current\_cycle + 1. -* Bitcoin address: the PoX reward address that can be used -* Topic: the stacking function that will use this signature -* Max amount: max amount of STX that can be used. Defaults to “max possible amount” -* Auth ID: defaults to random int -* Duration: must match the number of cycles used in the stacking transaction. For `stack-aggregation-commit-indexed`, use “1”. - -{% hint style="warning" %} -Each of these fields must be exactly matched in order for the Stacking transaction to work. Future updates to Leather Earn will verify the signature before the transaction is made. -{% endhint %} - -Click the “generate signature” button to popup a Leather page where you can generate the signature. Once you submit that popup, Leather Earn will have the signer key and signature you generated. - -After you sign that message, you'll see the information you can use in your Stacking transactions, including the signer public key and signature. - -You can click the “copy” icon next to “signer details to share with stackers”. This will copy a JSON string, which can be directly pasted into the Leather Earn page where you make your Stacking transaction. Alternatively, this information can be entered manually. - -We'll cover the Leather Earn pages for actually making those transactions in the next section of this document. - -#### Using a hardware or software wallet to generate signatures - -When the signer is configured with a `stacks_private_key`, the signer may want to be able to use that key in a wallet to make stacking signatures. - -If the signer uses a tool like [@stacks/cli](https://docs.hiro.so/get-started/command-line-interface) to generate the key, the CLI also outputs a mnemonic (aka “seed phrase”) that can be imported into a wallet. Because the Stacks CLI uses the standard derivation path for generating Stacks keys, any Stacks wallet will default to having that same private key when the wallet is imported from a derivation path. Similarly, if a hardware wallet is setup with that mnemonic, then the Signer can use a wallet like Leather to make stacking signatures. - -Use the following stepper for the recommended workflow for setting up a wallet to generate signatures: - -{% stepper %} -{% step %} -#### Generate the keypair and configure signer - -1. Use @stacks/cli to generate the keychain and private key. - * Typically, when using a hardware wallet, it’s better to generate the mnemonic on the hardware wallet. For this use case, however, the signer software needs the private key, and hardware wallets (by design) don’t allow exporting private keys. -2. Take the `privateKey` from the CLI output and add it to your signer’s configuration. -3. Take the mnemonic (24 words) and either: - * Setup a new hardware wallet with this mnemonic - * Store it somewhere securely, like a password manager. When the signer needs to generate signatures for Stacking transactions, they can import it into either Leather or XVerse. -{% endstep %} - -{% step %} -#### When you need to generate signatures - -1. Setup your wallet with your signer key’s private key. Either: - * Setup your Leather wallet with a Ledger hardware wallet - * Import your mnemonic into Leather, XVerse, or another Stacks wallet -2. Open an app that has stacking signature functionality built-in -3. Connect your wallet to the app (aka sign in) -4. In the app, enter your PoX address and “submit” - * The app will popup a window in your wallet that prompts you to sign the information - * The app will show clear information about what you’re signing -5. Create the signature - * If using a Ledger, confirm on your device -6. The app will display two results: - * Your signer key, which is the public key associated with your signer’s key - * Your signer signature -7. Finally, make a Stacking transaction using the signer key and signer signature. -{% endstep %} -{% endstepper %} - -Now that you have your signer signature generated, it's time to start stacking. This process will vary depending on your chosen method. We've included instructions for pool stacking using [Leather Earn](https://earn.leather.io/) below. - -### Step 3: Stack as a pool operator - -The first step with delegated stacking involves a stacker delegating their Stacks to a specific pool operator. Stackers can do this by visiting the “Stack in a pool” page on Leather Earn. - -As the pool operator, you must provide a STX address (a “pool admin address”) that will manage delegations. As discussed in previous sections, this is a separate address from the signer’s private key, and this can be any address. This address is what will be used when making transactions to confirm and aggregate delegated STX. - -Pool operators can log in to Leather Earn and visit [https://earn.leather.io/pool-admin](https://earn.leather.io/pool-admin) to make pool management transactions. - -#### delegate-stack-stx - -Once a user has delegated to a pool operator, the pool operator must call `delegate-stack-stx` for each individual stacker. - -#### stack-aggregation-commit - -Once a pool has enough delegated STX to become a signer, the pool admin needs to visit the `Stack Aggregation Commit` page on Leather Earn. The pool operator enters the following information: - -* Reward cycle: the reward cycle where the operator is “committing” delegated STX. This must be done for every individual reward cycle where the pool will be acting as a signer. -* BTC address -* New fields: - * Signer public key - * Signer key signature (generated in a previous step using the signer key) - * Auth ID - * Max amount - -Once this transaction has been confirmed, the pool operator is eligible to be a signer for an upcoming reward cycle. diff --git a/docs/operate/stacking-stx/solo-stack.md b/docs/operate/stacking-stx/solo-stack.md deleted file mode 100644 index 901f4a5545..0000000000 --- a/docs/operate/stacking-stx/solo-stack.md +++ /dev/null @@ -1,614 +0,0 @@ -# Solo Stack - -This doc assumes you are familiar with stacking at a conceptual level. If not, you may want to read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide. - -The guide below applies to those who want to solo stack, meaning they meet the minimum stacking requirement and need to either run a signer or collaborate with a signer. - -{% hint style="info" %} -There is a dapp created by Degen Lab for [solo stacking](https://solo.stacking.tools/) without needing a signer, as Degen Lab operates a signer. This is likely the easiest option for solo stacking. We'll cover this option below. -{% endhint %} - -If you prefer to participate in a pool by delegating your STX, you do not need to operate a signer either. If you fall into this category, check out the [Stack with a Pool](stack-with-a-pool.md) guide. - -### Solo Stacker Flow - -{% hint style="info" %} -Note that in order to solo stack, you need to have the minimum number of STX tokens. This number can be found by visiting the pox endpoint of Hiro's API at [https://api.mainnet.hiro.so/v2/pox](https://api.mainnet.hiro.so/v2/pox) and looking at the `min_threshold_ustx` field. (1 STX = 1,000,000 uSTX) -{% endhint %} - -#### Call the function `stack-stx` - -
- -Function source code - -```clojure -;; Lock up some uSTX for stacking! Note that the given amount here is in micro-STX (uSTX). -;; The STX will be locked for the given number of reward cycles (lock-period). -;; This is the self-service interface. tx-sender will be the Stacker. -;; -;; * The given stacker cannot currently be stacking. -;; * You will need the minimum uSTX threshold. This will be determined by (get-stacking-minimum) -;; at the time this method is called. -;; * You may need to increase the amount of uSTX locked up later, since the minimum uSTX threshold -;; may increase between reward cycles. -;; * You need to provide a signer key to be used in the signer DKG process. -;; * The Stacker will receive rewards in the reward cycle following `start-burn-ht`. -;; Importantly, `start-burn-ht` may not be further into the future than the next reward cycle, -;; and in most cases should be set to the current burn block height. -;; -;; To ensure that the Stacker is authorized to use the provided `signer-key`, the stacker -;; must provide either a signature have an authorization already saved. Refer to -;; `verify-signer-key-sig` for more information. -;; -;; The tokens will unlock and be returned to the Stacker (tx-sender) automatically. -(define-public (stack-stx (amount-ustx uint) - (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) - (start-burn-ht uint) - (lock-period uint) - (signer-sig (optional (buff 65))) - (signer-key (buff 33)) - (max-amount uint) - (auth-id uint)) - ;; this stacker's first reward cycle is the _next_ reward cycle - (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) - (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht)))) - ;; the start-burn-ht must result in the next reward cycle, do not allow stackers - ;; to "post-date" their `stack-stx` transaction - (asserts! (is-eq first-reward-cycle specified-reward-cycle) - (err ERR_INVALID_START_BURN_HEIGHT)) - - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - - ;; tx-sender principal must not be stacking - (asserts! (is-none (get-stacker-info tx-sender)) - (err ERR_STACKING_ALREADY_STACKED)) - - ;; tx-sender must not be delegating - (asserts! (is-none (get-check-delegation tx-sender)) - (err ERR_STACKING_ALREADY_DELEGATED)) - - ;; the Stacker must have sufficient unlocked funds - (asserts! (>= (stx-get-balance tx-sender) amount-ustx) - (err ERR_STACKING_INSUFFICIENT_FUNDS)) - - ;; Validate ownership of the given signer key - (try! (consume-signer-key-authorization pox-addr (- first-reward-cycle u1) "stack-stx" lock-period signer-sig signer-key amount-ustx max-amount auth-id)) - - ;; ensure that stacking can be performed - (try! (can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) - - ;; register the PoX address with the amount stacked - (let ((reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-reward-cycle lock-period amount-ustx tx-sender signer-key)))) - ;; add stacker record - (map-set stacking-state - { stacker: tx-sender } - { pox-addr: pox-addr, - reward-set-indexes: reward-set-indexes, - first-reward-cycle: first-reward-cycle, - lock-period: lock-period, - delegated-to: none }) - - ;; return the lock-up information, so the node can actually carry out the lock. - (ok { stacker: tx-sender, lock-amount: amount-ustx, signer-key: signer-key, unlock-burn-height: (reward-cycle-to-burn-height (+ first-reward-cycle lock-period)) })))) -``` - -
- -The first thing solo stackers will need to do is call the `stack-stx` function. - -Just like in previous versions of PoX, Stackers call `stack-stx`, but with the new arguments added in Nakamoto. The arguments are: - -* Amount: Denoted in uSTX (1 STX = 1,000,000 uSTX) -* PoX Address: the BTC wallet address where to receive Stacking rewards -* Start burn height: the current BTC block height -* Lock period: the number of cycles to lock for (between 1 and 12) -* Signer key: the public key that your signer is using -* Signer signature: the signature that proves control of this signer key -* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX that can be stacked in this transaction. -* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. - -Solo stackers have two choices when it comes to operating as a signer. They can choose to run a signer themselves or work with a signer on their behalf. - -#### Option 1: Act as a signer - -In the “prepare phase” before the next stacking cycle (last 100 blocks), the exact set of Stackers will be selected based on the amount of STX stacked. Just like in previous versions of PoX, each Stacker will have some number of reward slots based on the amount of STX locked. - -It is critical that solo stackers have their signer running during this period. The prepare phase is when distributed key generation (DKG) occurs, which is used when validating blocks by signers. - -In general, you don’t need to do anything actively during this period, other than monitoring your signer software to ensure it’s running properly. - -#### Option 2: Work with a signer - -If you do not want to operate a signer on your own, you can work with another signer. To do this, you will need to collaborate with them to get their signer key and signer signature (details in the following sections), which will have to be passed when calling the stacking functions. - -Rather than needing to find a signer to collaborate with, you can use the solo stacking dapp created by Degen Lab in order to use their signer to solo stack. They've created a UI that makes this process really simple. - -They also have a tool for you to generate a signer signature if you prefer to call the stacking functions yourself. - -#### Extending the stacking period - -Just like in the current version of PoX, you can extend your lock period while still Stacking. The function called is `stack-extend`. - -
- -Function source code - -```clojure -;; Extend an active Stacking lock. -;; *New in Stacks 2.1* -;; This method extends the `tx-sender`'s current lockup for an additional `extend-count` -;; and associates `pox-addr` with the rewards, The `signer-key` will be the key -;; used for signing. The `tx-sender` can thus decide to change the key when extending. -;; -;; Because no additional STX are locked in this function, the `amount` field used -;; to verify the signer key authorization is zero. Refer to `verify-signer-key-sig` for more information. -(define-public (stack-extend (extend-count uint) - (pox-addr { version: (buff 1), hashbytes: (buff 32) }) - (signer-sig (optional (buff 65))) - (signer-key (buff 33)) - (max-amount uint) - (auth-id uint)) - (let ((stacker-info (stx-account tx-sender)) - ;; to extend, there must already be an etry in the stacking-state - (stacker-state (unwrap! (get-stacker-info tx-sender) (err ERR_STACK_EXTEND_NOT_LOCKED))) - (amount-ustx (get locked stacker-info)) - (unlock-height (get unlock-height stacker-info)) - (cur-cycle (current-pox-reward-cycle)) - ;; first-extend-cycle will be the cycle in which tx-sender *would have* unlocked - (first-extend-cycle (burn-height-to-reward-cycle unlock-height)) - ;; new first cycle should be max(cur-cycle, stacker-state.first-reward-cycle) - (cur-first-reward-cycle (get first-reward-cycle stacker-state)) - (first-reward-cycle (if (> cur-cycle cur-first-reward-cycle) cur-cycle cur-first-reward-cycle))) - - ;; must be called with positive extend-count - (asserts! (>= extend-count u1) - (err ERR_STACKING_INVALID_LOCK_PERIOD)) - - ;; stacker must be directly stacking - (asserts! (> (len (get reward-set-indexes stacker-state)) u0) - (err ERR_STACKING_IS_DELEGATED)) - - ;; stacker must not be delegating - (asserts! (is-none (get delegated-to stacker-state)) - (err ERR_STACKING_IS_DELEGATED)) - - ;; Verify signature from delegate that allows this sender for this cycle - (try! (consume-signer-key-authorization pox-addr cur-cycle "stack-extend" extend-count signer-sig signer-key u0 max-amount auth-id)) - - ;; TODO: add more assertions to sanity check the `stacker-info` values with - ;; the `stacker-state` values - - (let ((last-extend-cycle (- (+ first-extend-cycle extend-count) u1)) - (lock-period (+ u1 (- last-extend-cycle first-reward-cycle))) - (new-unlock-ht (reward-cycle-to-burn-height (+ u1 last-extend-cycle)))) - - ;; first cycle must be after the current cycle - (asserts! (> first-extend-cycle cur-cycle) (err ERR_STACKING_INVALID_LOCK_PERIOD)) - ;; lock period must be positive - (asserts! (> lock-period u0) (err ERR_STACKING_INVALID_LOCK_PERIOD)) - - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - - ;; tx-sender must be locked - (asserts! (> amount-ustx u0) - (err ERR_STACK_EXTEND_NOT_LOCKED)) - - ;; tx-sender must not be delegating - (asserts! (is-none (get-check-delegation tx-sender)) - (err ERR_STACKING_ALREADY_DELEGATED)) - - ;; standard can-stack-stx checks - (try! (can-stack-stx pox-addr amount-ustx first-extend-cycle lock-period)) - - ;; register the PoX address with the amount stacked - ;; for the new cycles - (let ((extended-reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-extend-cycle extend-count amount-ustx tx-sender signer-key))) - (reward-set-indexes - ;; use the active stacker state and extend the existing reward-set-indexes - (let ((cur-cycle-index (- first-reward-cycle (get first-reward-cycle stacker-state))) - (old-indexes (get reward-set-indexes stacker-state)) - ;; build index list by taking the old-indexes starting from cur cycle - ;; and adding the new indexes to it. this way, the index is valid starting from the current cycle - (new-list (concat (default-to (list) (slice? old-indexes cur-cycle-index (len old-indexes))) - extended-reward-set-indexes))) - (unwrap-panic (as-max-len? new-list u12))))) - ;; update stacker record - (map-set stacking-state - { stacker: tx-sender } - { pox-addr: pox-addr, - reward-set-indexes: reward-set-indexes, - first-reward-cycle: first-reward-cycle, - lock-period: lock-period, - delegated-to: none }) - - ;; return lock-up information - (ok { stacker: tx-sender, unlock-burn-height: new-unlock-ht }))))) -``` - -
- -You can “rotate” your signing key when extending your lock period. - -The arguments are: - -* Extend count: the number of cycles to add to your lock period. The resulting lock period cannot be larger than 12. For example, if you're currently locked with 6 cycles remaining, the maximum number you can extend is 6. -* Pox Address: the BTC address to receive rewards -* Signer public key: the public key used for signing. This can stay the same, or you can use a new key. -* Signer signature: a signature proving control of your signing key -* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX (1 stx = 1,000,000 uSTX) that can be stacked in this transaction. -* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. - -#### Increasing the stacking amount - -The stacking amount can also be increased while actively Stacking. The increased position will take effect starting with the next Stacking cycle. The function called is `stack-increase`. - -
- -Function source code - -```clojure -;; Increase the number of STX locked. -;; *New in Stacks 2.1* -;; This method locks up an additional amount of STX from `tx-sender`'s, indicated -;; by `increase-by`. The `tx-sender` must already be Stacking & must not be -;; straddling more than one signer-key for the cycles effected. -;; Refer to `verify-signer-key-sig` for more information on the authorization parameters -;; included here. -(define-public (stack-increase - (increase-by uint) - (signer-sig (optional (buff 65))) - (signer-key (buff 33)) - (max-amount uint) - (auth-id uint)) - (let ((stacker-info (stx-account tx-sender)) - (amount-stacked (get locked stacker-info)) - (amount-unlocked (get unlocked stacker-info)) - (unlock-height (get unlock-height stacker-info)) - (cur-cycle (current-pox-reward-cycle)) - (first-increased-cycle (+ cur-cycle u1)) - (stacker-state (unwrap! (map-get? stacking-state - { stacker: tx-sender }) - (err ERR_STACK_INCREASE_NOT_LOCKED))) - (cur-pox-addr (get pox-addr stacker-state)) - (cur-period (get lock-period stacker-state))) - ;; tx-sender must be currently locked - (asserts! (> amount-stacked u0) - (err ERR_STACK_INCREASE_NOT_LOCKED)) - ;; must be called with positive `increase-by` - (asserts! (>= increase-by u1) - (err ERR_STACKING_INVALID_AMOUNT)) - ;; stacker must have enough stx to lock - (asserts! (>= amount-unlocked increase-by) - (err ERR_STACKING_INSUFFICIENT_FUNDS)) - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - ;; stacker must be directly stacking - (asserts! (> (len (get reward-set-indexes stacker-state)) u0) - (err ERR_STACKING_IS_DELEGATED)) - ;; stacker must not be delegating - (asserts! (is-none (get delegated-to stacker-state)) - (err ERR_STACKING_IS_DELEGATED)) - - ;; Validate that amount is less than or equal to `max-amount` - (asserts! (>= max-amount (+ increase-by amount-stacked)) (err ERR_SIGNER_AUTH_AMOUNT_TOO_HIGH)) - - ;; Verify signature from delegate that allows this sender for this cycle - (try! (consume-signer-key-authorization cur-pox-addr cur-cycle "stack-increase" cur-period signer-sig signer-key increase-by max-amount auth-id)) - - ;; update reward cycle amounts - (asserts! (is-some (fold increase-reward-cycle-entry - (get reward-set-indexes stacker-state) - (some { first-cycle: first-increased-cycle, - reward-cycle: (get first-reward-cycle stacker-state), - stacker: tx-sender, - add-amount: increase-by, - signer-key: signer-key }))) - (err ERR_INVALID_INCREASE)) - ;; NOTE: stacking-state map is unchanged: it does not track amount-stacked in PoX-4 - (ok { stacker: tx-sender, total-locked: (+ amount-stacked increase-by)}))) -``` - -
- -The arguments are: - -* Increase by: the amount of uSTX to add to your lock amount. -* Signer public key: the public key used for signing. This can stay the same, or you can use a new key. -* Signer signature: a signature proving control of your signing key -* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX (1 stx = 1,000,000 uSTX) that can be stacked in this transaction. -* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. - -### Step by Step Stacking Guide - -Now that you are familiar with the overall stacking flow and the different roles played, let's dive into the step-by-step guide for actually conducting the stacking process. - -{% hint style="info" %} -There are several ways you can go about stacking. This guide will cover using Leather Earn, which is a stacking web application and the simplest option. - -Additionally, you can choose to call the stacking functions directly from the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) in the explorer. - -The fields and process will be the same, but the UI will differ. - -Finally, you can stack using JS and the [@stacks/stacking](https://github.com/stx-labs/stacks.js/tree/main/packages/stacking) package if you prefer. Again, the functions and parameters will be the same, you will just be writing your JS code directly instead of using a UI. - -If you are interested in using this method, you'll want to follow the [stacking guide](https://docs.hiro.so/stacks.js/guides/how-to-integrate-stacking) created by Hiro, and be sure to integrate the new parameters described in [Hiro's Nakamoto update doc](https://docs.hiro.so/nakamoto/stacks-js). -{% endhint %} - -### Step 1: Run or work with a signer - -This is a necessary prerequisite to stacking as a solo stacker or pool operator. - -Running a signer involves setting up a hosted production environment that includes both a Stacks Node and the Stacks Signer. For more information, refer to the [running a signer doc](../run-a-signer/). - -Once the signer software is running, you'll have to keep track of the `stacks_private_key` that you used when configuring your signer software. This will be used in subsequent Stacking transactions. - -{% hint style="info" %} -In the note above about pool operator vs signer keys, this corresponds to your signer key, not your pool operator wallet -{% endhint %} - -Alternatively, you can work with a signer and have them perform step 2 below on your behalf. - -### Step 2: Generate a signer key signature - -#### Overview of signer keys and signatures - -The main difference with Stacking in Nakamoto is that the Signer (either solo Stacker or signer running a pool) needs to include new parameters in their Stacking transactions. These are Clarity transactions that Stackers will call when interacting with the `pox-4.clar` contract. Interacting with this contract is how you as a Stacker actually stack your STX tokens. - -{% hint style="info" %} -The current pox-4 contract address can be found by visiting the following endpoint of the Hiro API: [https://api.mainnet.hiro.so/v2/pox](https://api.mainnet.hiro.so/v2/pox). - -You can then visit the [Explorer](https://explorer.hiro.so/?chain=mainnet) to view the contract and pass in the contract id. - -You may want to review this contract to familiarize yourself with it. -{% endhint %} - -Here is an overview of the fields you will need to pass. We'll cover how to get and pass these fields as we dive further into this doc: - -{% stepper %} -{% step %} -#### signer-key - -The public key that corresponds to the `stacks_private_key` your signer is using. -{% endstep %} - -{% step %} -#### signer-signature - -A signature that demonstrates that you actually control your `signer-key`. Because signer keys need to be unique, this is also a safety check to ensure that other Stackers can’t use someone else’s public key. -{% endstep %} - -{% step %} -#### max-amount - -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 parameter dictates the maximum amount of uSTX that can be used to add more locked STX. -{% endstep %} - -{% step %} -#### auth-id - -A random integer that prevents the re-use of a particular signature, similar to how nonces are used with transactions. Must be less than 14 characters. -{% endstep %} -{% endstepper %} - -Signer signatures are signatures created using a particular signer key. They demonstrate that the controller of that signer key is allowing a Stacker to use their signing key. The signer signature’s message hash is created using the following data: - -* `method`: a string that indicates the Stacking method that is allowed to utilize this signature. The valid options are `stack-increase`, `stack-stx`, `stack-extend`, `agg-commit` (for stack-aggregation-commit-indexed), or `agg-increase` (for stack-aggregation-increase) -* `max-amount`: described above -* `auth-id`: described above -* `period`: a value between 1 and 12, which indicates the number of cycles that the Stacker is allowed to lock their STX for in this particular Stacking transaction. For `agg-commit`, this must be equal to 1 -* `reward-cycle`: This represents the reward cycle in which the Stacking transaction can be confirmed. For solo stacking operations (`stack-stx`, `stack-extend` and `stack-increase`), this has to be set as the current cycle. -* `pox-address`: The Bitcoin address that is allowed to be used for receiving rewards. This can be set to any Bitcoin address that you have access to. -* `config`: This represents the signer configuration file path where the `stacks_private_key` is located, and it is used for signing the generated signature. - -Now that we have an overview of role and contents of signatures, let's see how to actually generate them. You have several options available. - -Generating your signature with Degen Lab's stacks.tools - -Degen Lab has a signature generation tool that will generate a signature using their signer. This is the quickest and simplest option. To generate a signature using this method, all you need to do is visit their [signature tool](https://signature.stacking.tools/) and pass in the relevant information as covered on this page. - -#### Generating your signature with stacks.js - -The [@stacks/stacking](https://www.npmjs.com/package/@stacks/stacking) NPM package provides interfaces to generate and use signer signatures. - -There is a function called `signPoxSignature` that will allow you to generate this signature and pass it in to the stacking function. - -More information and code samples can be found on [Hiro's Nakamoto docs](https://docs.hiro.so/nakamoto/stacks-js). - -#### Generating your signature using the stacks-signer CLI - -If you already have your signer configured and set up, you can use the `stacks-signer` CLI to generate this signature. You can either SSH into your running signer or use the `stacks-signer` CLI locally. If using the CLI locally, you will need to ensure you have a matching configuration file located on your filesystem. Having a matching configuration file is important to ensure that the signer public key you make in Stacking transactions is the same as in your hosted signer. - -The CLI command is: - -```bash -# Max Amount equivallent to 1M STX - -# Auth Id should be a random string less than 14 characters -stacks-signer generate-stacking-signature \ - --method stack-stx \ - --max-amount 1000000000000 \ - --auth-id 71948271489 \ - --period 1 \ - --reward-cycle 100 \ - --pox-address bc1... \ - --config ./config.toml \ - --json -``` - -These arguments match those described in section [Overview of signer keys and signatures](solo-stack.md#overview-of-signer-keys-and-signatures), with the addition of: - -* `--json`, to optionally output the resulting signature in JSON - -You can use the following command to generate a random `32` bit integer as `auth-id`: - -```bash -python3 -c 'import secrets; print(secrets.randbits(32))' -``` - -Once the `generate-stacking-signature` command is run, the CLI will output a JSON: - -```json -{"authId":"1234","maxAmount":"12345","method":"stack-stx","period":1,"poxAddress":"bc1...","rewardCycle":100,"signerKey":"aaaaaaaa","signerSignature":"bbbbbbbbbbb"} -``` - -You will use the JSON when calling Stacking transactions from your Stacker address as outlined above. Remember that this may be different than your signer address. - -#### Generating your signature with Leather Earn - -Leather Earn is a web application that provides an easy-to-use interface for stacking and generating signatures. We'll cover using Leather Earn for stacking at the end of this document, here we will cover how to use it to generate a signature. - -{% hint style="info" %} -At the time of writing, this has only been tested using the [Leather](https://leather.io/) wallet. -{% endhint %} - -You can visit [earn.leather.io](https://earn.leather.io/) to generate a signer key signature. Make sure you’re connected to the correct network.\ -To generate a signer key signature, it’s important that you’ve logged in Leather with the same secret key that was used to [generate your signer key](broken-reference/), not the account that will serve as your pool operator address. Once you’ve setup that account on Leather, you can log in to Leather Earn.\ -Click the link “Signer key signature” at the bottom of the page. This will open the “generate a signer key signature” page. - -The fields are: - -* Reward cycle: - * For all solo stacking transactions, this must equal the current reward cycle, not the cycle in which they will start stacking. The field defaults to the current reward cycle. - * For stack-aggregation-commit-indexed, this field must equal the cycle used in that function’s “reward cycle” argument. Typically, that equates to current\_cycle + 1. -* Bitcoin address: the PoX reward address that can be used -* Topic: the stacking function that will use this signature -* Max amount: max amount of STX that can be used. Defaults to “max possible amount” -* Auth ID: defaults to random int -* Duration: must match the number of cycles used in the stacking transaction. For stack-aggregation-commit-indexed, use “1”. - -{% hint style="warning" %} -Each of these fields must be exactly matched in order for the Stacking transaction to work. Future updates to Leather Earn will verify the signature before the transaction is made. -{% endhint %} - -Click the “generate signature” button to popup a Leather page where you can generate the signature. Once you submit that popup, Leather Earn will have the signer key and signature you generated. - -After you sign that message, you'll see the information you can use in your Stacking transactions, including the signer public key and signature. - -You can click the “copy” icon next to “signer details to share with stackers”. This will copy a JSON string, which can be directly pasted into the Leather Earn page where you make your Stacking transaction. Alternatively, this information can be entered manually. - -We'll cover the Leather Earn pages for actually making those transactions in the next section of this document. - -#### Using a hardware or software wallet to generate signatures - -When the signer is configured with a `stacks_private_key`, the signer may want to be able to use that key in a wallet to make stacking signatures. - -If the signer uses a tool like [@stacks/cli](https://docs.hiro.so/get-started/command-line-interface) to generate the key, the CLI also outputs a mnemonic (aka “seed phrase”) that can be imported into a wallet. Because the Stacks CLI uses the standard derivation path for generating Stacks keys, any Stacks wallet will default to having that same private key when the wallet is imported from a derivation path. Similarly, if a hardware wallet is setup with that mnemonic, then the Signer can use a wallet like Leather to make stacking signatures. - -The workflow for setting up a wallet to generate signatures: - -{% stepper %} -{% step %} -Use @stacks/cli to generate the keychain and private key. - -* Typically, when using a hardware wallet, it’s better to generate the mnemonic on the hardware wallet. For this use case, however, the signer software needs the private key, and hardware wallets (by design) don’t allow exporting private keys. -{% endstep %} - -{% step %} -Take the `privateKey` from the CLI output and add it to your signer’s configuration. -{% endstep %} - -{% step %} -Take the mnemonic (24 words) and either: - -* Setup a new hardware wallet with this mnemonic, or -* Store it somewhere securely, like a password manager. When the signer needs to generate signatures for Stacking transactions, they can import it into either Leather or XVerse. -{% endstep %} -{% endstepper %} - -When the user needs to generate signatures: - -{% stepper %} -{% step %} -Set up your wallet with your signer key’s private key. Either: - -* Setup your Leather wallet with a Ledger hardware wallet, or -* Import your mnemonic into Leather, XVerse, or another Stacks wallet. -{% endstep %} - -{% step %} -Open an app that has stacking signature functionality built-in. -{% endstep %} - -{% step %} -Connect your wallet to the app (aka sign in). -{% endstep %} - -{% step %} -In the app, enter your PoX address and “submit”. - -* The app will popup a window in your wallet that prompts you to sign the information and will show clear information about what you’re signing. -{% endstep %} - -{% step %} -Create the signature. - -* If using a Ledger, confirm on your device. -{% endstep %} - -{% step %} -The app will display two results: - -* Your signer key, which is the public key associated with your signer’s key. -* Your signer signature. -{% endstep %} - -{% step %} -Finally, make a Stacking transaction using the signer key and signer signature. -{% endstep %} -{% endstepper %} - -Now that you have your signer signature generated, it's time to start stacking. This process will vary depending on your chosen method. We've included instructions for solo stacking using [Leather Earn](https://earn.leather.io/) below. - -### Step 3: Stack your STX - -#### stack-stx - -To start, you'll visit [Leather Earn](https://earn.leather.io/) and click the “Stack independently” button on the home page. - -This page will allow you to input the following input: - -* The amount of STX you want to lock -* The duration (in number of cycles) to lock for -* Your BTC address where you will receive Stacking rewards -* New fields: - * Your signer public key - * Your signer key signature (this is what you generated in the previous step) - * Auth ID - * Max amount - -#### stack-extend - -If you want to extend the time that your STX will be locked for, you can use the stack-extend page on Leather Earn. - -If you’re already stacking, the home page will provide a link to “view stacking details”. From there, you can choose to extend. - -On this page are the following fields: - -* The number of cycles you want to extend for -* Your BTC address to receive rewards -* New fields: - * Signer public key - * Signer key signature - * Auth ID - * Max amount - -#### stack-increase - -If you want to increase the amount of STX locked, you can use the stack-increase page on Leather Earn. - -If you’re already stacking, the home page will provide a link to “view stacking details”. From there, you can choose to increase. - -On this page are the following fields: - -* The amount of STX you want to increase by -* New fields: - * Signer public key - * Signer key signature - * Auth ID - * Max amount diff --git a/docs/operate/stacking-stx/solo-stacking.md b/docs/operate/stacking-stx/solo-stacking.md new file mode 100644 index 0000000000..e272713035 --- /dev/null +++ b/docs/operate/stacking-stx/solo-stacking.md @@ -0,0 +1,388 @@ +# Solo Stacking + +This guide covers everything you need to stack independently as a solo stacker: starting, extending, increasing, and stopping your stacking position. + +{% hint style="info" %} +This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. +{% endhint %} + +Solo stacking requires meeting the minimum STX threshold and either running a signer or collaborating with one. The minimum amount is dynamic and can be found at the [pox endpoint](https://api.mainnet.hiro.so/v2/pox) under `min_threshold_ustx` (1 STX = 1,000,000 uSTX). + +{% hint style="info" %} +[Degen Lab](https://solo.stacking.tools/) provides a solo stacking dapp that lets you stack without running your own signer, as they operate one on your behalf. This is likely the easiest option for solo stacking. +{% endhint %} + +If you don't meet the stacking minimum, see the [Stack with a Pool](stack-with-a-pool.md) guide instead. + +## Prerequisites + +Before you begin, make sure you have: + +1. **A running signer** or a signer you are collaborating with. See the [Run a Signer](../run-a-signer/) guide. +2. **A signer key signature** for the stacking transaction you want to make. See [Generate a Signer Signature](generate-signer-signature.md). +3. **Sufficient STX** — at or above the minimum stacking threshold. + +{% hint style="info" %} +There are several ways to make stacking transactions. This guide covers using [Leather Earn](https://earn.leather.io/), which is the simplest option. You can also call the stacking functions directly from the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) in the explorer, or use the [@stacks/stacking](https://github.com/stx-labs/stacks.js/tree/main/packages/stacking) NPM package. + +The functions and parameters are the same regardless of method. +{% endhint %} + +*** + +## Start Stacking + +### Call `stack-stx` + +
+ +Function source code + +```clojure +;; Lock up some uSTX for stacking! Note that the given amount here is in micro-STX (uSTX). +;; The STX will be locked for the given number of reward cycles (lock-period). +;; This is the self-service interface. tx-sender will be the Stacker. +;; +;; * The given stacker cannot currently be stacking. +;; * You will need the minimum uSTX threshold. This will be determined by (get-stacking-minimum) +;; at the time this method is called. +;; * You may need to increase the amount of uSTX locked up later, since the minimum uSTX threshold +;; may increase between reward cycles. +;; * You need to provide a signer key to be used in the signer DKG process. +;; * The Stacker will receive rewards in the reward cycle following `start-burn-ht`. +;; Importantly, `start-burn-ht` may not be further into the future than the next reward cycle, +;; and in most cases should be set to the current burn block height. +;; +;; To ensure that the Stacker is authorized to use the provided `signer-key`, the stacker +;; must provide either a signature have an authorization already saved. Refer to +;; `verify-signer-key-sig` for more information. +;; +;; The tokens will unlock and be returned to the Stacker (tx-sender) automatically. +(define-public (stack-stx (amount-ustx uint) + (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (start-burn-ht uint) + (lock-period uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; tx-sender principal must not be stacking + (asserts! (is-none (get-stacker-info tx-sender)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance tx-sender) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; Validate ownership of the given signer key + (try! (consume-signer-key-authorization pox-addr (- first-reward-cycle u1) "stack-stx" lock-period signer-sig signer-key amount-ustx max-amount auth-id)) + + ;; ensure that stacking can be performed + (try! (can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked + (let ((reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-reward-cycle lock-period amount-ustx tx-sender signer-key)))) + ;; add stacker record + (map-set stacking-state + { stacker: tx-sender } + { pox-addr: pox-addr, + reward-set-indexes: reward-set-indexes, + first-reward-cycle: first-reward-cycle, + lock-period: lock-period, + delegated-to: none }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: tx-sender, lock-amount: amount-ustx, signer-key: signer-key, unlock-burn-height: (reward-cycle-to-burn-height (+ first-reward-cycle lock-period)) })))) +``` + +
+ +The arguments are: + +* **Amount**: Denoted in uSTX (1 STX = 1,000,000 uSTX) +* **PoX Address**: the BTC wallet address where you will receive stacking rewards +* **Start burn height**: the current BTC block height +* **Lock period**: the number of cycles to lock for (between 1 and 12) +* **Signer key**: the public key that your signer is using +* **Signer signature**: a signature that proves control of this signer key (see [Generate a Signer Signature](generate-signer-signature.md)) +* **Max Amount**: used to validate the signer signature; represents the maximum number of uSTX that can be stacked in this transaction +* **Auth Id**: used to validate the signer signature; a random integer that prevents re-use of this particular signature + +### Using Leather Earn + +Visit [Leather Earn](https://earn.leather.io/) and click the "Stack independently" button on the home page. You will be prompted to enter: + +* The amount of STX to lock +* The duration (number of cycles) to lock for +* Your BTC address for stacking rewards +* Your signer public key +* Your signer key signature +* Auth ID +* Max amount + +{% hint style="info" %} +When using Leather Earn, you can paste the JSON output from the signature generation step directly into the form. +{% endhint %} + +### Acting as a signer vs. working with one + +**Option 1: Act as a signer.** If you run your own signer, ensure it is running during the prepare phase (last 100 blocks before the next cycle). This is when distributed key generation (DKG) occurs. You don't need to do anything actively during this period other than monitoring your signer. + +**Option 2: Work with a signer.** If you don't want to run a signer yourself, you can collaborate with an existing one. You'll need their signer key and a signer signature generated for your stacking transaction. [Degen Lab's solo stacking dapp](https://solo.stacking.tools/) simplifies this process by providing their signer for you to use. + +*** + +## Extend Your Lock Period + +You can extend your lock period while actively stacking by calling `stack-extend`. You can also rotate your signer key and change your Bitcoin reward address as part of this call. + +
+ +Function source code + +```clojure +;; Extend an active Stacking lock. +;; *New in Stacks 2.1* +;; This method extends the `tx-sender`'s current lockup for an additional `extend-count` +;; and associates `pox-addr` with the rewards, The `signer-key` will be the key +;; used for signing. The `tx-sender` can thus decide to change the key when extending. +;; +;; Because no additional STX are locked in this function, the `amount` field used +;; to verify the signer key authorization is zero. Refer to `verify-signer-key-sig` for more information. +(define-public (stack-extend (extend-count uint) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((stacker-info (stx-account tx-sender)) + ;; to extend, there must already be an etry in the stacking-state + (stacker-state (unwrap! (get-stacker-info tx-sender) (err ERR_STACK_EXTEND_NOT_LOCKED))) + (amount-ustx (get locked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (cur-cycle (current-pox-reward-cycle)) + ;; first-extend-cycle will be the cycle in which tx-sender *would have* unlocked + (first-extend-cycle (burn-height-to-reward-cycle unlock-height)) + ;; new first cycle should be max(cur-cycle, stacker-state.first-reward-cycle) + (cur-first-reward-cycle (get first-reward-cycle stacker-state)) + (first-reward-cycle (if (> cur-cycle cur-first-reward-cycle) cur-cycle cur-first-reward-cycle))) + + ;; must be called with positive extend-count + (asserts! (>= extend-count u1) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; stacker must be directly stacking + (asserts! (> (len (get reward-set-indexes stacker-state)) u0) + (err ERR_STACKING_IS_DELEGATED)) + + ;; stacker must not be delegating + (asserts! (is-none (get delegated-to stacker-state)) + (err ERR_STACKING_IS_DELEGATED)) + + ;; Verify signature from delegate that allows this sender for this cycle + (try! (consume-signer-key-authorization pox-addr cur-cycle "stack-extend" extend-count signer-sig signer-key u0 max-amount auth-id)) + + (let ((last-extend-cycle (- (+ first-extend-cycle extend-count) u1)) + (lock-period (+ u1 (- last-extend-cycle first-reward-cycle))) + (new-unlock-ht (reward-cycle-to-burn-height (+ u1 last-extend-cycle)))) + + ;; first cycle must be after the current cycle + (asserts! (> first-extend-cycle cur-cycle) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + ;; lock period must be positive + (asserts! (> lock-period u0) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; tx-sender must be locked + (asserts! (> amount-ustx u0) + (err ERR_STACK_EXTEND_NOT_LOCKED)) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; standard can-stack-stx checks + (try! (can-stack-stx pox-addr amount-ustx first-extend-cycle lock-period)) + + ;; register the PoX address with the amount stacked + ;; for the new cycles + (let ((extended-reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-extend-cycle extend-count amount-ustx tx-sender signer-key))) + (reward-set-indexes + ;; use the active stacker state and extend the existing reward-set-indexes + (let ((cur-cycle-index (- first-reward-cycle (get first-reward-cycle stacker-state))) + (old-indexes (get reward-set-indexes stacker-state)) + ;; build index list by taking the old-indexes starting from cur cycle + ;; and adding the new indexes to it. this way, the index is valid starting from the current cycle + (new-list (concat (default-to (list) (slice? old-indexes cur-cycle-index (len old-indexes))) + extended-reward-set-indexes))) + (unwrap-panic (as-max-len? new-list u12))))) + ;; update stacker record + (map-set stacking-state + { stacker: tx-sender } + { pox-addr: pox-addr, + reward-set-indexes: reward-set-indexes, + first-reward-cycle: first-reward-cycle, + lock-period: lock-period, + delegated-to: none }) + + ;; return lock-up information + (ok { stacker: tx-sender, unlock-burn-height: new-unlock-ht }))))) +``` + +
+ +The arguments are: + +* **Extend count**: the number of cycles to add to your lock period. The resulting total lock period cannot exceed 12. For example, if you have 6 cycles remaining, the maximum you can extend is 6. +* **Pox Address**: the BTC address to receive rewards. This can be changed from your original address. +* **Signer public key**: the public key used for signing. This can stay the same, or you can rotate to a new key. See [Key and Address Rotation](key-and-address-rotation.md). +* **Signer signature**: a signature proving control of your signing key (see [Generate a Signer Signature](generate-signer-signature.md)) +* **Max Amount**: used to validate the signer signature +* **Auth Id**: used to validate the signer signature + +### Using Leather Earn + +If you're already stacking, the Leather Earn home page will show a link to "view stacking details". From there, you can choose to extend. The form asks for: + +* The number of cycles to extend for +* Your BTC address for rewards +* Signer public key +* Signer key signature +* Auth ID +* Max amount + +*** + +## Increase Your Stacked Amount + +You can increase the amount of STX locked while actively stacking. The increased position takes effect starting with the next stacking cycle. Call the `stack-increase` function. + +
+ +Function source code + +```clojure +;; Increase the number of STX locked. +;; *New in Stacks 2.1* +;; This method locks up an additional amount of STX from `tx-sender`'s, indicated +;; by `increase-by`. The `tx-sender` must already be Stacking & must not be +;; straddling more than one signer-key for the cycles effected. +;; Refer to `verify-signer-key-sig` for more information on the authorization parameters +;; included here. +(define-public (stack-increase + (increase-by uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((stacker-info (stx-account tx-sender)) + (amount-stacked (get locked stacker-info)) + (amount-unlocked (get unlocked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (cur-cycle (current-pox-reward-cycle)) + (first-increased-cycle (+ cur-cycle u1)) + (stacker-state (unwrap! (map-get? stacking-state + { stacker: tx-sender }) + (err ERR_STACK_INCREASE_NOT_LOCKED))) + (cur-pox-addr (get pox-addr stacker-state)) + (cur-period (get lock-period stacker-state))) + ;; tx-sender must be currently locked + (asserts! (> amount-stacked u0) + (err ERR_STACK_INCREASE_NOT_LOCKED)) + ;; must be called with positive `increase-by` + (asserts! (>= increase-by u1) + (err ERR_STACKING_INVALID_AMOUNT)) + ;; stacker must have enough stx to lock + (asserts! (>= amount-unlocked increase-by) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; stacker must be directly stacking + (asserts! (> (len (get reward-set-indexes stacker-state)) u0) + (err ERR_STACKING_IS_DELEGATED)) + ;; stacker must not be delegating + (asserts! (is-none (get delegated-to stacker-state)) + (err ERR_STACKING_IS_DELEGATED)) + + ;; Validate that amount is less than or equal to `max-amount` + (asserts! (>= max-amount (+ increase-by amount-stacked)) (err ERR_SIGNER_AUTH_AMOUNT_TOO_HIGH)) + + ;; Verify signature from delegate that allows this sender for this cycle + (try! (consume-signer-key-authorization cur-pox-addr cur-cycle "stack-increase" cur-period signer-sig signer-key increase-by max-amount auth-id)) + + ;; update reward cycle amounts + (asserts! (is-some (fold increase-reward-cycle-entry + (get reward-set-indexes stacker-state) + (some { first-cycle: first-increased-cycle, + reward-cycle: (get first-reward-cycle stacker-state), + stacker: tx-sender, + add-amount: increase-by, + signer-key: signer-key }))) + (err ERR_INVALID_INCREASE)) + ;; NOTE: stacking-state map is unchanged: it does not track amount-stacked in PoX-4 + (ok { stacker: tx-sender, total-locked: (+ amount-stacked increase-by)}))) +``` + +
+ +The arguments are: + +* **Increase by**: the amount of uSTX to add to your locked amount +* **Signer public key**: the public key used for signing. This can stay the same, or you can use a new key. +* **Signer signature**: a signature proving control of your signing key (see [Generate a Signer Signature](generate-signer-signature.md)) +* **Max Amount**: used to validate the signer signature; represents the maximum number of uSTX (1 STX = 1,000,000 uSTX) that can be stacked in this transaction +* **Auth Id**: used to validate the signer signature + +### Using Leather Earn + +If you're already stacking, the home page will show a link to "view stacking details". From there, choose to increase. The form asks for: + +* The amount of STX to increase by +* Signer public key +* Signer key signature +* Auth ID +* Max amount + +*** + +## Stop Stacking + +When stacking solo, your STX is locked for a fixed period defined when you initiated stacking or extended the lock period. **No additional action is required to stop stacking** — your tokens unlock automatically when the lock period expires. + +{% hint style="info" %} +Both the `stack-stx` and `stack-extend` functions emit an event that includes the `unlock-burn-height` field. This is the burn block height at which your tokens will be automatically unlocked. +{% endhint %} + +To avoid your position being extended, simply do not call `stack-extend` before the current lock period ends. Once the lock period expires, your STX will be returned to your account. + +### Monitoring your unlock + +* Use your wallet's interface or the [Hiro Explorer](https://explorer.hiro.so/?chain=mainnet) to track the status of your lock period. +* Hiro's API offers an endpoint to [Get account STX balance](https://docs.hiro.so/stacks/api/accounts/stx-balances), which includes the `burnchain_unlock_height` representing when your STX unlock. + +*** + +## How Signer Registration Works + +In the prepare phase before the next stacking cycle (last 100 blocks), the signer set is selected based on the amount of STX stacked. For solo stackers, the only transaction needed is `stack-stx` — once it is confirmed during the first 2000 blocks of the current reward cycle, your signer will be registered in the next cycle's signer set. + +It is critical that your signer is running during the prepare phase. This is when DKG occurs, and the signer automatically participates — no manual action required beyond monitoring. diff --git a/docs/operate/stacking-stx/stack-with-a-pool.md b/docs/operate/stacking-stx/stack-with-a-pool.md index c6712b3c82..d7f8c98ba6 100644 --- a/docs/operate/stacking-stx/stack-with-a-pool.md +++ b/docs/operate/stacking-stx/stack-with-a-pool.md @@ -1,65 +1,178 @@ # Stack with a Pool -The most common stacking scenario is to be an individual stacker that does not meet the stacking minimum and to stack with a pool. +This guide covers delegated stacking from the **delegator** perspective — how to delegate your STX to a pool, increase your delegation, revoke it, and stop stacking. -This is also the least complex version and is very easy to accomplish. - -Be sure you are familiar with the [concept of stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) before digging into this. - -The dynamic minimum required to stack STX changes based on the total liquid supply of STX in the network and can be found by looking at the `pox` endpoint of the Hiro API: [https://api.hiro.so/v2/pox](https://api.hiro.so/v2/pox). +{% hint style="info" %} +This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. +{% endhint %} -If you do not meet this minimum, you'll need to delegate your STX to a pool operator. +Delegating is the most common stacking scenario. It applies when you do not meet the minimum STX threshold to solo stack and want a pool operator to stack on your behalf. This is a **non-custodial** delegation — your STX do not leave your wallet. -This is a non-custodial delegation, meaning that your STX do not actually leave your wallet. +The minimum stacking amount is dynamic and can be found at the [pox endpoint](https://api.mainnet.hiro.so/v2/pox) under `min_threshold_ustx` (1 STX = 1,000,000 uSTX). {% hint style="info" %} -Pool operators have a lot of control over exactly how they implement stacking. Usually users will be interacting with a wrapper contract that the pool operator has created to utilize the core stacking contract. +Pool operators have control over how they implement stacking and reward distribution. Usually you will interact with a wrapper contract the pool operator has created. {% endhint %} -Delegating your STX to a pool operator involves a few steps: +If you want to operate a pool instead, see the [Operate a Pool](operate-a-pool.md) guide. + +*** + +## Delegate to a Pool {% stepper %} {% step %} #### Find a pool -The first step is to find a stacking pool you would like to stack with. Pool operators have a lot of control over exactly how they implement stacking and reward distribution, and they all do things a bit differently, so it's important to do your research. The Stacks website has a great [page on stacking](https://www.stacks.co/learn/stacking) that links to several pool operators for you to research. +The Stacks website has a [page on stacking](https://www.stacks.co/learn/stacking) that links to several pool operators. Do your research — operators differ in reward distribution, fees, and trust models. {% endstep %} {% step %} -#### Use the pool's UI to call the delegate function - -After you've chosen your pool operator, you'll need to get set up with a [Stacks-compatible wallet](https://www.stacks.co/explore/ecosystem?category=All+Teams#wallets) like Leather, Xverse, or Asigna. - -Then you'll use your chosen pool operator's UI to call their delegation function. You may need to pass a couple of parameters here like how long you want to grant delegation permission for. +#### Call `delegate-stx` + +Use your pool operator's UI or call the function directly to delegate. This does not lock your STX — it gives the pool operator **permission** to stack on your behalf. + +
+ +Function source code + +```clojure +;; Delegate to `delegate-to` the ability to stack from a given address. +;; This method _does not_ lock the funds, rather, it allows the delegate +;; to issue the stacking lock. +;; The caller specifies: +;; * amount-ustx: the total amount of ustx the delegate may be allowed to lock +;; * until-burn-ht: an optional burn height at which this delegation expires +;; * pox-addr: an optional address to which any rewards *must* be sent +(define-public (delegate-stx (amount-ustx uint) + (delegate-to principal) + (until-burn-ht (optional uint)) + (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) }))) + + (begin + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; pox-addr, if given, must be valid + (match pox-addr + address + (asserts! (check-pox-addr-version (get version address)) + (err ERR_STACKING_INVALID_POX_ADDRESS)) + true) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; add delegation record + (map-set delegation-state + { stacker: tx-sender } + { amount-ustx: amount-ustx, + delegated-to: delegate-to, + until-burn-ht: until-burn-ht, + pox-addr: pox-addr }) + + (ok true))) +``` + +
+ +The arguments are: + +* **Amount**: Denoted in uSTX (1 STX = 1,000,000 uSTX). The maximum amount the pool operator is allowed to lock. +* **Delegate to**: the STX address of the pool operator. +* **Until burn height**: optional BTC block height when the delegation expires. If not set, the delegation permission expires only when explicitly revoked. +* **Pox Address**: optional BTC address that, if specified, the pool operator must use when accepting this delegation. {% endstep %} {% step %} -#### Pool operator stacks tokens +#### Pool operator stacks your tokens -Once you grant permission for the pool operator to delegate, they will take over and call a separate function in the stacking contract to actually stack those delegated tokens. At this point your STX will be locked. +Once you've delegated, the pool operator takes over. They call `delegate-stack-stx` to lock your STX, and then `stack-aggregation-commit-indexed` to commit the pool's total to the reward cycle. Your STX will be locked at this point. -Depending on how the pool operator handles things, they may offer the option to automatically continue to stack your STX for up to 12 continuous cycles. +The pool operator may offer the option to automatically continue stacking for up to 12 continuous cycles. {% endstep %} {% step %} -#### Pool operator allocates rewards +#### Pool operator distributes rewards -Next, the pool operator will track what proportion of rewards you should earn based on the proportion of STX you delegated. If distributing rewards directly in Bitcoin, the pool operator will need to take custody of the Bitcoin and manually distribute it. +The pool operator tracks the proportion of rewards you've earned and distributes them in BTC or STX, depending on their model. Research your pool's reward distribution mechanism to ensure you understand and trust it. +{% endstep %} +{% endstepper %} + +*** + +## Increase Your Delegation + +To increase the amount of STX you've delegated, you need to revoke your current delegation and re-delegate with a higher amount. -This is why it is important to do your research and stack with a pool operator whose reward distribution mechanism you trust. Different operators have different methods to make this process transparent and as trust-minimized as possible. +{% stepper %} +{% step %} +#### Revoke your current delegation + +Call `revoke-delegate-stx` to cancel the existing delegation. See [Revoke and Stop Stacking](#revoke-and-stop-stacking) below for details. {% endstep %} {% step %} -#### Pool operator distributes rewards +#### Delegate with a higher amount -Finally, the pool operator will distribute those rewards to you in either BTC or STX, depending on the model they use. +After the revocation is confirmed, call `delegate-stx` again with the new, higher amount to the same pool operator. + +{% hint style="info" %} +Make sure the revocation is successful before initiating a new delegation. Otherwise, the `delegate-stx` transaction will fail. +{% endhint %} {% endstep %} {% endstepper %} -If you want to learn more about the specific functions and the contract that handles the stacking process, be sure to take a look at the [stacking contract walkthrough](/broken/spaces/GVj1Z9vMuEOMe7oH7Wnq/pages/fc4fa1c229d8cb4deedf49de6dc1de0dc0b1ed72). +*** + +## Revoke and Stop Stacking + +To stop stacking as a delegator, you must cancel the delegation with the pool operator by calling `revoke-delegate-stx`. + +
+ +Function source code + +```clojure +;; Revokes the delegation to the current stacking pool. +;; New in pox-4: Fails if the delegation was already revoked. +;; Returns the last delegation state. +(define-public (revoke-delegate-stx) + (let ((last-delegation-state (get-check-delegation tx-sender))) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (asserts! (is-some last-delegation-state) (err ERR_DELEGATION_ALREADY_REVOKED)) + (asserts! (map-delete delegation-state { stacker: tx-sender }) (err ERR_DELEGATION_ALREADY_REVOKED)) + (ok last-delegation-state))) +``` + +
+ +You can call this through the pool's interface or directly on the [pox-4](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) contract. + +{% hint style="warning" %} +Revoking delegation **does not immediately unlock** your STX. Your tokens remain locked until the end of the last stacking cycle chosen by the pool operator (can be at most 12 cycles). Revoking only prevents the pool from stacking your STX in future cycles. + +Failing to revoke means you continue to allow the pool to stack your STX until the burn block height specified in the `delegate-stx` call. +{% endhint %} + +After revoking, wait for the current lock period to expire. The unlock occurs automatically. + +*** + +## Liquid Stacking + +Liquid stacking is when you delegate your STX to a liquid stacking provider who issues you a new token (e.g., stSTX) that you can use in the ecosystem while your STX are locked. This lets you participate in DeFi protocols even while stacking. + +Links to liquid stacking providers can be found on the [Stacks website](https://www.stacks.co/learn/stacking). -### Liquid Stacking +*** -Liquid stacking is when you delegate your STX tokens to a liquid stacking provider and they issue you a new token such as stSTX that you can then use in the ecosystem. This makes it so that stackers can still use their STX to participate in DeFi protocols and other apps even while their STX are locked. +## Considerations -This works a bit differently than traditional stacking and links to liquid stacking providers for you to research can be found on the [Stacks website](https://www.stacks.co/learn/stacking). +* **Monitor your stacking status**: Use your wallet's interface or the [Hiro Explorer](https://explorer.hiro.so/?chain=mainnet) to track your lock period. +* **Using the API**: Hiro's API offers an endpoint to [Get account STX balance](https://docs.hiro.so/stacks/api/accounts/stx-balances), which includes the `burnchain_unlock_height` representing when your STX unlock. +* **Plan ahead**: Unlocking is bound to cycle timing. Plan your revocation accordingly to minimize delays in accessing your funds. diff --git a/docs/operate/stacking-stx/stop-stacking.md b/docs/operate/stacking-stx/stop-stacking.md deleted file mode 100644 index 4b21fab4d2..0000000000 --- a/docs/operate/stacking-stx/stop-stacking.md +++ /dev/null @@ -1,67 +0,0 @@ -# Stop Stacking - -When you decide it's time to stop stacking your STX tokens, the process depends on whether you are stacking solo or delegating your tokens to a pool operator. This guide explains the steps for both scenarios. - -*** - -## Stopping Solo Stacking - -When stacking solo using the `stack-stx` function, your STX is locked for a fixed period (the lock period) defined when you initiated stacking or when you extended the lock period. **No additional action is required to stop stacking**, you simply have to wait until the lock period expires. - -{% hint style="info" %} -In solo stacking, both the `stack-stx` and `stack-extend` functions emits an event that includes the `unlock-burn-height` field. This is the burn block height at which your tokens will be automatically unlocked. -{% endhint %} - -## Stopping Pooled Stacking - -If you're stacking with a pool (where you delegate your STX via the `delegate-stx` function), the process to stop stacking requires one extra step before your STX is eventually unlocked. - -{% stepper %} -{% step %} -#### Revoke Delegation - -Before your STX can be unlocked, you must cancel the delegation with the pool operator. This is done by calling the `revoke-delegate-stx` function through the pool's interface, or within the [pox-4](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) contract. - -
- -Function source code - -```clojure -;; Revokes the delegation to the current stacking pool. -;; New in pox-4: Fails if the delegation was already revoked. -;; Returns the last delegation state. -(define-public (revoke-delegate-stx) - (let ((last-delegation-state (get-check-delegation tx-sender))) - ;; must be called directly by the tx-sender or by an allowed contract-caller - (asserts! (check-caller-allowed) - (err ERR_STACKING_PERMISSION_DENIED)) - (asserts! (is-some last-delegation-state) (err ERR_DELEGATION_ALREADY_REVOKED)) - (asserts! (map-delete delegation-state { stacker: tx-sender }) (err ERR_DELEGATION_ALREADY_REVOKED)) - (ok last-delegation-state))) -``` - -
- -Calling `revoke-delegate-stx` cancels your STX delegation, revoking the pool operator's access to further lock/stack your funds. Even after revoking the delegation, your STX will remain locked until the end of the last stacking cycle chosen by the pool (can be at most 12 cycles in the future). - -{% hint style="warning" %} -Failing to revoke your delegation will mean that you continue to allow the pool to stack your STX until the reach of the burn block height mentioned in the delegate function (`delegate-stx`). Ensure that you have successfully called `revoke-delegate-stx` if you want to stop stacking sooner. -{% endhint %} -{% endstep %} - -{% step %} -#### Wait for Funds to Unlock - -After revoking your delegation, your STX tokens will still remain locked until the last stacking cycle chosen by the pool operator completes. The unlock occurs automatically at the predefined unlock burn height for that cycle. - -{% hint style="info" %} -Even in pooled stacking, the unlocking mechanism follows the same blockchain timing as solo stacking. Revoking delegation only stops future stacking actions, it does not immediately unlock your tokens. -{% endhint %} -{% endstep %} -{% endstepper %} - -## Considerations - -* Monitor Your Stacking Status: Use your wallet's interface or the [Hiro Explorer](https://explorer.hiro.so/?chain=mainnet) to track the status of your lock period and confirm when your tokens are available. -* Using the API: Hiro's API offers an endpoint to [Get account STX balance](https://docs.hiro.so/stacks/api/accounts/stx-balances), which contains the `burnchain_unlock_height` height, representing the burn block height where your STX unlocks. -* Plan Ahead: Since the unlocking is bound to cycle's timing, plan your stacking period or revocation accordingly to minimize delays in accessing your funds. From 2a77d7e9bfdde116bd4d81b2d4151b7f8cc50eaf Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 23 Mar 2026 11:38:52 +0100 Subject: [PATCH 2/5] Fixes --- docs/operate/run-a-signer/README.md | 22 +++++++++---------- docs/operate/stacking-stx/README.md | 2 ++ .../stacking-stx/generate-signer-signature.md | 18 +++++++-------- docs/operate/stacking-stx/operate-a-pool.md | 12 +++++----- docs/operate/stacking-stx/solo-stacking.md | 8 +++---- .../operate/stacking-stx/stack-with-a-pool.md | 8 +++---- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/docs/operate/run-a-signer/README.md b/docs/operate/run-a-signer/README.md index 83ba0559c8..5ca4710cc8 100644 --- a/docs/operate/run-a-signer/README.md +++ b/docs/operate/run-a-signer/README.md @@ -4,7 +4,7 @@ ### How to Use This Guide -This guide is a step-by-step walkthrough for setting up and running a signer. It covers only the signer infrastructure — the signer software and the Stacks node it connects to. +This guide is a step-by-step walkthrough for setting up and running a signer. It covers only the signer infrastructure: the signer software and the Stacks node it connects to. If you are not familiar with the concept of signing, be sure to check out the [Stackers and Signing concept guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing). @@ -21,7 +21,7 @@ This doc provides instructions to set up both using either Docker or the release {% stepper %} {% step %} -#### Signer Checklist — Pre-Launch Setup +#### Signer Checklist: Pre-Launch Setup Quick reference of major setup steps prior to launching a signer. @@ -30,14 +30,14 @@ Quick reference of major setup steps prior to launching a signer. {% endstep %} {% step %} -#### Signer Checklist — Preflight Setup +#### Signer Checklist: Preflight Setup * Generate a new private key using stacks-cli (see Preflight Setup). * Save the generated account information securely. {% endstep %} {% step %} -#### Signer Checklist — Configuration Setup +#### Signer Checklist: Configuration Setup * Create a `signer-config.toml` file with necessary configurations: * node\_host @@ -50,7 +50,7 @@ Quick reference of major setup steps prior to launching a signer. {% endstep %} {% step %} -#### Signer Checklist — Running the Signer +#### Signer Checklist: Running the Signer * Decide whether to run the signer using Docker (recommended) or as a binary. * If using Docker: @@ -62,14 +62,14 @@ Quick reference of major setup steps prior to launching a signer. {% endstep %} {% step %} -#### Signer Checklist — Verify Signer Operation +#### Signer Checklist: Verify Signer Operation * Check that the signer is listening on its configured endpoint. * Confirm that there are no errors and that the system is ready for connections. {% endstep %} {% step %} -#### Signer Checklist — Setting Up the Stacks Node +#### Signer Checklist: Setting Up the Stacks Node * Create a `node-config.toml`, include: * connection\_options.sauth\_token @@ -78,7 +78,7 @@ Quick reference of major setup steps prior to launching a signer. {% endstep %} {% step %} -#### Signer Checklist — Verify Stacks Node Operation +#### Signer Checklist: Verify Stacks Node Operation * Check Stacks node logs for successful connection to the signer. * Confirm the node is syncing Bitcoin headers properly. @@ -168,7 +168,7 @@ CMD ["stacks-signer", "run", "--config", "/config.toml"] ### Running the Signer as a Binary -Download the pre-built binaries from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases), unzip the archive for your architecture — inside is a `stacks-signer` binary. +Download the pre-built binaries from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases), unzip the archive for your architecture. Inside is a `stacks-signer` binary. Run the signer: @@ -225,7 +225,7 @@ Guides: ## Set Up Your Stacks Node -Start the Stacks node after the signer is running — the node will not run unless it can send events to the signer. +Start the Stacks node after the signer is running. The node will not run unless it can send events to the signer. ### Stacks Node Configuration @@ -292,7 +292,7 @@ EXPOSE 20443 CMD ["stacks-node", "start", "--config", "/config.toml"] ``` -If you get connection refused errors, you may need to point `events_observer.endpoint` to the Docker signer container. If using default Docker bridge mode, `localhost` inside the container is not the host — point the endpoint to the Docker host or the signer container hostname accordingly. +If you get connection refused errors, you may need to point `events_observer.endpoint` to the Docker signer container. If using default Docker bridge mode, `localhost` inside the container is not the host. Point the endpoint to the Docker host or the signer container hostname accordingly. ### Run a Stacks Node with a Binary diff --git a/docs/operate/stacking-stx/README.md b/docs/operate/stacking-stx/README.md index d540941180..048d7760f7 100644 --- a/docs/operate/stacking-stx/README.md +++ b/docs/operate/stacking-stx/README.md @@ -50,6 +50,8 @@ How to rotate your signer key, Bitcoin reward address, and pool operator key. In {% endstep %} {% endstepper %} +If you do not meet the minimum amount of STX to solo stack, you can [delegate your STX to a pool operator](stack-with-a-pool.md) and have them stack on your behalf. This is the most common stacking scenario. + {% hint style="info" %} The minimum stacking threshold is dynamic and can be found at the [pox endpoint](https://api.mainnet.hiro.so/v2/pox) under `min_threshold_ustx` (1 STX = 1,000,000 uSTX). {% endhint %} diff --git a/docs/operate/stacking-stx/generate-signer-signature.md b/docs/operate/stacking-stx/generate-signer-signature.md index f00e5131c8..1cddede33a 100644 --- a/docs/operate/stacking-stx/generate-signer-signature.md +++ b/docs/operate/stacking-stx/generate-signer-signature.md @@ -8,7 +8,7 @@ Both [solo stacking](solo-stacking.md) and delegated stacking ([Stack with a Poo ## Overview -Signer signatures are created using a particular 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. +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. {% hint style="info" %} 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). @@ -40,7 +40,7 @@ The maximum amount of uSTX (1 STX = 1,000,000 uSTX) that can be locked in the tr {% step %} #### auth-id -A random integer that prevents re-use of a particular signature, similar to how nonces are used with transactions. Must be less than 14 characters. +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. {% endstep %} {% endstepper %} @@ -49,11 +49,11 @@ A random integer that prevents re-use of a particular signature, similar to how The signer signature's message hash is created using the following data: * **`method`**: the stacking function that is allowed to use this signature. Valid options: - * `stack-stx` — for solo stacking - * `stack-extend` — for extending a solo stacking lock - * `stack-increase` — for increasing a solo stacking position - * `agg-commit` — for `stack-aggregation-commit-indexed` (pool operators) - * `agg-increase` — for `stack-aggregation-increase` (pool operators) + * `stack-stx`: for solo stacking + * `stack-extend`: for extending a solo stacking lock + * `stack-increase`: for increasing a solo stacking position + * `agg-commit`: for `stack-aggregation-commit-indexed` (pool operators) + * `agg-increase`: for `stack-aggregation-increase` (pool operators) * **`max-amount`**: the maximum uSTX allowed (described above) * **`auth-id`**: the random integer (described above) * **`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. @@ -134,7 +134,7 @@ More information and code samples can be found on [Hiro's Nakamoto docs](https:/ ### Using Degen Lab's stacking.tools -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. +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. ### Using Leather Earn @@ -187,7 +187,7 @@ If you used [@stacks/cli](https://docs.hiro.so/get-started/command-line-interfac * Import your mnemonic into Leather, XVerse, or another Stacks wallet 2. Open an app with stacking signature functionality (e.g., Leather Earn) 3. Connect your wallet (sign in) -4. Enter your PoX address and submit — the app will prompt you to sign +4. Enter your PoX address and submit. The app will prompt you to sign 5. Confirm the signature (if using a Ledger, confirm on the device) 6. The app displays your signer key and signature 7. Use the signer key and signature in your stacking transaction diff --git a/docs/operate/stacking-stx/operate-a-pool.md b/docs/operate/stacking-stx/operate-a-pool.md index be8c0fdd23..a1e4621d51 100644 --- a/docs/operate/stacking-stx/operate-a-pool.md +++ b/docs/operate/stacking-stx/operate-a-pool.md @@ -1,6 +1,6 @@ # Operate a Pool -This guide covers how to operate a stacking pool — accepting delegated STX from stackers, committing them to reward cycles, and managing the pool's stacked position. +This guide covers how to operate a stacking pool: accepting delegated STX from stackers, committing them to reward cycles, and managing the pool's stacked position. {% hint style="info" %} This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. @@ -13,14 +13,14 @@ If you want to delegate your STX to a pool instead, see the [Stack with a Pool]( ## Prerequisites 1. **A running signer** or a signer you are collaborating with. See the [Run a Signer](../run-a-signer/) guide. -2. **A pool operator wallet** — a separate STX address used to manage delegations and make stacking transactions. This is different from your signer key. See [Key and Address Rotation](key-and-address-rotation.md) for why this separation matters. -3. **Funding** — your pool operator wallet needs enough STX to cover transaction fees. +2. **A pool operator wallet**: a separate STX address used to manage delegations and make stacking transactions. This is different from your signer key. See [Key and Address Rotation](key-and-address-rotation.md) for why this separation matters. +3. **Funding**: your pool operator wallet needs enough STX to cover transaction fees. {% hint style="info" %} As a pool operator, you should have two separate accounts: -* **Pool operator account** — used for all stacking operations (`delegate-stack-stx`, `stack-aggregation-commit-indexed`, etc.). -* **Signer account** — the key used to configure your signer. The signer public key is what you pass into the aggregation commit function. +* **Pool operator account**: used for all stacking operations (`delegate-stack-stx`, `stack-aggregation-commit-indexed`, etc.). +* **Signer account**: the key used to configure your signer. The signer public key is what you pass into the aggregation commit function. This separation is recommended because you can rotate a signer key without disrupting delegations, but you cannot rotate a pool operator key without delegators needing to un-stack and re-delegate. See [Key and Address Rotation](key-and-address-rotation.md) for more details. {% endhint %} @@ -476,4 +476,4 @@ The pool operator calls `stack-aggregation-commit-indexed` to commit all delegat {% endstep %} {% endstepper %} -All steps must be completed before the prepare phase of the target reward cycle. During the prepare phase, DKG occurs and the signer is automatically registered — no manual action needed beyond monitoring. +All steps must be completed before the prepare phase of the target reward cycle. During the prepare phase, DKG occurs and the signer is automatically registered. No manual action is needed beyond monitoring. diff --git a/docs/operate/stacking-stx/solo-stacking.md b/docs/operate/stacking-stx/solo-stacking.md index e272713035..b5a430f635 100644 --- a/docs/operate/stacking-stx/solo-stacking.md +++ b/docs/operate/stacking-stx/solo-stacking.md @@ -20,7 +20,7 @@ Before you begin, make sure you have: 1. **A running signer** or a signer you are collaborating with. See the [Run a Signer](../run-a-signer/) guide. 2. **A signer key signature** for the stacking transaction you want to make. See [Generate a Signer Signature](generate-signer-signature.md). -3. **Sufficient STX** — at or above the minimum stacking threshold. +3. **Sufficient STX**: at or above the minimum stacking threshold. {% hint style="info" %} There are several ways to make stacking transactions. This guide covers using [Leather Earn](https://earn.leather.io/), which is the simplest option. You can also call the stacking functions directly from the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) in the explorer, or use the [@stacks/stacking](https://github.com/stx-labs/stacks.js/tree/main/packages/stacking) NPM package. @@ -366,7 +366,7 @@ If you're already stacking, the home page will show a link to "view stacking det ## Stop Stacking -When stacking solo, your STX is locked for a fixed period defined when you initiated stacking or extended the lock period. **No additional action is required to stop stacking** — your tokens unlock automatically when the lock period expires. +When stacking solo, your STX is locked for a fixed period defined when you initiated stacking or extended the lock period. **No additional action is required to stop stacking.** Your tokens unlock automatically when the lock period expires. {% hint style="info" %} Both the `stack-stx` and `stack-extend` functions emit an event that includes the `unlock-burn-height` field. This is the burn block height at which your tokens will be automatically unlocked. @@ -383,6 +383,6 @@ To avoid your position being extended, simply do not call `stack-extend` before ## How Signer Registration Works -In the prepare phase before the next stacking cycle (last 100 blocks), the signer set is selected based on the amount of STX stacked. For solo stackers, the only transaction needed is `stack-stx` — once it is confirmed during the first 2000 blocks of the current reward cycle, your signer will be registered in the next cycle's signer set. +In the prepare phase before the next stacking cycle (last 100 blocks), the signer set is selected based on the amount of STX stacked. For solo stackers, the only transaction needed is `stack-stx`. Once it is confirmed during the first 2000 blocks of the current reward cycle, your signer will be registered in the next cycle's signer set. -It is critical that your signer is running during the prepare phase. This is when DKG occurs, and the signer automatically participates — no manual action required beyond monitoring. +It is critical that your signer is running during the prepare phase. This is when DKG occurs, and the signer automatically participates. No manual action is required beyond monitoring. diff --git a/docs/operate/stacking-stx/stack-with-a-pool.md b/docs/operate/stacking-stx/stack-with-a-pool.md index d7f8c98ba6..46f7127867 100644 --- a/docs/operate/stacking-stx/stack-with-a-pool.md +++ b/docs/operate/stacking-stx/stack-with-a-pool.md @@ -1,12 +1,12 @@ # Stack with a Pool -This guide covers delegated stacking from the **delegator** perspective — how to delegate your STX to a pool, increase your delegation, revoke it, and stop stacking. +This guide covers delegated stacking from the **delegator** perspective: how to delegate your STX to a pool, increase your delegation, revoke it, and stop stacking. {% hint style="info" %} This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. {% endhint %} -Delegating is the most common stacking scenario. It applies when you do not meet the minimum STX threshold to solo stack and want a pool operator to stack on your behalf. This is a **non-custodial** delegation — your STX do not leave your wallet. +Delegating is the most common stacking scenario. It applies when you do not meet the minimum STX threshold to solo stack and want a pool operator to stack on your behalf. This is a **non-custodial** delegation. Your STX do not leave your wallet. The minimum stacking amount is dynamic and can be found at the [pox endpoint](https://api.mainnet.hiro.so/v2/pox) under `min_threshold_ustx` (1 STX = 1,000,000 uSTX). @@ -24,13 +24,13 @@ If you want to operate a pool instead, see the [Operate a Pool](operate-a-pool.m {% step %} #### Find a pool -The Stacks website has a [page on stacking](https://www.stacks.co/learn/stacking) that links to several pool operators. Do your research — operators differ in reward distribution, fees, and trust models. +The Stacks website has a [page on stacking](https://www.stacks.co/learn/stacking) that links to several pool operators. Do your research, as operators differ in reward distribution, fees, and trust models. {% endstep %} {% step %} #### Call `delegate-stx` -Use your pool operator's UI or call the function directly to delegate. This does not lock your STX — it gives the pool operator **permission** to stack on your behalf. +Use your pool operator's UI or call the function directly to delegate. This does not lock your STX. It gives the pool operator **permission** to stack on your behalf.
From ad14147ad15208570f92340d03cca41632fab804 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 23 Mar 2026 11:41:30 +0100 Subject: [PATCH 3/5] Fix a sentence --- docs/operate/run-a-signer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operate/run-a-signer/README.md b/docs/operate/run-a-signer/README.md index 5ca4710cc8..bbf4345dfc 100644 --- a/docs/operate/run-a-signer/README.md +++ b/docs/operate/run-a-signer/README.md @@ -168,7 +168,7 @@ CMD ["stacks-signer", "run", "--config", "/config.toml"] ### Running the Signer as a Binary -Download the pre-built binaries from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases), unzip the archive for your architecture. Inside is a `stacks-signer` binary. +Download the pre-built binaries from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases), unzip the archive for your architecture. It includes the `stacks-signer` binary. Run the signer: From cf33771378d0173e05d6ed66c448a90c3198eb9b Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 23 Mar 2026 11:59:04 +0100 Subject: [PATCH 4/5] Add key link --- docs/operate/run-a-signer/README.md | 4 +++- docs/operate/run-a-signer/signer-quickstart.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/operate/run-a-signer/README.md b/docs/operate/run-a-signer/README.md index bbf4345dfc..6433394316 100644 --- a/docs/operate/run-a-signer/README.md +++ b/docs/operate/run-a-signer/README.md @@ -325,4 +325,6 @@ You may see many logs while syncing; refer to How to Read the Signer Logs if con ## Next Steps: Stacking -Once your signer and Stacks node are running and verified, the next step is to stack STX to register your signer for a reward cycle. See the [Stacking STX](../stacking-stx/) guide for complete instructions on solo stacking, delegated stacking, generating signer signatures, and managing your keys. +Once your signer and Stacks node are running and verified, the next step is to stack STX to register your signer for a reward cycle. See the [Stacking STX](../stacking-stx/) guide for complete instructions on solo stacking, delegated stacking, and managing your keys. + +You will need to [generate a signer signature](../stacking-stx/generate-signer-signature.md) before making any stacking transaction. diff --git a/docs/operate/run-a-signer/signer-quickstart.md b/docs/operate/run-a-signer/signer-quickstart.md index 294e00d24d..c466107af6 100644 --- a/docs/operate/run-a-signer/signer-quickstart.md +++ b/docs/operate/run-a-signer/signer-quickstart.md @@ -430,6 +430,8 @@ If you would like to learn more about monitoring your signer and its correspondi {% step %} #### Next Steps: Stacking -Once your signer and Stacks node are running and verified, the next step is to stack STX to register your signer for a reward cycle. See the [Stacking STX](../stacking-stx/) guide for complete instructions on solo stacking, delegated stacking, generating signer signatures, and managing your keys. +Once your signer and Stacks node are running and verified, the next step is to stack STX to register your signer for a reward cycle. See the [Stacking STX](../stacking-stx/) guide for complete instructions on solo stacking, delegated stacking, and managing your keys. + +You will need to [generate a signer signature](../stacking-stx/generate-signer-signature.md) before making any stacking transaction. {% endstep %} {% endstepper %} From 9de99224c8449fd45f648e0a65890c97e08b931f Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 24 Mar 2026 13:16:43 +0100 Subject: [PATCH 5/5] Fixes after Jesse review --- docs/operate/run-a-signer/README.md | 16 ++++++++-------- .../best-practices-to-run-a-signer.md | 7 +++++++ docs/operate/run-a-signer/signer-quickstart.md | 6 +++--- docs/operate/stacking-stx/README.md | 2 +- docs/operate/stacking-stx/operate-a-pool.md | 2 +- docs/operate/stacking-stx/solo-stacking.md | 2 +- docs/operate/stacking-stx/stack-with-a-pool.md | 2 +- 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/docs/operate/run-a-signer/README.md b/docs/operate/run-a-signer/README.md index 6433394316..90be6ac026 100644 --- a/docs/operate/run-a-signer/README.md +++ b/docs/operate/run-a-signer/README.md @@ -6,18 +6,18 @@ This guide is a step-by-step walkthrough for setting up and running a signer. It covers only the signer infrastructure: the signer software and the Stacks node it connects to. -If you are not familiar with the concept of signing, be sure to check out the [Stackers and Signing concept guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing). +If you are not familiar with the concept of signing, be sure to check out the [Stackers and Signing concept guide](../../learn/block-production/signing.md). ### Background and High-Level Process To run a signer you'll run a signer and a Stacks node side-by-side. Specifically, run a follower node. The signer monitors events from the Stacks node and uses the generated account (see Preflight Setup) to sign incoming Stacks blocks sent from the Stacks node. -This doc provides instructions to set up both using either Docker or the release binaries available in the [stacks core releases](https://github.com/stacks-network/stacks-core/releases) repository, and how to configure them so the signer and Stacks node communicate correctly. +This doc provides instructions to set up both using either Docker or the release binaries available in the [stacks core releases](https://github.com/stacks-network/stacks-core/releases/latest) repository, and how to configure them so the signer and Stacks node communicate correctly. ### Knowledge Prerequisites * Docker and basic knowledge of pulling and running images -* Basic knowledge of [Stacks accounts](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/network-fundamentals/wallets-and-accounts) +* Basic knowledge of [Stacks accounts](../../learn/network-fundamentals/wallets-and-accounts.md) {% stepper %} {% step %} @@ -114,13 +114,13 @@ Signers are intended to work with a local node. The node<->signer connection is ## Create a Configuration File -Create a file named `signer-config.toml`. Populate it with the example signer config file contents from the [Sample Configuration Files](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/node-operations/signer-configuration) page. Each field is described on that page. +Create a file named `signer-config.toml`. Populate it with the example signer config file contents from the [Sample Configuration Files](../../reference/node-operations/signer-configuration.md) page. Each field is described on that page. *** ## Running the Signer -Two options: Docker (recommended) or binary. Binaries are available on the [Stacks Core releases page](https://github.com/stacks-network/stacks-core/releases). +Two options: Docker (recommended) or binary. Binaries are available on the [Stacks Core releases page](https://github.com/stacks-network/stacks-core/releases/latest). ### Running the Signer with Docker @@ -168,7 +168,7 @@ CMD ["stacks-signer", "run", "--config", "/config.toml"] ### Running the Signer as a Binary -Download the pre-built binaries from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases), unzip the archive for your architecture. It includes the `stacks-signer` binary. +Download the pre-built binaries from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases/latest), unzip the archive for your architecture. It includes the `stacks-signer` binary. Run the signer: @@ -229,7 +229,7 @@ Start the Stacks node after the signer is running. The node will not run unless ### Stacks Node Configuration -Create `node-config.toml`. See the [Sample Configuration Files](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/node-operations/signer-configuration) page for the full contents. +Create `node-config.toml`. See the [Sample Configuration Files](../../reference/node-operations/signer-configuration.md) page for the full contents. Important fields to change: @@ -296,7 +296,7 @@ If you get connection refused errors, you may need to point `events_observer.end ### Run a Stacks Node with a Binary -Download the pre-built `stacks-node` binary from the [Stacks Core releases](https://github.com/stacks-network/stacks-core/releases). +Download the pre-built `stacks-node` binary from the [Stacks Core releases](https://github.com/stacks-network/stacks-core/releases/latest). Start the node: diff --git a/docs/operate/run-a-signer/best-practices-to-run-a-signer.md b/docs/operate/run-a-signer/best-practices-to-run-a-signer.md index 8ae0eccfef..8fc22ca3da 100644 --- a/docs/operate/run-a-signer/best-practices-to-run-a-signer.md +++ b/docs/operate/run-a-signer/best-practices-to-run-a-signer.md @@ -193,6 +193,13 @@ sudo systemctl status stacks-signer.service sudo systemctl status stacks-node.service ``` +View logs with: + +```bash +journalctl -xefu stacks-signer +journalctl -xefu stacks-node +``` + ### Backup signer keys in cold-storage * Keep an offline, secure backup of all Signer private keys (e.g., hardware security modules or encrypted storage devices). diff --git a/docs/operate/run-a-signer/signer-quickstart.md b/docs/operate/run-a-signer/signer-quickstart.md index c466107af6..0a1f466def 100644 --- a/docs/operate/run-a-signer/signer-quickstart.md +++ b/docs/operate/run-a-signer/signer-quickstart.md @@ -16,7 +16,7 @@ If you want to get up and running as an active signer as quickly as possible, here is a list of the commands you need to run and actions to take. -If you are not familiar with how signing works yet, be sure to check out the [Signing concept guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing). +If you are not familiar with how signing works yet, be sure to check out the [Signing concept guide](../../learn/block-production/signing.md). {% hint style="danger" %} The CLI examples below may show outdated release versions. For the latest releases, always refer to the links above in the top info block. @@ -88,7 +88,7 @@ From this file, you'll need the `privateKey` value. **Download the stacks-signer binary** -Official binaries are available from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases). Each release includes pre-built binaries. Download the [latest signer release ZIP file](https://github.com/stacks-network/stacks-core/releases/latest) for your server’s architecture and decompress it. Inside of that folder is a `stacks-signer` binary. +Official binaries are available from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases/latest). Each release includes pre-built binaries. Download the [latest signer release ZIP file](https://github.com/stacks-network/stacks-core/releases/latest) for your server’s architecture and decompress it. Inside of that folder is a `stacks-signer` binary. Assuming a `Linux x64 glibc` machine, the commands to download and uncompress the signer binary look like this: @@ -201,7 +201,7 @@ We have created guides for running both a [full Bitcoin node](../run-a-node/run- **Download the stacks-node binary** -Official binaries are available from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases). Each release includes pre-built binaries. Download the [latest node release ZIP file](https://github.com/stacks-network/stacks-core/releases/latest) for your server’s architecture and decompress it. Inside of that folder is a `stacks-node` binary. +Official binaries are available from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases/latest). Each release includes pre-built binaries. Download the [latest node release ZIP file](https://github.com/stacks-network/stacks-core/releases/latest) for your server’s architecture and decompress it. Inside of that folder is a `stacks-node` binary. Assuming a `Linux x64 glibc` machine, the commands to download and uncompress the node binary look like this: diff --git a/docs/operate/stacking-stx/README.md b/docs/operate/stacking-stx/README.md index 048d7760f7..0406d96c15 100644 --- a/docs/operate/stacking-stx/README.md +++ b/docs/operate/stacking-stx/README.md @@ -1,6 +1,6 @@ # Stacking STX -Stacking is the process of locking STX tokens to support the network's consensus and earn BTC rewards. If you aren't familiar with how stacking works, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) and [Stackers and Signing](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing) concept guides first. +Stacking is the process of locking STX tokens to support the network's consensus and earn BTC rewards. If you aren't familiar with how stacking works, read the [Stacking](../../learn/block-production/stacking.md) and [Stackers and Signing](../../learn/block-production/signing.md) concept guides first. Stacking utilizes the `pox-4` contract. You can view it on the [Explorer](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) and review the detailed [stacking contract walkthrough](/broken/spaces/GVj1Z9vMuEOMe7oH7Wnq/pages/fc4fa1c229d8cb4deedf49de6dc1de0dc0b1ed72) to understand what each function does. diff --git a/docs/operate/stacking-stx/operate-a-pool.md b/docs/operate/stacking-stx/operate-a-pool.md index a1e4621d51..b2de5614a6 100644 --- a/docs/operate/stacking-stx/operate-a-pool.md +++ b/docs/operate/stacking-stx/operate-a-pool.md @@ -3,7 +3,7 @@ This guide covers how to operate a stacking pool: accepting delegated STX from stackers, committing them to reward cycles, and managing the pool's stacked position. {% hint style="info" %} -This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. +This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](../../learn/block-production/stacking.md) concept guide first. {% endhint %} If you want to delegate your STX to a pool instead, see the [Stack with a Pool](stack-with-a-pool.md) guide. diff --git a/docs/operate/stacking-stx/solo-stacking.md b/docs/operate/stacking-stx/solo-stacking.md index b5a430f635..810f7fbd88 100644 --- a/docs/operate/stacking-stx/solo-stacking.md +++ b/docs/operate/stacking-stx/solo-stacking.md @@ -3,7 +3,7 @@ This guide covers everything you need to stack independently as a solo stacker: starting, extending, increasing, and stopping your stacking position. {% hint style="info" %} -This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. +This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](../../learn/block-production/stacking.md) concept guide first. {% endhint %} Solo stacking requires meeting the minimum STX threshold and either running a signer or collaborating with one. The minimum amount is dynamic and can be found at the [pox endpoint](https://api.mainnet.hiro.so/v2/pox) under `min_threshold_ustx` (1 STX = 1,000,000 uSTX). diff --git a/docs/operate/stacking-stx/stack-with-a-pool.md b/docs/operate/stacking-stx/stack-with-a-pool.md index 46f7127867..177c169966 100644 --- a/docs/operate/stacking-stx/stack-with-a-pool.md +++ b/docs/operate/stacking-stx/stack-with-a-pool.md @@ -3,7 +3,7 @@ This guide covers delegated stacking from the **delegator** perspective: how to delegate your STX to a pool, increase your delegation, revoke it, and stop stacking. {% hint style="info" %} -This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide first. +This guide assumes you are familiar with stacking at a conceptual level. If not, read the [Stacking](../../learn/block-production/stacking.md) concept guide first. {% endhint %} Delegating is the most common stacking scenario. It applies when you do not meet the minimum STX threshold to solo stack and want a pool operator to stack on your behalf. This is a **non-custodial** delegation. Your STX do not leave your wallet.