What is this

Draft documentation for sBTC Signer specifically for the Nakamoto release

The readers of this are assumed to be anyone who is looking to run a sBTC Signer with minimal custom configuration.

Disclaimer

Development is ongoing and changes to the Signer configuration should be expected. This document will be updated every sprint to ensure its accuracy.

Signing functionality will only become active and relevant on the Stacks mainnet at the release of the Nakamoto upgrade, currently expected in early 2024. Prospective signers and Stacking providers can contact signers@sbtc.tech for support and additional information.

Prerequisites

Rust - To install.

Accessible Stacks node - A Stacks node running the stackerDB instance which is used for signer communication and transaction monitoring and broadcasting.

Accessible Bitcoin node - A Bitcoin node used for transaction monitoring and broadcasting.

A Stacks Private Key - Identify your address as a Signer

Installing

Building from Source

If you wish to compile the default binary from source, follow the steps outlined below. Otherwise, download the binary directly (below)

  1. First, clone the Stacks sBTC mono repository:
git clone git@github.com:stacks-network/stacks-blockchain.git
  1. Next, navigate to the stacks-signer directory:
cd stacks-blockchain/stacks-signer
  1. Checkout the appropriate release branch you wish to use if you are not using the default main branch
git checkout master
  1. Compile the signer binary: Note the binary path defaults to target/release/stacks-signer.
cargo build --release

Downloading the Binary

  1. First, download the precompiled default TODO:NEED:LINK.

  2. Untar the file

tar -xvf signer_binary.tar
  1. Check Extracted Files: After running the untar command, the contents of the tar file should be extracted to the current directory. You should see the signer binary (stacks-signer) and the configuration file (signer.toml) listed among the extracted files.

  2. Next, install the signer.

cargo install --path stacks-signer

Configuration

The signer takes a TOML config file with the following expected properties

KeyRequiredDescription
signer_private_keytrueStacks private key of the signer, used for signing sBTC transactions.
stacks_node_rpc_urltrueStacks node RPC URL that points to a node running the stackerDB instance which is used for signer communication and transaction monitoring and broadcasting.
bitcoin_node_rpc_urltrueBitcoin node RPC URL used for transaction monitoring and broadcasting.
stackerdb_event_endpointtrueRPC endpoint for receiving events from StackerDB
stackerdb_contract_idfalseStackerDB qualified contract ID for Signer communication. Defaults to "ST11Z60137Y96MF89K1KKRTA3CR6B25WY1Y931668.signers-stackerdb".
networkfalseOne of ['Signet', 'Regtest', 'Testnet', 'Bitcoin']. Defaults to Testnet
signer_api_server_urlfalseUrl at which to host the signer api server for transaction monitoring. Defaults to "http://localhost:3000".
auto_deny_blockfalseNumber of blocks before signing deadline to auto deny a transaction waiting for manual review. Defaults to 10.
auto_approve_max_amountfalseMaximum amount of a transactions that will be auto approved
auto_deny_addresses_btcfalseList of bitcoin addresses that trigger an auto deny
auto_deny_addresses_stxfalseList of stx addresses that trigger an auto deny
auto_deny_deadline_blocksfalseThe number of blocks before deadline at which point the transaction will be auto denied. Default is 10 blocks.

Example TOML file

# config.toml

# Mandatory fields

# Note: Replace 'MY_PRIVATE_KEY' with the actual private key value
signer_private_key = "MY_PRIVATE_KEY"
stacks_node_rpc_url = "http://localhost:9776"
bitcoin_node_rpc_url = "http://localhost:9777"
revealer_rpc_url = "http://locahost:9778"

# Optional fields
network = "Signet"
auto_approve_max_amount = 500000
auto_deny_addresses_btc = [
	"BTC_ADDRESS_1",
	"BTC_ADDRESS_2"
]
auto_deny_addresses_stx = [
	"STX_ADDRESS"
]
auto_deny_deadline_blocks = 120

Running the binary

After installing and creating a config file to run the binary

stacks-signer --config conf/signer.toml

Monitor Transactions

The signer binary operates a web server/client and it can be navigated to by default at http://localhost:3000/ (unless otherwise specified from config). Here you can see pending transactions and manually review and sign transactions that cannot be automatically signed on your behalf. Note that manual review is triggered based on the options you have set in your configuration file.

Custom Signer Implementation

If you wish to have more fine-grained control of the Signer binary and its transaction signing logic, you may wish to take advantage of the [Signer SDK](TODO: LINK TO GITHUB REPO).

  1. Set Up a New Rust Project

To add a Signer library to your Rust project and create a main function that utilizes it, follow these step-by-step instructions:

If you don't have an existing Rust project, create one using Cargo, Rust's package manager and build tool:

cargo new my_signer
cd my_signer

Replace my_signer with your desired project name.

  1. Add Signer Library to the Cargo.toml File

Open the Cargo.toml file in your project directory and add the Signer library as a dependency under the [dependencies] section.

[dependencies]
signer = "1.0.0"

Specify the appropriate version that you wish to use. Make sure to check the latest version available on crates.io.

  1. Import the Signer Library in Your Rust Code

In your main.rs file (located in the src folder by default), import the Signer library at the beginning of the file:

#![allow(unused)]
fn main() {
use signer::Signer;
}
  1. Create a Main Function

Add the main function to your main.rs file. This is where you'll utilize the Signer library to perform the required actions:

fn main() {
    // Initialize the signer with a private key
    let signer = Signer::new("your_private_key"); // Replace with the actual private key
    // Must serve web client to utilize manual review
    let _ = signer.serve_http("0.0.0.0", 3000);
    while let Ok(transaction) = signer.retrieve_pending_transaction() {
        // Trigger manual review for a specific address
        if transaction.recipient.to_string() == "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC" {
            // Manually approve or deny a transaction
            let _ = signer.trigger_manual_review(transaction);
        } else if transaction.amount > 3418260000 {
            // deny transactions with an amount greater than 1 million USD
            let _ = signer.deny(transaction);
        } else {
            // Approve anything else
            let _ = signer.approve(transaction);
        }
    }
}
  1. Build and Run Your Signer

Now that you've added the Signer library and created the main function, you can build and run your custom signer using Cargo:

cargo build
cargo run