diff --git a/Cargo.lock b/Cargo.lock index 691a93c40cd..fc8f337bd29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1253,7 +1253,7 @@ dependencies = [ "thiserror", "tracing", "tracing-subscriber", - "wsts 2.0.0", + "wsts", ] [[package]] @@ -1277,7 +1277,7 @@ dependencies = [ "tracing", "tracing-subscriber", "ureq", - "wsts 2.0.0", + "wsts", ] [[package]] @@ -1537,16 +1537,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", - "serde", -] - [[package]] name = "hashbrown" version = "0.14.0" @@ -3476,10 +3466,8 @@ dependencies = [ "async-std", "backtrace", "base64 0.12.3", - "bincode", "chrono", "clarity", - "hashbrown 0.14.0", "http-types", "lazy_static", "libc", @@ -3487,7 +3475,6 @@ dependencies = [ "p256k1", "pico-args", "rand 0.7.3", - "rand_core 0.6.4", "regex", "reqwest", "ring", @@ -3503,7 +3490,6 @@ dependencies = [ "tokio", "toml 0.5.11", "warp", - "wsts 1.2.0", ] [[package]] @@ -3531,7 +3517,7 @@ dependencies = [ "stacks-common", "thiserror", "toml 0.5.11", - "wsts 2.0.0", + "wsts", ] [[package]] @@ -4661,25 +4647,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "wsts" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44abcb9564cb6b3fc6f75a3883bc740ee37abdc343a6fdd6949e253d49c98f5e" -dependencies = [ - "bs58 0.4.0", - "hashbrown 0.13.2", - "hex", - "num-traits", - "p256k1", - "polynomial", - "primitive-types", - "rand_core 0.6.4", - "serde", - "sha2 0.10.6", - "thiserror", -] - [[package]] name = "wsts" version = "2.0.0" diff --git a/stacks-signer/README.md b/stacks-signer/README.md index 9be7647aa32..73cd9554f51 100644 --- a/stacks-signer/README.md +++ b/stacks-signer/README.md @@ -1,6 +1,6 @@ # stacks-signer: Stacks Signer CLI -stacks-signer is a command-line interface (CLI) for executing DKG (Distributed Key Generation) rounds, signing transactions and blocks, and more within the Stacks blockchain ecosystem. This tool provides various subcommands to interact with the StackerDB, perform cryptographic operations, and manage configurations. +stacks-signer is a command-line interface (CLI) for executing DKG (Distributed Key Generation) rounds, signing transactions and blocks, and more within the Stacks blockchain ecosystem. This tool provides various subcommands to interact with the StackerDB contract, perform cryptographic operations, and run a Stacks compliant signer. ## Installation @@ -25,16 +25,6 @@ To use stacks-signer, you need to build and install the Rust program. You can do ./target/release/stacks-signer --help ``` -### Configuration - -You can provide configuration options such as the host, contract, and private key using a TOML file. Use the `--config` option to specify the path to the configuration file. Alternatively, you can provide the necessary options directly in the command line. - -```bash -./stacks-signer --config -``` - -- `--config`: Path to the TOML configuration file. - ## Usage The stacks-signer CLI provides the following subcommands: @@ -44,51 +34,108 @@ The stacks-signer CLI provides the following subcommands: Retrieve a chunk from the StackerDB instance. ```bash -./stacks-signer --config get-chunk --slot_id --slot_version +./stacks-signer get-chunk --host --contract --slot_id --slot_version ``` -- `--host`: The stacks node host to connect to. Required if not using the --config option. -- `--contract`: The contract ID of the StackerDB instance. Required if not using the --config option. -- `--slot_id`: The slot ID to get. -- `--slot_version`: The slot version to get. +- `--host`: The stacks node host to connect to. +- `--contract`: The contract ID of the StackerDB instance. +- `--slot-id`: The slot ID to get. +- `--slot-version`: The slot version to get. ### `get-latest-chunk` Retrieve the latest chunk from the StackerDB instance. ```bash -./stacks-signer --config get-latest-chunk --slot_id +./stacks-signer get-latest-chunk --host --contract --slot-id ``` -- `--host`: The stacks node host to connect to. Required if not using the --config option. -- `--contract`: The contract ID of the StackerDB instance. Required if not using the --config option. -- `--slot_id`: The slot ID to get. +- `--host`: The stacks node host to connect to. +- `--contract`: The contract ID of the StackerDB instance. +- `--slot-id`: The slot ID to get. ### `list-chunks` List chunks from the StackerDB instance. ```bash -./stacks-signer --config list-chunks +./stacks-signer list-chunks ``` - -- `--host`: The stacks node host to connect to. Required if not using the --config option. -- `--contract`: The contract ID of the StackerDB instance. Required if not using the --config option. +- `--host`: The stacks node host to connect to. +- `--contract`: The contract ID of the StackerDB instance. ### `put-chunk` Upload a chunk to the StackerDB instance. ```bash -./stacks-signer --config put-chunk --slot_id --slot_version [--data ] +./stacks-signer put-chunk --host --contract --private_key --slot-id --slot-version [--data ] ``` -- `--host`: The stacks node host to connect to. Required if not using the --config option. -- `--contract`: The contract ID of the StackerDB instance. Required if not using the --config option. -- `--slot_id`: The slot ID to get. -- `--slot_version`: The slot version to get. +- `--host`: The stacks node host to connect to. +- `--contract`: The contract ID of the StackerDB instance. +- `--private_key`: The Stacks private key to use in hexademical format. +- `--slot-id`: The slot ID to get. +- `--slot-version`: The slot version to get. - `--data`: The data to upload. If you wish to pipe data using STDIN, use with '-'. +### `dkg` + +Run a distributed key generation round through stacker-db. + +```bash +./stacks-signer dkg --config +``` + +- `--config`: The path to the signer configuration file. + +### `dkg-sign` + +Run a distributed key generation round and sign a given message through stacker-db. + +```bash +./stacks-signer dkg-sign --config [--data ] +``` +- `--config`: The path to the signer configuration file. +- `--data`: The data to sign. If you wish to pipe data using STDIN, use with '-'. + + +### `dkg-sign` + +Sign a given message through stacker-db. + +```bash +./stacks-signer sign --config [--data ] +``` +- `--config`: The path to the signer configuration file. +- `--data`: The data to sign. If you wish to pipe data using STDIN, use with '-'. + +### `run` + +Start the signer and handle requests to sign messages and participate in DKG rounds via stacker-db. +```bash +./stacks-signer run --config +``` +- `--config`: The path to the signer configuration file. + +### `generate-files` + +Generate the necessary files to run a collection of signers to communicate via stacker-db. + +```bash +./stacks-signer generate-files --num-signers --num-keys --network --contract-name --host --dir +``` +- `--num-signers`: The number of signers to generate configuration files for. +- `--num-keys`: The total number of key ids to distribute among the signers. +- `--private-keys:` A path to a file containing a list of hexadecimal representations of Stacks private keys. Required if `--num-keys` is not set. +- `--network`: The network to use. One of "mainnet" or "testnet". +- `--contract-name`: The contract name to give the generated stacker-db contract. +- `--host`: The stacks node host to connect to. +- `--dir`: The directory to write files to. Defaults to the current directory. + +## Contributing + +To contribute to the stacks-signer project, please read the [Contributing Guidelines](../CONTRIBUTING.md). ## License This program is open-source software released under the terms of the GNU General Public License (GPL). You should have received a copy of the GNU General Public License along with this program. \ No newline at end of file diff --git a/stacks-signer/src/cli.rs b/stacks-signer/src/cli.rs new file mode 100644 index 00000000000..b403d72d751 --- /dev/null +++ b/stacks-signer/src/cli.rs @@ -0,0 +1,185 @@ +use std::{ + io::{self, Read}, + net::SocketAddr, + path::PathBuf, +}; + +use clap::Parser; +use clarity::vm::types::QualifiedContractIdentifier; +use stacks_common::types::chainstate::StacksPrivateKey; + +use crate::config::Network; + +#[derive(Parser, Debug)] +#[command(author, version, about)] +/// The CLI arguments for the stacks signer +pub struct Cli { + /// Subcommand action to take + #[command(subcommand)] + pub command: Command, +} + +/// Subcommands for the stacks signer binary +#[derive(clap::Subcommand, Debug)] +pub enum Command { + /// Get a chunk from the stacker-db instance + GetChunk(GetChunkArgs), + /// Get the latest chunk from the stacker-db instance + GetLatestChunk(GetLatestChunkArgs), + /// List chunks from the stacker-db instance + ListChunks(StackerDBArgs), + /// Upload a chunk to the stacker-db instance + PutChunk(PutChunkArgs), + /// Run DKG and sign the message through the stacker-db instance + DkgSign(SignArgs), + /// Sign the message through the stacker-db instance + Sign(SignArgs), + /// Run a DKG round through the stacker-db instance + Dkg(RunDkgArgs), + /// Run the signer, waiting for events from the stacker-db instance + Run(RunDkgArgs), + /// Generate necessary files for running a collection of signers + GenerateFiles(GenerateFilesArgs), +} + +/// Basic arguments for all cyrptographic and stacker-db functionality +#[derive(Parser, Debug, Clone)] +pub struct StackerDBArgs { + /// The Stacks node to connect to + #[arg(long)] + pub host: SocketAddr, + /// The stacker-db contract to use + #[arg(short, long, value_parser = parse_contract)] + pub contract: QualifiedContractIdentifier, +} + +/// Arguments for the get-chunk command +#[derive(Parser, Debug, Clone)] +pub struct GetChunkArgs { + /// The base arguments + #[clap(flatten)] + pub db_args: StackerDBArgs, + /// The slot ID to get + #[arg(long)] + pub slot_id: u32, + /// The slot version to get + #[arg(long)] + pub slot_version: u32, +} + +/// Arguments for the get-latest-chunk command +#[derive(Parser, Debug, Clone)] +pub struct GetLatestChunkArgs { + /// The base arguments + #[clap(flatten)] + pub db_args: StackerDBArgs, + /// The slot ID to get + #[arg(long)] + pub slot_id: u32, +} + +#[derive(Parser, Debug, Clone)] +/// Arguments for the put-chunk command +pub struct PutChunkArgs { + /// The base arguments + #[clap(flatten)] + pub db_args: StackerDBArgs, + /// The Stacks private key to use in hexademical format + #[arg(short, long, value_parser = parse_private_key)] + pub private_key: StacksPrivateKey, + /// The slot ID to get + #[arg(long)] + pub slot_id: u32, + /// The slot version to get + #[arg(long)] + pub slot_version: u32, + /// The data to upload + #[arg(required = false, value_parser = parse_data)] + pub data: Vec, +} + +#[derive(Parser, Debug, Clone)] +/// Arguments for the dkg-sign and sign command +pub struct SignArgs { + /// Path to config file + #[arg(long, value_name = "FILE")] + pub config: PathBuf, + /// The data to sign + #[arg(required = false, value_parser = parse_data)] + pub data: Vec, +} + +#[derive(Parser, Debug, Clone)] +/// Arguments for the Run and Dkg commands +pub struct RunDkgArgs { + /// Path to config file + #[arg(long, value_name = "FILE")] + pub config: PathBuf, +} + +#[derive(Parser, Debug, Clone)] +/// Arguments for the generate-files command +pub struct GenerateFilesArgs { + #[arg( + long, + required_unless_present = "private_keys", + conflicts_with = "private_keys" + )] + /// The number of signers to generate + pub num_signers: Option, + #[clap(long, value_name = "FILE")] + /// A path to a file containing a list of hexadecimal Stacks private keys + pub private_keys: Option, + #[arg(long)] + /// The total number of key ids to distribute among the signers + pub num_keys: u32, + #[arg(long, value_parser = parse_network)] + /// The network to use. One of "mainnet" or "testnet". + pub network: Network, + #[arg(long)] + /// The name of the contract to use + pub contract_name: String, + #[arg(long)] + /// The stacks node host to use + pub host: SocketAddr, + /// The directory to write the test data files to + #[arg(long, default_value = ".")] + pub dir: PathBuf, +} + +/// Parse the contract ID +fn parse_contract(contract: &str) -> Result { + QualifiedContractIdentifier::parse(contract).map_err(|e| format!("Invalid contract: {}", e)) +} + +/// Parse the hexadecimal Stacks private key +fn parse_private_key(private_key: &str) -> Result { + StacksPrivateKey::from_hex(private_key).map_err(|e| format!("Invalid private key: {}", e)) +} + +/// Parse the input data +fn parse_data(data: &str) -> Result, String> { + let data = if data == "-" { + // Parse the data from stdin + let mut buf = vec![]; + io::stdin().read_to_end(&mut buf).unwrap(); + buf + } else { + data.as_bytes().to_vec() + }; + Ok(data) +} + +/// Parse the network. Must be one of "mainnet" or "testnet". +fn parse_network(network: &str) -> Result { + Ok(match network.to_lowercase().as_str() { + "mainnet" => Network::Mainnet, + "testnet" => Network::Testnet, + _ => { + return Err(format!( + "Invalid network: {}. Must be one of \"mainnet\" or \"testnet\".", + network + )) + } + }) +} diff --git a/stacks-signer/src/lib.rs b/stacks-signer/src/lib.rs index 7848b469d93..7cc2f207177 100644 --- a/stacks-signer/src/lib.rs +++ b/stacks-signer/src/lib.rs @@ -3,6 +3,8 @@ # stacks-signer: a libary for creating a Stacks compliant signer. A default implementation binary is also provided. Usage documentation can be found in the [README](https://github.com/Trust-Machines/core-eng/stacks-signer-api/README.md). */ +/// The cli module for the signer binary +pub mod cli; /// The configuration module for the signer pub mod config; /// All crypto related modules @@ -11,3 +13,5 @@ pub mod crypto; pub mod runloop; /// The signer client for communicating with stackerdb/stacks nodes pub mod stacks_client; +/// Util functions +pub mod utils; diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index 215199381b0..80b60304d97 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -30,126 +30,34 @@ use clap::Parser; use clarity::vm::types::QualifiedContractIdentifier; use libsigner::{RunningSigner, Signer, SignerSession, StackerDBEventReceiver, StackerDBSession}; use libstackerdb::StackerDBChunkData; -use stacks_common::types::chainstate::StacksPrivateKey; +use slog::slog_debug; +use stacks_common::{ + address::{ + AddressHashMode, C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + C32_ADDRESS_VERSION_TESTNET_SINGLESIG, + }, + debug, + types::chainstate::{StacksAddress, StacksPrivateKey, StacksPublicKey}, +}; use stacks_signer::{ - config::Config, + cli::{ + Cli, Command, GenerateFilesArgs, GetChunkArgs, GetLatestChunkArgs, PutChunkArgs, + StackerDBArgs, + }, + config::{Config, Network}, crypto::frost::Coordinator as FrostCoordinator, runloop::{RunLoop, RunLoopCommand}, + utils::{build_signer_config_tomls, build_stackerdb_contract}, }; use std::{ - io::{self, Read, Write}, + fs::File, + io::{self, BufRead, Write}, net::SocketAddr, path::PathBuf, sync::mpsc::{channel, Receiver}, }; use wsts::Point; -#[derive(Parser, Debug)] -#[command(author, version, about)] -/// The CLI arguments for the stacks signer -pub struct Cli { - /// Path to config file - #[arg(long, value_name = "FILE")] - config: Option, - /// The Stacks node to connect to - #[clap(long, required_unless_present = "config", conflicts_with = "config")] - host: Option, - /// The stacker-db contract to use - #[arg(short, long, value_parser = parse_contract, required_unless_present = "config", conflicts_with = "config")] - contract: Option, - /// The Stacks private key to use in hexademical format - #[arg(short, long, value_parser = parse_private_key, required_unless_present = "config", conflicts_with = "config")] - private_key: Option, - /// Subcommand action to take - #[command(subcommand)] - pub command: Command, -} - -/// Subcommands for the stacks signer binary -#[derive(clap::Subcommand, Debug)] -pub enum Command { - /// Get a chunk from the stacker-db instance - GetChunk(GetChunkArgs), - /// Get the latest chunk from the stacker-db instance - GetLatestChunk(GetLatestChunkArgs), - /// List chunks from the stacker-db instance - ListChunks, - /// Upload a chunk to the stacker-db instance - PutChunk(PutChunkArgs), - /// Run DKG and sign the message through the stacker-db instance - DkgSign(SignArgs), - /// Sign the message through the stacker-db instance - Sign(SignArgs), - /// Run a DKG round through the stacker-db instance - Dkg, - /// Run the signer, waiting for events from the stacker-db instance - Run, -} - -/// Arguments for the get-chunk command -#[derive(Parser, Debug, Clone)] -pub struct GetChunkArgs { - /// The slot ID to get - #[arg(long)] - slot_id: u32, - /// The slot version to get - #[arg(long)] - slot_version: u32, -} - -/// Arguments for the get-latest-chunk command -#[derive(Parser, Debug, Clone)] -pub struct GetLatestChunkArgs { - /// The slot ID to get - #[arg(long)] - slot_id: u32, -} - -#[derive(Parser, Debug, Clone)] -/// Arguments for the put-chunk command -pub struct PutChunkArgs { - /// The slot ID to get - #[arg(long)] - slot_id: u32, - /// The slot version to get - #[arg(long)] - slot_version: u32, - /// The data to upload - #[arg(required = false, value_parser = parse_data)] - data: Vec, -} - -#[derive(Parser, Debug, Clone)] -/// Arguments for the dkg-sign and sign command -pub struct SignArgs { - /// The data to sign - #[arg(required = false, value_parser = parse_data)] - data: Vec, -} - -/// Parse the contract ID -fn parse_contract(contract: &str) -> Result { - QualifiedContractIdentifier::parse(contract).map_err(|e| format!("Invalid contract: {}", e)) -} - -/// Parse the hexadecimal Stacks private key -fn parse_private_key(private_key: &str) -> Result { - StacksPrivateKey::from_hex(private_key).map_err(|e| format!("Invalid private key: {}", e)) -} - -/// Parse the input data -fn parse_data(data: &str) -> Result, String> { - let data = if data == "-" { - // Parse the data from stdin - let mut buf = vec![]; - io::stdin().read_to_end(&mut buf).unwrap(); - buf - } else { - data.as_bytes().to_vec() - }; - Ok(data) -} - /// Create a new stacker db session fn stackerdb_session(host: SocketAddr, contract: QualifiedContractIdentifier) -> StackerDBSession { let mut session = StackerDBSession::new(host, contract.clone()); @@ -188,83 +96,160 @@ fn spawn_running_signer( signer.spawn(endpoint).unwrap() } -fn main() { - let cli = Cli::parse(); - let (host, contract, private_key) = if let Some(config) = &cli.config { - let config = Config::try_from(config).unwrap(); - ( - config.node_host, - config.stackerdb_contract_id, - config.stacks_private_key, - ) +fn handle_get_chunk(args: GetChunkArgs) { + debug!("Getting chunk..."); + let mut session = stackerdb_session(args.db_args.host, args.db_args.contract); + let chunk_opt = session.get_chunk(args.slot_id, args.slot_version).unwrap(); + write_chunk_to_stdout(chunk_opt); +} + +fn handle_get_latest_chunk(args: GetLatestChunkArgs) { + debug!("Getting latest chunk..."); + let mut session = stackerdb_session(args.db_args.host, args.db_args.contract); + let chunk_opt = session.get_latest_chunk(args.slot_id).unwrap(); + write_chunk_to_stdout(chunk_opt); +} + +fn handle_list_chunks(args: StackerDBArgs) { + debug!("Listing chunks..."); + let mut session = stackerdb_session(args.host, args.contract); + let chunk_list = session.list_chunks().unwrap(); + println!("{}", serde_json::to_string(&chunk_list).unwrap()); +} + +fn handle_put_chunk(args: PutChunkArgs) { + debug!("Putting chunk..."); + let mut session = stackerdb_session(args.db_args.host, args.db_args.contract); + let mut chunk = StackerDBChunkData::new(args.slot_id, args.slot_version, args.data.clone()); + chunk.sign(&args.private_key).unwrap(); + let chunk_ack = session.put_chunk(chunk).unwrap(); + println!("{}", serde_json::to_string(&chunk_ack).unwrap()); +} + +fn handle_generate_files(args: GenerateFilesArgs) { + debug!("Generating files..."); + let signer_stacks_private_keys = if let Some(path) = args.private_keys { + let file = File::open(&path).unwrap(); + let reader = io::BufReader::new(file); + + let private_keys: Vec = reader.lines().collect::>().unwrap(); + println!("{}", StacksPrivateKey::new().to_hex()); + let private_keys = private_keys + .iter() + .map(|key| StacksPrivateKey::from_hex(key).expect("Failed to parse private key.")) + .collect::>(); + if private_keys.is_empty() { + panic!("Private keys file is empty."); + } + private_keys } else { - ( - cli.host.unwrap(), - cli.contract.unwrap(), - cli.private_key.unwrap(), - ) + let num_signers = args.num_signers.unwrap(); + if num_signers == 0 { + panic!("--num-signers must be non-zero."); + } + (0..num_signers) + .map(|_| StacksPrivateKey::new()) + .collect::>() }; + let signer_stacks_addresses = signer_stacks_private_keys + .iter() + .map(|key| to_addr(key, &args.network)) + .collect::>(); + // Build the stackerdb contract + let stackerdb_contract = build_stackerdb_contract(&signer_stacks_addresses); + debug!("Stacker DB Contract: {}", &stackerdb_contract); + let contract_id = QualifiedContractIdentifier::new( + signer_stacks_addresses[0].into(), + args.contract_name.as_str().into(), + ); + debug!("Contract ID: {}", contract_id); + let signer_config_tomls = build_signer_config_tomls( + &signer_stacks_private_keys, + args.num_keys, + &args.host.to_string(), + &contract_id.to_string(), + ); + debug!("Built {:?} signer config tomls.", signer_config_tomls.len()); + for (i, file_contents) in signer_config_tomls.iter().enumerate() { + let signer_conf_path = args.dir.join(format!("signer-{}.toml", i)); + let signer_conf_filename = signer_conf_path.to_str().unwrap(); + let mut signer_conf_file = File::create(signer_conf_filename).unwrap(); + signer_conf_file + .write_all(file_contents.as_bytes()) + .unwrap(); + println!("Created signer config toml file: {}", signer_conf_filename); + } + let stackerdb_contract_path = args.dir.join("stackerdb.clar"); + let stackerdb_contract_filename = stackerdb_contract_path.to_str().unwrap(); + let mut stackerdb_contract_file = File::create(stackerdb_contract_filename).unwrap(); + stackerdb_contract_file + .write_all(stackerdb_contract.as_bytes()) + .unwrap(); + println!( + "Created stackerdb clarity contract: {}", + stackerdb_contract_filename + ); +} +fn main() { + let cli = Cli::parse(); let (_cmd_send, cmd_recv) = channel(); - let mut session = stackerdb_session(host, contract); match cli.command { Command::GetChunk(args) => { - let chunk_opt = session.get_chunk(args.slot_id, args.slot_version).unwrap(); - write_chunk_to_stdout(chunk_opt); + handle_get_chunk(args); } Command::GetLatestChunk(args) => { - let chunk_opt = session.get_latest_chunk(args.slot_id).unwrap(); - write_chunk_to_stdout(chunk_opt); + handle_get_latest_chunk(args); } - Command::ListChunks => { - let chunk_list = session.list_chunks().unwrap(); - println!("{}", serde_json::to_string(&chunk_list).unwrap()); + Command::ListChunks(args) => { + handle_list_chunks(args); } Command::PutChunk(args) => { - let mut chunk = - StackerDBChunkData::new(args.slot_id, args.slot_version, args.data.clone()); - chunk.sign(&private_key).unwrap(); - let chunk_ack = session.put_chunk(chunk).unwrap(); - println!("{}", serde_json::to_string(&chunk_ack).unwrap()); + handle_put_chunk(args); } - Command::Dkg => { - if let Some(config) = &cli.config { - let _running_signer = spawn_running_signer(config, RunLoopCommand::Dkg, cmd_recv); - } else { - panic!("dkg is currently only supported when using a config file"); - } + Command::Dkg(args) => { + debug!("Running DKG..."); + let _running_signer = spawn_running_signer(&args.config, RunLoopCommand::Dkg, cmd_recv); } Command::DkgSign(args) => { - if let Some(config) = &cli.config { - let _running_signer = spawn_running_signer( - config, - RunLoopCommand::DkgSign { message: args.data }, - cmd_recv, - ); - } else { - panic!("dkg-sign is currently only supported when using a config file"); - } + debug!("Running DKG and Signing round..."); + let _running_signer = spawn_running_signer( + &args.config, + RunLoopCommand::DkgSign { message: args.data }, + cmd_recv, + ); } Command::Sign(args) => { - if let Some(config) = &cli.config { - let _running_signer = spawn_running_signer( - config, - RunLoopCommand::Sign { message: args.data }, - cmd_recv, - ); - } else { - panic!("dkg-sign is currently only supported when using a config file"); - } + debug!("Signing message..."); + let _running_signer = spawn_running_signer( + &args.config, + RunLoopCommand::Sign { message: args.data }, + cmd_recv, + ); + } + Command::Run(args) => { + debug!("Running signer..."); + let _running_signer = spawn_running_signer(&args.config, RunLoopCommand::Run, cmd_recv); } - Command::Run => { - if let Some(config) = &cli.config { - let _running_signer = spawn_running_signer(config, RunLoopCommand::Run, cmd_recv); - } else { - panic!("dkg-sign is currently only supported when using a config file"); - } + Command::GenerateFiles(args) => { + handle_generate_files(args); } } } +fn to_addr(stacks_private_key: &StacksPrivateKey, network: &Network) -> StacksAddress { + let version = match network { + Network::Mainnet => C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + Network::Testnet => C32_ADDRESS_VERSION_TESTNET_SINGLESIG, + }; + StacksAddress::from_public_keys( + version, + &AddressHashMode::SerializeP2PKH, + 1, + &vec![StacksPublicKey::from_private(stacks_private_key)], + ) + .unwrap() +} + #[cfg(test)] pub mod tests; diff --git a/stacks-signer/src/stacks_client.rs b/stacks-signer/src/stacks_client.rs index 79bda34ec86..d94dc6b6542 100644 --- a/stacks-signer/src/stacks_client.rs +++ b/stacks-signer/src/stacks_client.rs @@ -8,7 +8,7 @@ use stacks_common::{debug, info, types::chainstate::StacksPrivateKey, warn}; use crate::config::Config; -const SLOTS_PER_USER: u32 = 16; +const SLOTS_PER_USER: u32 = 10; #[derive(thiserror::Error, Debug)] /// Client error type diff --git a/stacks-signer/src/tests/conf/signer-0.toml b/stacks-signer/src/tests/conf/signer-0.toml new file mode 100644 index 00000000000..d97d2501647 --- /dev/null +++ b/stacks-signer/src/tests/conf/signer-0.toml @@ -0,0 +1,19 @@ + +message_private_key = "CosqumxnRfykP24DdUCDEEMZ2qCwHPUyBy21Qf55EPKJ" +stacks_private_key = "8a99f02ddede302a0e9fbb4eb4ef1bd587a9c076f67230c3e7a62a9d78d34d0401" +node_host = "127.0.0.1:20444" +endpoint = "localhost:30000" +network = "testnet" +stackerdb_contract_id = "ST1RER7KAS4Q81ZTCA025SVSNZ73444NHG4D7BKWC.signers-stackerdb" +signer_id = 0 +signers = [ + {public_key = "y2ZGH2FdFYXCP5Tm2y8yGAR6LB2j3yVUTaV4QpbtC7xm", key_ids = [1, 2, 3, 4]} + , + {public_key = "24z7XAnx99SAbeQekcoAM5jQvDyvErF7pSGxNJ9Mqu5uy", key_ids = [5, 6, 7, 8]} + , + {public_key = "eAsVpTz66qN687yzRPrVosyWhMxewd6rfCC2tt4fDGDf", key_ids = [9, 10, 11, 12]} + , + {public_key = "2BYKuG6a2jizKaoJJFvdyJc8WFqhBu3oZYuxGHCAt8Pgd", key_ids = [13, 14, 15, 16]} + , + {public_key = "cHMPeNTruE17rvvQ1LhD7m9QCna4KZZCYgsAUbkrWHU4", key_ids = [17, 18, 19, 20]} + ] diff --git a/stacks-signer/src/tests/conf/signer-1.toml b/stacks-signer/src/tests/conf/signer-1.toml new file mode 100644 index 00000000000..f4cf9d0021c --- /dev/null +++ b/stacks-signer/src/tests/conf/signer-1.toml @@ -0,0 +1,19 @@ + +message_private_key = "2KA3ArTdW8NhqMzaZUG5RXo5RgPWx5PRwuLdiZamZ6du" +stacks_private_key = "2136d9374c3aff76808be7917a8c66a30b07d7d5a56a42fedb23cb90c038114e01" +node_host = "127.0.0.1:20444" +endpoint = "localhost:30001" +network = "testnet" +stackerdb_contract_id = "ST1RER7KAS4Q81ZTCA025SVSNZ73444NHG4D7BKWC.signers-stackerdb" +signer_id = 1 +signers = [ + {public_key = "y2ZGH2FdFYXCP5Tm2y8yGAR6LB2j3yVUTaV4QpbtC7xm", key_ids = [1, 2, 3, 4]} + , + {public_key = "24z7XAnx99SAbeQekcoAM5jQvDyvErF7pSGxNJ9Mqu5uy", key_ids = [5, 6, 7, 8]} + , + {public_key = "eAsVpTz66qN687yzRPrVosyWhMxewd6rfCC2tt4fDGDf", key_ids = [9, 10, 11, 12]} + , + {public_key = "2BYKuG6a2jizKaoJJFvdyJc8WFqhBu3oZYuxGHCAt8Pgd", key_ids = [13, 14, 15, 16]} + , + {public_key = "cHMPeNTruE17rvvQ1LhD7m9QCna4KZZCYgsAUbkrWHU4", key_ids = [17, 18, 19, 20]} + ] diff --git a/stacks-signer/src/tests/conf/signer-2.toml b/stacks-signer/src/tests/conf/signer-2.toml new file mode 100644 index 00000000000..e96e1cf42f9 --- /dev/null +++ b/stacks-signer/src/tests/conf/signer-2.toml @@ -0,0 +1,19 @@ + +message_private_key = "5PsUp2Sn21DeXNH45eUiUsea6Abne7xcBzSM1PCVZzLf" +stacks_private_key = "e7db7cb2e44693041ce195b37cc77d2373f957fd505f769cb26465cd91a518d801" +node_host = "127.0.0.1:20444" +endpoint = "localhost:30002" +network = "testnet" +stackerdb_contract_id = "ST1RER7KAS4Q81ZTCA025SVSNZ73444NHG4D7BKWC.signers-stackerdb" +signer_id = 2 +signers = [ + {public_key = "y2ZGH2FdFYXCP5Tm2y8yGAR6LB2j3yVUTaV4QpbtC7xm", key_ids = [1, 2, 3, 4]} + , + {public_key = "24z7XAnx99SAbeQekcoAM5jQvDyvErF7pSGxNJ9Mqu5uy", key_ids = [5, 6, 7, 8]} + , + {public_key = "eAsVpTz66qN687yzRPrVosyWhMxewd6rfCC2tt4fDGDf", key_ids = [9, 10, 11, 12]} + , + {public_key = "2BYKuG6a2jizKaoJJFvdyJc8WFqhBu3oZYuxGHCAt8Pgd", key_ids = [13, 14, 15, 16]} + , + {public_key = "cHMPeNTruE17rvvQ1LhD7m9QCna4KZZCYgsAUbkrWHU4", key_ids = [17, 18, 19, 20]} + ] diff --git a/stacks-signer/src/tests/conf/signer-3.toml b/stacks-signer/src/tests/conf/signer-3.toml new file mode 100644 index 00000000000..3b90338af3a --- /dev/null +++ b/stacks-signer/src/tests/conf/signer-3.toml @@ -0,0 +1,19 @@ + +message_private_key = "78WZR12DxgbtCpTr7cUYFL1mWnVNJrwG8hWTvAMa1tHQ" +stacks_private_key = "a7ade2a5189f79161e02a6c2f4dab325fef2a5a74890d4ff0c0d1721786f920e01" +node_host = "127.0.0.1:20444" +endpoint = "localhost:30003" +network = "testnet" +stackerdb_contract_id = "ST1RER7KAS4Q81ZTCA025SVSNZ73444NHG4D7BKWC.signers-stackerdb" +signer_id = 3 +signers = [ + {public_key = "y2ZGH2FdFYXCP5Tm2y8yGAR6LB2j3yVUTaV4QpbtC7xm", key_ids = [1, 2, 3, 4]} + , + {public_key = "24z7XAnx99SAbeQekcoAM5jQvDyvErF7pSGxNJ9Mqu5uy", key_ids = [5, 6, 7, 8]} + , + {public_key = "eAsVpTz66qN687yzRPrVosyWhMxewd6rfCC2tt4fDGDf", key_ids = [9, 10, 11, 12]} + , + {public_key = "2BYKuG6a2jizKaoJJFvdyJc8WFqhBu3oZYuxGHCAt8Pgd", key_ids = [13, 14, 15, 16]} + , + {public_key = "cHMPeNTruE17rvvQ1LhD7m9QCna4KZZCYgsAUbkrWHU4", key_ids = [17, 18, 19, 20]} + ] diff --git a/stacks-signer/src/tests/conf/signer-4.toml b/stacks-signer/src/tests/conf/signer-4.toml new file mode 100644 index 00000000000..49833e9dfe6 --- /dev/null +++ b/stacks-signer/src/tests/conf/signer-4.toml @@ -0,0 +1,19 @@ + +message_private_key = "25sCKA2hhrbYLR9U7RfKtL4hWQiY9cdmntCDww5SB9HA" +stacks_private_key = "61a2e31072a9cec9b1e5950d7d63e69a8b0e049a7eab43bc5918f7bb1bed8aa601" +node_host = "127.0.0.1:20444" +endpoint = "localhost:30004" +network = "testnet" +stackerdb_contract_id = "ST1RER7KAS4Q81ZTCA025SVSNZ73444NHG4D7BKWC.signers-stackerdb" +signer_id = 4 +signers = [ + {public_key = "y2ZGH2FdFYXCP5Tm2y8yGAR6LB2j3yVUTaV4QpbtC7xm", key_ids = [1, 2, 3, 4]} + , + {public_key = "24z7XAnx99SAbeQekcoAM5jQvDyvErF7pSGxNJ9Mqu5uy", key_ids = [5, 6, 7, 8]} + , + {public_key = "eAsVpTz66qN687yzRPrVosyWhMxewd6rfCC2tt4fDGDf", key_ids = [9, 10, 11, 12]} + , + {public_key = "2BYKuG6a2jizKaoJJFvdyJc8WFqhBu3oZYuxGHCAt8Pgd", key_ids = [13, 14, 15, 16]} + , + {public_key = "cHMPeNTruE17rvvQ1LhD7m9QCna4KZZCYgsAUbkrWHU4", key_ids = [17, 18, 19, 20]} + ] diff --git a/stacks-signer/src/tests/conf/signer1.toml b/stacks-signer/src/tests/conf/signer1.toml deleted file mode 100644 index ddda546861a..00000000000 --- a/stacks-signer/src/tests/conf/signer1.toml +++ /dev/null @@ -1,14 +0,0 @@ -message_private_key = "8PdTCUUd9SvW1HHjosqwTJk4Ct9iRsguu7EwqG52SmJJ" -stacks_private_key = "03c1e0ec2a869e4f0214a2f0d2653bbe993e2fc028b41af4f3056723f72b94778e" -network = "testnet" -node_host = "127.0.0.1:20443" -stackerdb_contract_id = "" -signer_id = 1 -# This data should be retrieved from .pox contract -signers = [ - {public_key = "22t2a1z8vkZgZF9gekHBNyT7s2bugRHsPmGwS7Pm5MnEx", key_ids = [1, 2, 3, 4]}, - {public_key = "25hwd38Ynr4dMqnfe5ssC8KN8sKPDbSTJa2GkaPZJkVwj", key_ids = [5, 6, 7, 8]}, - {public_key = "sdAKXq6rv4tkCW8Vd8Km2DTSG4mAKMnij92vvdqsCBg9", key_ids = [9, 10, 11, 12]} - {public_key = "23si1XhLwZsrsMC19gBi5EsK2g4uJo4qCWjRSEP3qdGzi", key_ids = [13, 14, 15, 16]} - {public_key = "eAk931xsKvpS8v5Cfk3G48g7pgGdSA58FLaEA8KG3SPx", key_ids = [17, 18, 19, 20]} -] \ No newline at end of file diff --git a/stacks-signer/src/tests/conf/signer2.toml b/stacks-signer/src/tests/conf/signer2.toml deleted file mode 100644 index 67093f87801..00000000000 --- a/stacks-signer/src/tests/conf/signer2.toml +++ /dev/null @@ -1,14 +0,0 @@ -message_private_key = "8PdTCUUd9SvW1HHjosqwTJk4Ct9iRsguu7EwqG52SmJJ" -stacks_private_key = "b612f5ab0af6b540f19907f41b9070469a015b3b874e671e39aa390cb4815a8501" -network = "testnet" -node_host = "127.0.0.1:20443" -stackerdb_contract_id = "" -signer_id = 2 -# This data should be retrieved from .pox contract -signers = [ - {public_key = "22t2a1z8vkZgZF9gekHBNyT7s2bugRHsPmGwS7Pm5MnEx", key_ids = [1, 2, 3, 4]}, - {public_key = "25hwd38Ynr4dMqnfe5ssC8KN8sKPDbSTJa2GkaPZJkVwj", key_ids = [5, 6, 7, 8]}, - {public_key = "sdAKXq6rv4tkCW8Vd8Km2DTSG4mAKMnij92vvdqsCBg9", key_ids = [9, 10, 11, 12]} - {public_key = "23si1XhLwZsrsMC19gBi5EsK2g4uJo4qCWjRSEP3qdGzi", key_ids = [13, 14, 15, 16]} - {public_key = "eAk931xsKvpS8v5Cfk3G48g7pgGdSA58FLaEA8KG3SPx", key_ids = [17, 18, 19, 20]} -] \ No newline at end of file diff --git a/stacks-signer/src/tests/conf/signer3.toml b/stacks-signer/src/tests/conf/signer3.toml deleted file mode 100644 index d53d34cee4d..00000000000 --- a/stacks-signer/src/tests/conf/signer3.toml +++ /dev/null @@ -1,14 +0,0 @@ -message_private_key = "8PdTCUUd9SvW1HHjosqwTJk4Ct9iRsguu7EwqG52SmJJ" -stacks_private_key = "660299ed73625b9ab328f158d4d77af6942778de83f4739d165bb7eda1d9d40e01" -network = "testnet" -node_host = "127.0.0.1:20443" -stackerdb_contract_id = "" -signer_id = 3 -# This data should be retrieved from .pox contract -signers = [ - {public_key = "22t2a1z8vkZgZF9gekHBNyT7s2bugRHsPmGwS7Pm5MnEx", key_ids = [1, 2, 3, 4]}, - {public_key = "25hwd38Ynr4dMqnfe5ssC8KN8sKPDbSTJa2GkaPZJkVwj", key_ids = [5, 6, 7, 8]}, - {public_key = "sdAKXq6rv4tkCW8Vd8Km2DTSG4mAKMnij92vvdqsCBg9", key_ids = [9, 10, 11, 12]} - {public_key = "23si1XhLwZsrsMC19gBi5EsK2g4uJo4qCWjRSEP3qdGzi", key_ids = [13, 14, 15, 16]} - {public_key = "eAk931xsKvpS8v5Cfk3G48g7pgGdSA58FLaEA8KG3SPx", key_ids = [17, 18, 19, 20]} -] \ No newline at end of file diff --git a/stacks-signer/src/tests/conf/signer4.toml b/stacks-signer/src/tests/conf/signer4.toml deleted file mode 100644 index fd934765365..00000000000 --- a/stacks-signer/src/tests/conf/signer4.toml +++ /dev/null @@ -1,14 +0,0 @@ -message_private_key = "8PdTCUUd9SvW1HHjosqwTJk4Ct9iRsguu7EwqG52SmJJ" -stacks_private_key = "2374fa45e32342aee5ad27b819ba6bfe8553646aea4c7fb1c2e92d60680e8adc01" -network = "testnet" -node_host = "127.0.0.1:20443" -stackerdb_contract_id = "" -signer_id = 4 -# This data should be retrieved from .pox contract -signers = [ - {public_key = "22t2a1z8vkZgZF9gekHBNyT7s2bugRHsPmGwS7Pm5MnEx", key_ids = [1, 2, 3, 4]}, - {public_key = "25hwd38Ynr4dMqnfe5ssC8KN8sKPDbSTJa2GkaPZJkVwj", key_ids = [5, 6, 7, 8]}, - {public_key = "sdAKXq6rv4tkCW8Vd8Km2DTSG4mAKMnij92vvdqsCBg9", key_ids = [9, 10, 11, 12]} - {public_key = "23si1XhLwZsrsMC19gBi5EsK2g4uJo4qCWjRSEP3qdGzi", key_ids = [13, 14, 15, 16]} - {public_key = "eAk931xsKvpS8v5Cfk3G48g7pgGdSA58FLaEA8KG3SPx", key_ids = [17, 18, 19, 20]} -] \ No newline at end of file diff --git a/stacks-signer/src/tests/conf/signers-test-data.txt b/stacks-signer/src/tests/conf/signers-test-data.txt deleted file mode 100644 index 4e0122fe8fe..00000000000 --- a/stacks-signer/src/tests/conf/signers-test-data.txt +++ /dev/null @@ -1,31 +0,0 @@ -# This file is just to keep track of private and public keys of signers used in tests and in the default deployed contract -signers = [ - { - stacks_address = "ST2S05M62W9J94F6Z2YEPKSMMHHFCSAEVVCF01WWE", - stacks_private_key = "03c1e0ec2a869e4f0214a2f0d2653bbe993e2fc028b41af4f3056723f72b94778e", - stacks_public_key = "41f19c45968b069110e064881fe5eac93e1d183dcbce575ae635fb1db3030bda01", - ecdsa_private_key = "8PdTCUUd9SvW1HHjosqwTJk4Ct9iRsguu7EwqG52SmJJ", - ecdsa_public_key = "22t2a1z8vkZgZF9gekHBNyT7s2bugRHsPmGwS7Pm5MnEx", - }, - { - stacks_address = "STB1HK64HJKE3TKW4JJ9KV9FG78DDFNADQZVVNJT", - stacks_public_key = "02baba8a8ea89b6cb46fe11227a26e50151a914640926f3a2bbdbe037cd69fe59e", - stacks_private_key = "b612f5ab0af6b540f19907f41b9070469a015b3b874e671e39aa390cb4815a8501", - ecdsa_private_key = "Dfv8AobctbL4AP12STiAcuiHtPrgkU7NRoihk8qR29uL" - ecdsa_public_key = "25hwd38Ynr4dMqnfe5ssC8KN8sKPDbSTJa2GkaPZJkVwj", - }, - { - stacks_address = "ST3DHSJ3YRGRPRD5MA457YPD7N2Y2E7RQRH6T5E36", - stacks_public_key = "03ea842c35964629086154717f02960b791cdc2278b4fd48e92c2d25c289754135", - stacks_private_key = "660299ed73625b9ab328f158d4d77af6942778de83f4739d165bb7eda1d9d40e01", - ecdsa_private_key = "D4rspwxdXb4XNQrC4PLkVBo5mHGFVrStY8RopYUGa5sv" - ecdsa_public_key = "sdAKXq6rv4tkCW8Vd8Km2DTSG4mAKMnij92vvdqsCBg9", - }, - { - stacks_address = "STWVA0RJYMNJW7RRXMGQ4TBNCFQYPGVK7Z7P5EVY", - stacks_public_key = "03ddf86619e497456f3cd31c3ba6a618546474b93c3121a141fe44c5161de9d175", - stacks_private_key = "2374fa45e32342aee5ad27b819ba6bfe8553646aea4c7fb1c2e92d60680e8adc01", - ecdsa_private_key = "gCb6CLqoNXV3LTEKW9toCtBxxLvcM5jwNU5rLosVCRy" - ecdsa_public_key = "eAk931xsKvpS8v5Cfk3G48g7pgGdSA58FLaEA8KG3SPx", - } -] diff --git a/stacks-signer/src/tests/contracts/signers-dkg.clar b/stacks-signer/src/tests/contracts/signers-dkg.clar deleted file mode 100644 index a05753f7e49..00000000000 --- a/stacks-signer/src/tests/contracts/signers-dkg.clar +++ /dev/null @@ -1,37 +0,0 @@ -;; title: signers-dkg -;; version: -;; summary: Signer StackerDB test contract -;; description: - -;; traits -;; -(impl-trait 'ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.sip-010-trait) - -(define-read-only (stackerdb-get-signer-slots) - (ok (list - { - signer: 'ST2S05M62W9J94F6Z2YEPKSMMHHFCSAEVVCF01WWE, - num-slots: u16 - } - { - signer: 'STB1HK64HJKE3TKW4JJ9KV9FG78DDFNADQZVVNJT, - num-slots: u16 - } - { - signer: 'ST3DHSJ3YRGRPRD5MA457YPD7N2Y2E7RQRH6T5E36, - num-slots: u16 - } - { - signer: 'STWVA0RJYMNJW7RRXMGQ4TBNCFQYPGVK7Z7P5EVY, - num-slots: u16 - } - ))) - -(define-read-only (stackerdb-get-config) - (ok { - chunk-size: u4096, - write-freq: u0, - max-writes: u4096, - max-neighbors: u32, - hint-replicas: (list ) - })) \ No newline at end of file diff --git a/stacks-signer/src/tests/contracts/signers-stackerdb.clar b/stacks-signer/src/tests/contracts/signers-stackerdb.clar new file mode 100644 index 00000000000..64fe1ae8a10 --- /dev/null +++ b/stacks-signer/src/tests/contracts/signers-stackerdb.clar @@ -0,0 +1,34 @@ + ;; stacker DB + (define-read-only (stackerdb-get-signer-slots) + (ok (list + { + signer: 'ST1RER7KAS4Q81ZTCA025SVSNZ73444NHG4D7BKWC, + num-slots: u10 + } + { + signer: 'ST3WM0P0EWTVRA1EDWS9YFGDKV2A6A8N9TFANTSV9, + num-slots: u10 + } + { + signer: 'ST2411R5HEBCKFQ8YKRANVFAJ8B6QNPEG9WHHP5TX, + num-slots: u10 + } + { + signer: 'ST3N2XTMKWPWCGE3MC8EMDEP0C9CRB728M4EDZDNZ, + num-slots: u10 + } + { + signer: 'ST2NHTSBDFQRJSCBKHFRWG5C0DS22G357CNQE1ZQ, + num-slots: u10 + } + ))) + + (define-read-only (stackerdb-get-config) + (ok { + chunk-size: u4096, + write-freq: u0, + max-writes: u4096, + max-neighbors: u32, + hint-replicas: (list ) + })) + \ No newline at end of file diff --git a/stacks-signer/src/utils.rs b/stacks-signer/src/utils.rs new file mode 100644 index 00000000000..265d220d33a --- /dev/null +++ b/stacks-signer/src/utils.rs @@ -0,0 +1,108 @@ +use p256k1::ecdsa; +use rand_core::OsRng; +use slog::slog_debug; +use stacks_common::{ + debug, + types::chainstate::{StacksAddress, StacksPrivateKey}, +}; +use wsts::Scalar; + +/// Helper function for building a collection of signer config tomls +pub fn build_signer_config_tomls( + signer_stacks_private_keys: &[StacksPrivateKey], + num_keys: u32, + node_host: &str, + contract_id: &str, +) -> Vec { + let num_signers = signer_stacks_private_keys.len() as u32; + let mut rng = OsRng; + let keys_per_signer = num_keys / num_signers; + let mut key_id: u32 = 1; + let mut key_ids = Vec::new(); + for i in 0..num_signers { + let mut ids = Vec::new(); + for _ in 0..keys_per_signer { + ids.push(format!("{key_id}")); + key_id += 1; + } + if i + 1 == num_signers { + for _ in 0..num_keys % num_signers { + // We have requested a number of keys that cannot fit evenly into the number of signers + // Append the remaining keys to the last signer + ids.push(format!("{key_id}")); + key_id += 1; + debug!("Appending extra key to last signer..."); + } + } + key_ids.push(ids.join(", ")); + } + let signer_ecdsa_private_keys = (0..num_signers) + .map(|_| Scalar::random(&mut rng)) + .collect::>(); + + let mut signer_config_tomls = vec![]; + let mut signers_array = String::new(); + signers_array += "signers = ["; + for (i, private_key) in signer_ecdsa_private_keys.iter().enumerate() { + let ecdsa_public_key = ecdsa::PublicKey::new(private_key).unwrap().to_string(); + let ids = key_ids[i].clone(); + signers_array += &format!( + r#" + {{public_key = "{ecdsa_public_key}", key_ids = [{ids}]}} + "# + ); + if i != signer_ecdsa_private_keys.len() - 1 { + signers_array += ","; + } + } + signers_array += "]"; + let mut port = 30000; + for (i, stacks_private_key) in signer_stacks_private_keys.iter().enumerate() { + let endpoint = format!("localhost:{}", port); + port += 1; + let id = i; + let message_private_key = signer_ecdsa_private_keys[i].to_string(); + let stacks_private_key = stacks_private_key.to_hex(); + let signer_config_toml = format!( + r#" +message_private_key = "{message_private_key}" +stacks_private_key = "{stacks_private_key}" +node_host = "{node_host}" +endpoint = "{endpoint}" +network = "testnet" +stackerdb_contract_id = "{contract_id}" +signer_id = {id} +{signers_array} +"# + ); + signer_config_tomls.push(signer_config_toml); + } + signer_config_tomls +} + +/// Helper function for building a stackerdb contract from the provided signer private keys +pub fn build_stackerdb_contract(signer_stacks_addresses: &[StacksAddress]) -> String { + let mut stackerdb_contract = String::new(); // " + stackerdb_contract += " ;; stacker DB\n"; + stackerdb_contract += " (define-read-only (stackerdb-get-signer-slots)\n"; + stackerdb_contract += " (ok (list\n"; + for signer_stacks_address in signer_stacks_addresses { + stackerdb_contract += " {\n"; + stackerdb_contract += + format!(" signer: '{},\n", signer_stacks_address).as_str(); + stackerdb_contract += format!(" num-slots: u{}\n", 10).as_str(); // We only have 10 Message Types + stackerdb_contract += " }\n"; + } + stackerdb_contract += " )))\n"; + stackerdb_contract += "\n"; + stackerdb_contract += " (define-read-only (stackerdb-get-config)\n"; + stackerdb_contract += " (ok {\n"; + stackerdb_contract += " chunk-size: u4096,\n"; + stackerdb_contract += " write-freq: u0,\n"; + stackerdb_contract += " max-writes: u4096,\n"; + stackerdb_contract += " max-neighbors: u32,\n"; + stackerdb_contract += " hint-replicas: (list )\n"; + stackerdb_contract += " }))\n"; + stackerdb_contract += " "; + stackerdb_contract +} diff --git a/testnet/stacks-node/Cargo.toml b/testnet/stacks-node/Cargo.toml index 45d026cf87c..4d63c0cf772 100644 --- a/testnet/stacks-node/Cargo.toml +++ b/testnet/stacks-node/Cargo.toml @@ -38,11 +38,7 @@ clarity = { path = "../../clarity", features = ["default", "testing"]} stacks-common = { path = "../../stacks-common", features = ["default", "testing"] } stacks = { package = "stackslib", path = "../../stackslib", features = ["default", "testing"] } stacks-signer = { path = "../../stacks-signer" } -rand_core = "0.6" p256k1 = "5.4.1" -wsts = "1.2" -bincode = "1.3.3" -hashbrown = "0.14" [dev-dependencies.rusqlite] version = "=0.24.2" diff --git a/testnet/stacks-node/src/tests/signer.rs b/testnet/stacks-node/src/tests/signer.rs index ede535425ef..d5631d10924 100644 --- a/testnet/stacks-node/src/tests/signer.rs +++ b/testnet/stacks-node/src/tests/signer.rs @@ -18,12 +18,11 @@ use crate::{ }; use clarity::vm::types::QualifiedContractIdentifier; use libsigner::{RunningSigner, Signer, StackerDBEventReceiver}; -use p256k1::{ecdsa, point::Point, scalar::Scalar}; -use rand_core::OsRng; +use p256k1::point::Point; use stacks::chainstate::stacks::StacksPrivateKey; +use stacks_common::types::chainstate::StacksAddress; use stacks_signer::runloop::RunLoopCommand; - -const SLOTS_PER_USER: u32 = 16; +use stacks_signer::utils::{build_signer_config_tomls, build_stackerdb_contract}; // Helper struct for holding the btc and stx neon nodes #[allow(dead_code)] @@ -34,101 +33,6 @@ struct RunningNodes { pub conf: NeonConfig, } -fn build_contract(num_signers: u32, signer_stacks_private_keys: &[StacksPrivateKey]) -> String { - let mut stackerdb_contract = String::new(); // " - stackerdb_contract += " ;; stacker DB\n"; - stackerdb_contract += " (define-read-only (stackerdb-get-signer-slots)\n"; - stackerdb_contract += " (ok (list\n"; - for i in 0..num_signers { - stackerdb_contract += " {\n"; - stackerdb_contract += format!( - " signer: '{},\n", - to_addr(&signer_stacks_private_keys[i as usize]) - ) - .as_str(); - stackerdb_contract += - format!(" num-slots: u{}\n", SLOTS_PER_USER).as_str(); - stackerdb_contract += " }\n"; - } - stackerdb_contract += " )))\n"; - stackerdb_contract += "\n"; - stackerdb_contract += " (define-read-only (stackerdb-get-config)\n"; - stackerdb_contract += " (ok {\n"; - stackerdb_contract += " chunk-size: u4096,\n"; - stackerdb_contract += " write-freq: u0,\n"; - stackerdb_contract += " max-writes: u4096,\n"; - stackerdb_contract += " max-neighbors: u32,\n"; - stackerdb_contract += " hint-replicas: (list )\n"; - stackerdb_contract += " }))\n"; - stackerdb_contract += " "; - - info!("stackerdb_contract:\n{}\n", &stackerdb_contract); - stackerdb_contract -} - -fn build_signer_config_tomls( - num_signers: u32, - signer_stacks_private_keys: &[StacksPrivateKey], - node_host: &str, - contract_id: String, -) -> Vec { - let mut rng = OsRng::default(); - let num_keys: u32 = 20; - let keys_per_signer = num_keys / num_signers; - let mut key_id: u32 = 1; - let mut key_ids = Vec::new(); - for _ in 0..num_signers { - let mut ids = Vec::new(); - for _ in 0..keys_per_signer { - ids.push(format!("{key_id}")); - key_id += 1; - } - key_ids.push(ids.join(", ")); - } - let signer_ecdsa_private_keys = (0..num_signers) - .map(|_| Scalar::random(&mut rng)) - .collect::>(); - - let mut signer_config_tomls = vec![]; - let mut signers_array = String::new(); - signers_array += "signers = ["; - for (i, private_key) in signer_ecdsa_private_keys.iter().enumerate() { - let ecdsa_public_key = ecdsa::PublicKey::new(private_key).unwrap().to_string(); - let ids = key_ids[i].clone(); - signers_array += &format!( - r#" - {{public_key = "{ecdsa_public_key}", key_ids = [{ids}]}} - "# - ); - if i != signer_ecdsa_private_keys.len() - 1 { - signers_array += ","; - } - } - signers_array += "]"; - let mut port = 30000; - for (i, stacks_private_key) in signer_stacks_private_keys.iter().enumerate() { - let endpoint = format!("localhost:{}", port); - port += 1; - let id = i; - let message_private_key = signer_ecdsa_private_keys[i].to_string(); - let stacks_private_key = stacks_private_key.to_hex(); - let signer_config_toml = format!( - r#" -message_private_key = "{message_private_key}" -stacks_private_key = "{stacks_private_key}" -node_host = "{node_host}" -endpoint = "{endpoint}" -network = "testnet" -stackerdb_contract_id = "{contract_id}" -signer_id = {id} -{signers_array} -"# - ); - signer_config_tomls.push(signer_config_toml); - } - signer_config_tomls -} - fn spawn_running_signer( data: &str, command: RunLoopCommand, @@ -244,30 +148,34 @@ fn setup_stx_btc_node( #[test] fn test_stackerdb_dkg() { + if env::var("BITCOIND_TEST") != Ok("1".into()) { + return; + } + // Generate Signer Data let num_signers: u32 = 5; + let num_keys: u32 = 20; let signer_stacks_private_keys = (0..num_signers) .map(|_| StacksPrivateKey::new()) .collect::>(); + let signer_stacks_addresses = signer_stacks_private_keys + .iter() + .map(|key| to_addr(key).into()) + .collect::>(); // Setup the neon node - if env::var("BITCOIND_TEST") != Ok("1".into()) { - return; - } let (mut conf, _) = neon_integration_test_conf(); // Build the stackerdb contract - let stackerdb_contract = build_contract(num_signers, &signer_stacks_private_keys); - let contract_id = QualifiedContractIdentifier::new( - to_addr(&signer_stacks_private_keys[0]).into(), - "hello-world".into(), - ); + let stackerdb_contract = build_stackerdb_contract(&signer_stacks_addresses); + let contract_id = + QualifiedContractIdentifier::new(signer_stacks_addresses[0].into(), "hello-world".into()); // Setup the signer and coordinator configurations let signer_configs = build_signer_config_tomls( - num_signers, &signer_stacks_private_keys, + num_keys, &conf.node.rpc_bind, - contract_id.to_string(), + &contract_id.to_string(), ); // The test starts here