Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pull current stacks signer out into v1 implementation and create placeholder v0 mod #4778

Merged
merged 3 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ jobs:
- tests::signer::stackerdb_sign_request_rejected
- tests::signer::stackerdb_block_proposal
- tests::signer::stackerdb_filter_bad_transactions
- tests::signer::stackerdb_mine_2_nakamoto_reward_cycles
- tests::signer::stackerdb_sign_after_signer_reboot
# TODO: enable these once v1 signer is fixed
# - tests::signer::stackerdb_mine_2_nakamoto_reward_cycles
# - tests::signer::stackerdb_sign_after_signer_reboot
- tests::nakamoto_integrations::stack_stx_burn_op_integration_test
- tests::signer::stackerdb_delayed_dkg
# Do not run this one until we figure out why it fails in CI
Expand Down
1 change: 1 addition & 0 deletions libsigner/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub trait SignerSession {
}

/// signer session for a stackerdb instance
#[derive(Debug)]
pub struct StackerDBSession {
/// host we're talking to
pub host: String,
Expand Down
1 change: 0 additions & 1 deletion stacks-signer/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ pub(crate) mod tests {

use super::*;
use crate::config::{GlobalConfig, SignerConfig};
use crate::signer::SignerSlotID;

pub struct MockServerClient {
pub server: TcpListener,
Expand Down
12 changes: 11 additions & 1 deletion stacks-signer/src/client/stackerdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ use wsts::net::Packet;
use super::ClientError;
use crate::client::retry_with_exponential_backoff;
use crate::config::SignerConfig;
use crate::signer::SignerSlotID;

/// The signer StackerDB slot ID, purposefully wrapped to prevent conflation with SignerID
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
pub struct SignerSlotID(pub u32);

impl std::fmt::Display for SignerSlotID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

/// The StackerDB client for communicating with the .signers contract
#[derive(Debug)]
pub struct StackerDB {
/// The stacker-db sessions for each signer set and message type.
/// Maps message ID to the DB session.
Expand Down
2 changes: 1 addition & 1 deletion stacks-signer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use stacks_common::types::chainstate::{StacksAddress, StacksPrivateKey, StacksPu
use stacks_common::types::PrivateKey;
use wsts::curve::scalar::Scalar;

use crate::signer::SignerSlotID;
use crate::client::SignerSlotID;

const EVENT_TIMEOUT_MS: u64 = 5000;
// Default transaction fee to use in microstacks (if unspecificed in the config file)
Expand Down
48 changes: 40 additions & 8 deletions stacks-signer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,46 @@ pub mod cli;
pub mod client;
/// The configuration module for the signer
pub mod config;
/// The coordinator selector for the signer
pub mod coordinator;
/// The monitoring server for the signer
pub mod monitoring;
/// The primary runloop for the signer
pub mod runloop;
/// The signer module for processing events
pub mod signer;
/// The state module for the signer
pub mod signerdb;
/// The v0 implementation of the signer. This does not include WSTS support
pub mod v0;
/// The v1 implementation of the singer. This includes WSTS support
pub mod v1;

/// The monitoring server for the signer
pub mod monitoring;
use std::fmt::{Debug, Display};
use std::sync::mpsc::Sender;

use libsigner::SignerEvent;
use wsts::state_machine::OperationResult;

use crate::client::StacksClient;
use crate::config::SignerConfig;
use crate::runloop::RunLoopCommand;

/// A trait which provides a common `Signer` interface for `v1` and `v2`
pub trait Signer: Debug + Display {
/// Create a new `Signer` instance
fn new(config: SignerConfig) -> Self;
/// Update the `Signer` instance's next reward cycle data with the latest `SignerConfig`
fn update_next_signer_data(&mut self, next_signer_config: &SignerConfig);
/// Get the reward cycle of the signer
fn reward_cycle(&self) -> u64;
/// Process an event
fn process_event(
&mut self,
stacks_client: &StacksClient,
event: Option<&SignerEvent>,
res: Sender<Vec<OperationResult>>,
current_reward_cycle: u64,
);
/// Process a command
fn process_command(
&mut self,
stacks_client: &StacksClient,
current_reward_cycle: u64,
command: Option<RunLoopCommand>,
);
}
23 changes: 14 additions & 9 deletions stacks-signer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@ use stacks_signer::cli::{
};
use stacks_signer::config::GlobalConfig;
use stacks_signer::runloop::{RunLoop, RunLoopCommand};
use stacks_signer::v1;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
use wsts::state_machine::OperationResult;

struct SpawnedSigner {
running_signer: RunningSigner<SignerEventReceiver, Vec<OperationResult>>,
cmd_send: Sender<RunLoopCommand>,
res_recv: Receiver<Vec<OperationResult>>,
_cmd_send: Sender<RunLoopCommand>,
_res_recv: Receiver<Vec<OperationResult>>,
}

/// Create a new stacker db session
Expand Down Expand Up @@ -82,21 +83,25 @@ fn spawn_running_signer(path: &PathBuf) -> SpawnedSigner {
let config = GlobalConfig::try_from(path).unwrap();
let endpoint = config.endpoint;
info!("Starting signer with config: {}", config);
let (cmd_send, cmd_recv) = channel();
let (res_send, res_recv) = channel();
let (_cmd_send, cmd_recv) = channel();
let (res_send, _res_recv) = channel();
let ev = SignerEventReceiver::new(config.network.is_mainnet());
#[cfg(feature = "monitoring_prom")]
{
stacks_signer::monitoring::start_serving_monitoring_metrics(config.clone()).ok();
}
let runloop = RunLoop::from(config);
let mut signer: Signer<RunLoopCommand, Vec<OperationResult>, RunLoop, SignerEventReceiver> =
Signer::new(runloop, ev, cmd_recv, res_send);
let runloop = RunLoop::new(config);
let mut signer: Signer<
RunLoopCommand,
Vec<OperationResult>,
RunLoop<v1::signer::Signer>,
SignerEventReceiver,
> = Signer::new(runloop, ev, cmd_recv, res_send);
let running_signer = signer.spawn(endpoint).unwrap();
SpawnedSigner {
running_signer,
cmd_send,
res_recv,
_cmd_send,
_res_recv,
}
}

Expand Down
106 changes: 37 additions & 69 deletions stacks-signer/src/runloop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,32 @@ use blockstack_lib::burnchains::PoxConstants;
use blockstack_lib::chainstate::stacks::boot::SIGNERS_NAME;
use blockstack_lib::util_lib::boot::boot_code_id;
use hashbrown::HashMap;
use libsigner::{SignerEntries, SignerEvent, SignerRunLoop};
use libsigner::{BlockProposalSigners, SignerEntries, SignerEvent, SignerRunLoop};
use slog::{slog_debug, slog_error, slog_info, slog_warn};
use stacks_common::types::chainstate::StacksAddress;
use stacks_common::{debug, error, info, warn};
use wsts::common::MerkleRoot;
use wsts::state_machine::OperationResult;

use crate::client::{retry_with_exponential_backoff, ClientError, StacksClient};
use crate::client::{retry_with_exponential_backoff, ClientError, SignerSlotID, StacksClient};
use crate::config::{GlobalConfig, SignerConfig};
use crate::signer::{Command as SignerCommand, Signer, SignerSlotID};
use crate::Signer as SignerTrait;

/// Which signer operation to perform
#[derive(PartialEq, Clone, Debug)]
pub enum SignerCommand {
/// Generate a DKG aggregate public key
Dkg,
/// Sign a message
Sign {
/// The block to sign over
block_proposal: BlockProposalSigners,
/// Whether to make a taproot signature
is_taproot: bool,
/// Taproot merkle root
merkle_root: Option<MerkleRoot>,
},
}

/// Which operation to perform
#[derive(PartialEq, Clone, Debug)]
Expand Down Expand Up @@ -101,7 +118,7 @@ impl RewardCycleInfo {
}

/// The runloop for the stacks signer
pub struct RunLoop {
pub struct RunLoop<Signer: SignerTrait> {
/// Configuration info
pub config: GlobalConfig,
/// The stacks node client
Expand All @@ -117,9 +134,9 @@ pub struct RunLoop {
pub current_reward_cycle_info: Option<RewardCycleInfo>,
}

impl From<GlobalConfig> for RunLoop {
/// Creates new runloop from a config
fn from(config: GlobalConfig) -> Self {
impl<Signer: SignerTrait> RunLoop<Signer> {
/// Create a new signer runloop from the provided configuration
pub fn new(config: GlobalConfig) -> Self {
let stacks_client = StacksClient::from(&config);
Self {
config,
Expand All @@ -130,9 +147,6 @@ impl From<GlobalConfig> for RunLoop {
current_reward_cycle_info: None,
}
}
}

impl RunLoop {
/// Get the registered signers for a specific reward cycle
/// Returns None if no signers are registered or its not Nakamoto cycle
pub fn get_parsed_reward_set(
Expand Down Expand Up @@ -237,20 +251,14 @@ impl RunLoop {
let prior_reward_cycle = reward_cycle.saturating_sub(1);
let prior_reward_set = prior_reward_cycle % 2;
if let Some(signer) = self.stacks_signers.get_mut(&prior_reward_set) {
if signer.reward_cycle == prior_reward_cycle {
if signer.reward_cycle() == prior_reward_cycle {
// The signers have been calculated for the next reward cycle. Update the current one
debug!("{signer}: Next reward cycle ({reward_cycle}) signer set calculated. Reconfiguring current reward cycle signer.");
signer.next_signer_addresses = new_signer_config
.signer_entries
.signer_ids
.keys()
.copied()
.collect();
signer.next_signer_slot_ids = new_signer_config.signer_slot_ids.clone();
signer.update_next_signer_data(&new_signer_config);
}
}
}
let new_signer = Signer::from(new_signer_config);
let new_signer = Signer::new(new_signer_config);
info!("{new_signer} initialized.");
self.stacks_signers.insert(reward_index, new_signer);
} else {
Expand Down Expand Up @@ -318,7 +326,7 @@ impl RunLoop {
if self
.stacks_signers
.get(&(next_reward_cycle % 2))
.map(|signer| signer.reward_cycle != next_reward_cycle)
.map(|signer| signer.reward_cycle() != next_reward_cycle)
.unwrap_or(true)
{
info!("Received a new burnchain block height ({current_burn_block_height}) in the prepare phase of the next reward cycle ({next_reward_cycle}). Checking for signer registration...");
Expand All @@ -337,7 +345,7 @@ impl RunLoop {
fn cleanup_stale_signers(&mut self, current_reward_cycle: u64) {
let mut to_delete = Vec::new();
for (idx, signer) in &mut self.stacks_signers {
if signer.reward_cycle < current_reward_cycle {
if signer.reward_cycle() < current_reward_cycle {
debug!("{signer}: Signer's tenure has completed.");
to_delete.push(*idx);
continue;
Expand All @@ -349,7 +357,7 @@ impl RunLoop {
}
}

impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
impl<Signer: SignerTrait> SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop<Signer> {
fn set_event_timeout(&mut self, timeout: Duration) {
self.config.event_timeout = timeout;
}
Expand Down Expand Up @@ -399,58 +407,18 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
return None;
}
for signer in self.stacks_signers.values_mut() {
let event_parity = match event {
Some(SignerEvent::BlockValidationResponse(_)) => Some(current_reward_cycle % 2),
// Block proposal events do have reward cycles, but each proposal has its own cycle,
// and the vec could be heterogenous, so, don't differentiate.
Some(SignerEvent::MinerMessages(..))
| Some(SignerEvent::NewBurnBlock(_))
| Some(SignerEvent::StatusCheck)
| None => None,
Some(SignerEvent::SignerMessages(msg_parity, ..)) => {
Some(u64::from(msg_parity) % 2)
}
};
let other_signer_parity = (signer.reward_cycle + 1) % 2;
if event_parity == Some(other_signer_parity) {
continue;
}
if signer.approved_aggregate_public_key.is_none() {
if let Err(e) =
signer.refresh_dkg(&self.stacks_client, res.clone(), current_reward_cycle)
{
error!("{signer}: failed to refresh DKG: {e}");
}
}
signer.refresh_coordinator();
if let Err(e) = signer.process_event(
signer.process_event(
&self.stacks_client,
event.as_ref(),
res.clone(),
current_reward_cycle,
) {
error!("{signer}: errored processing event: {e}");
}
if let Some(command) = self.commands.pop_front() {
let reward_cycle = command.reward_cycle;
if signer.reward_cycle != reward_cycle {
warn!(
"{signer}: not registered for reward cycle {reward_cycle}. Ignoring command: {command:?}"
);
} else {
info!(
"{signer}: Queuing an external runloop command ({:?}): {command:?}",
signer
.state_machine
.public_keys
.signers
.get(&signer.signer_id)
);
signer.commands.push_back(command.command);
}
}
);
// After processing event, run the next command for each signer
signer.process_next_command(&self.stacks_client, current_reward_cycle);
signer.process_command(
&self.stacks_client,
current_reward_cycle,
self.commands.pop_front(),
);
}
None
}
Expand Down
15 changes: 15 additions & 0 deletions stacks-signer/src/v0/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
// Copyright (C) 2020-2024 Stacks Open Internet Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
22 changes: 22 additions & 0 deletions stacks-signer/src/v1/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
// Copyright (C) 2020-2024 Stacks Open Internet Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

/// The coordinator selector for the signer
pub mod coordinator;
/// The signer module for processing events
pub mod signer;
/// The state module for the signer
pub mod signerdb;
Loading