Skip to content

Commit

Permalink
Merge pull request #5243 from stacks-network/chore/remove-v1-signer
Browse files Browse the repository at this point in the history
Chore/remove v1 signer
  • Loading branch information
kantai authored Sep 30, 2024
2 parents 9c90c5c + 8c0137c commit a50f955
Show file tree
Hide file tree
Showing 58 changed files with 405 additions and 8,451 deletions.
473 changes: 14 additions & 459 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ members = [
# Dependencies we want to keep the same between workspace members
[workspace.dependencies]
ed25519-dalek = { version = "2.1.1", features = ["serde", "rand_core"] }
hashbrown = "0.14.3"
hashbrown = { version = "0.14.3", features = ["serde"] }
rand_core = "0.6"
rand = "0.8"
rand_chacha = "0.3.1"
tikv-jemallocator = "0.5.4"
wsts = { version = "9.0.0", default-features = false }
rusqlite = { version = "0.31.0", features = ["blob", "serde_json", "i128_blob", "bundled", "trace"] }

# Use a bit more than default optimization for
Expand Down
1 change: 0 additions & 1 deletion libsigner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ stacks-common = { path = "../stacks-common" }
stackslib = { path = "../stackslib"}
thiserror = "1.0"
tiny_http = "0.12"
wsts = { workspace = true }

[dev-dependencies]
mutants = "0.0.3"
Expand Down
8 changes: 1 addition & 7 deletions libsigner/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::time::SystemTime;
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
use blockstack_lib::chainstate::stacks::boot::{MINERS_NAME, SIGNERS_NAME};
use blockstack_lib::chainstate::stacks::events::StackerDBChunksEvent;
use blockstack_lib::chainstate::stacks::{StacksTransaction, ThresholdSignature};
use blockstack_lib::chainstate::stacks::StacksTransaction;
use blockstack_lib::net::api::postblock_proposal::{
BlockValidateReject, BlockValidateResponse, ValidateRejectCode,
};
Expand All @@ -48,12 +48,6 @@ use stacks_common::util::HexError;
use tiny_http::{
Method as HttpMethod, Request as HttpRequest, Response as HttpResponse, Server as HttpServer,
};
use wsts::common::Signature;
use wsts::net::{
DkgBegin, DkgEnd, DkgEndBegin, DkgPrivateBegin, DkgPrivateShares, DkgPublicShares, DkgStatus,
Message, NonceRequest, NonceResponse, Packet, SignatureShareRequest, SignatureShareResponse,
};
use wsts::state_machine::signer;

use crate::http::{decode_http_body, decode_http_request};
use crate::EventError;
Expand Down
2 changes: 0 additions & 2 deletions libsigner/src/libsigner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ mod session;
mod signer_set;
/// v0 signer related code
pub mod v0;
/// v1 signer related code
pub mod v1;

use std::cmp::Eq;
use std::fmt::Debug;
Expand Down
43 changes: 9 additions & 34 deletions libsigner/src/runloop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,15 @@ const STDERR: i32 = 2;
/// Trait describing the needful components of a top-level runloop.
/// This is where the signer business logic would go.
/// Implement this, and you get all the multithreaded setup for free.
pub trait SignerRunLoop<R: Send, CMD: Send, T: SignerEventTrait> {
pub trait SignerRunLoop<R: Send, T: SignerEventTrait> {
/// Hint to set how long to wait for new events
fn set_event_timeout(&mut self, timeout: Duration);
/// Getter for the event poll timeout
fn get_event_timeout(&self) -> Duration;
/// Run one pass of the event loop, given new Signer events discovered since the last pass.
/// Returns Some(R) if this is the final pass -- the runloop evaluated to R
/// Returns None to keep running.
fn run_one_pass(
&mut self,
event: Option<SignerEvent<T>>,
cmd: Option<CMD>,
res: &Sender<R>,
) -> Option<R>;
fn run_one_pass(&mut self, event: Option<SignerEvent<T>>, res: &Sender<R>) -> Option<R>;

/// This is the main loop body for the signer. It continuously receives events from
/// `event_recv`, polling for up to `self.get_event_timeout()` units of time. Once it has
Expand All @@ -66,7 +61,6 @@ pub trait SignerRunLoop<R: Send, CMD: Send, T: SignerEventTrait> {
fn main_loop<EVST: EventStopSignaler>(
&mut self,
event_recv: Receiver<SignerEvent<T>>,
command_recv: Receiver<CMD>,
result_send: Sender<R>,
mut event_stop_signaler: EVST,
) -> Option<R> {
Expand All @@ -81,11 +75,7 @@ pub trait SignerRunLoop<R: Send, CMD: Send, T: SignerEventTrait> {
return None;
}
};
// Do not block for commands
let next_command_opt = command_recv.try_recv().ok();
if let Some(final_state) =
self.run_one_pass(next_event_opt, next_command_opt, &result_send)
{
if let Some(final_state) = self.run_one_pass(next_event_opt, &result_send) {
info!("Runloop exit; signaling event-receiver to stop");
event_stop_signaler.send();
return Some(final_state);
Expand All @@ -95,13 +85,11 @@ pub trait SignerRunLoop<R: Send, CMD: Send, T: SignerEventTrait> {
}

/// The top-level signer implementation
pub struct Signer<CMD, R, SL, EV, T> {
pub struct Signer<R, SL, EV, T> {
/// the runloop itself
signer_loop: Option<SL>,
/// the event receiver to use
event_receiver: Option<EV>,
/// the command receiver to use
command_receiver: Option<Receiver<CMD>>,
/// the result sender to use
result_sender: Option<Sender<R>>,
/// phantom data for the codec
Expand Down Expand Up @@ -193,31 +181,24 @@ pub fn set_runloop_signal_handler<ST: EventStopSignaler + Send + 'static>(mut st
}).expect("FATAL: failed to set signal handler");
}

impl<CMD, R, SL, EV, T> Signer<CMD, R, SL, EV, T> {
impl<R, SL, EV, T> Signer<R, SL, EV, T> {
/// Create a new signer with the given runloop and event receiver.
pub fn new(
runloop: SL,
event_receiver: EV,
command_receiver: Receiver<CMD>,
result_sender: Sender<R>,
) -> Signer<CMD, R, SL, EV, T> {
pub fn new(runloop: SL, event_receiver: EV, result_sender: Sender<R>) -> Signer<R, SL, EV, T> {
Signer {
signer_loop: Some(runloop),
event_receiver: Some(event_receiver),
command_receiver: Some(command_receiver),
result_sender: Some(result_sender),
phantom_data: PhantomData,
}
}
}

impl<
CMD: Send + 'static,
R: Send + 'static,
T: SignerEventTrait + 'static,
SL: SignerRunLoop<R, CMD, T> + Send + 'static,
SL: SignerRunLoop<R, T> + Send + 'static,
EV: EventReceiver<T> + Send + 'static,
> Signer<CMD, R, SL, EV, T>
> Signer<R, SL, EV, T>
{
/// This is a helper function to spawn both the runloop and event receiver in their own
/// threads. Advanced signers may not need this method, and instead opt to run the receiver
Expand All @@ -234,10 +215,6 @@ impl<
.event_receiver
.take()
.ok_or(EventError::AlreadyRunning)?;
let command_receiver = self
.command_receiver
.take()
.ok_or(EventError::AlreadyRunning)?;
let result_sender = self
.result_sender
.take()
Expand Down Expand Up @@ -266,9 +243,7 @@ impl<
let runloop_thread = thread::Builder::new()
.name(format!("signer_runloop:{bind_port}"))
.stack_size(THREAD_STACK_SIZE)
.spawn(move || {
signer_loop.main_loop(event_recv, command_receiver, result_sender, stop_signaler)
})
.spawn(move || signer_loop.main_loop(event_recv, result_sender, stop_signaler))
.map_err(|e| {
error!("SignerRunLoop failed to start: {:?}", &e);
ret_stop_signaler.send();
Expand Down
132 changes: 42 additions & 90 deletions libsigner/src/signer_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,125 +13,77 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

use std::collections::{BTreeMap, HashMap};

use blockstack_lib::chainstate::stacks::boot::NakamotoSignerEntry;
use hashbrown::{HashMap, HashSet};
use stacks_common::types::chainstate::{StacksAddress, StacksPublicKey};
use wsts::curve::ecdsa;
use wsts::curve::point::{Compressed, Point};
use wsts::state_machine::PublicKeys;

/// A reward set parsed into the structures required by WSTS party members and coordinators.
/// A reward set parsed into relevant structures
#[derive(Debug, Clone)]
pub struct SignerEntries {
/// The signer addresses mapped to signer id
pub signer_ids: HashMap<StacksAddress, u32>,
/// The signer ids mapped to public key and key ids mapped to public keys
pub public_keys: PublicKeys,
/// The signer ids mapped to key ids
pub signer_key_ids: HashMap<u32, Vec<u32>>,
/// The signer ids mapped to wsts public keys
pub signer_public_keys: HashMap<u32, Point>,
/// The signer ids mapped to a hash set of key ids
/// The wsts coordinator uses a hash set for each signer since it needs to do lots of lookups
pub coordinator_key_ids: HashMap<u32, HashSet<u32>>,
/// The signer addresses mapped to signer ID
pub signer_addr_to_id: HashMap<StacksAddress, u32>,
/// The signer IDs mapped to addresses. Uses a BTreeMap to ensure *reward cycle order*
pub signer_id_to_addr: BTreeMap<u32, StacksAddress>,
/// signer ID mapped to public key
pub signer_id_to_pk: HashMap<u32, StacksPublicKey>,
/// public_key mapped to signer ID
pub signer_pk_to_id: HashMap<StacksPublicKey, u32>,
/// The signer public keys
pub signer_pks: Vec<StacksPublicKey>,
/// The signer addresses
pub signer_addresses: Vec<StacksAddress>,
/// The signer address mapped to signing weight
pub signer_addr_to_weight: HashMap<StacksAddress, u32>,
}

/// Parsing errors for `SignerEntries`
#[derive(Debug)]
pub enum Error {
/// A member of the signing set has a signing key buffer
/// which does not represent a ecdsa public key.
/// which does not represent a valid Stacks public key
BadSignerPublicKey(String),
/// The number of signers was greater than u32::MAX
SignerCountOverflow,
}

impl SignerEntries {
/// Try to parse the reward set defined by `NakamotoSignEntry` into the structures required
/// by WSTS party members and coordinators.
/// Try to parse the reward set defined by `NakamotoSignEntry` into the SignerEntries struct
pub fn parse(is_mainnet: bool, reward_set: &[NakamotoSignerEntry]) -> Result<Self, Error> {
let mut weight_end = 1;
let mut signer_key_ids = HashMap::with_capacity(reward_set.len());
let mut signer_public_keys = HashMap::with_capacity(reward_set.len());
let mut coordinator_key_ids = HashMap::with_capacity(4000);
let mut signer_ids = HashMap::with_capacity(reward_set.len());
let mut wsts_signers = HashMap::new();
let mut wsts_key_ids = HashMap::new();
let mut signer_pk_to_id = HashMap::with_capacity(reward_set.len());
let mut signer_id_to_pk = HashMap::with_capacity(reward_set.len());
let mut signer_addr_to_id = HashMap::with_capacity(reward_set.len());
let mut signer_pks = Vec::with_capacity(reward_set.len());
let mut signer_id_to_addr = BTreeMap::new();
let mut signer_addr_to_weight = HashMap::new();
let mut signer_addresses = Vec::with_capacity(reward_set.len());
for (i, entry) in reward_set.iter().enumerate() {
let signer_id = u32::try_from(i).map_err(|_| Error::SignerCountOverflow)?;
let ecdsa_pk =
ecdsa::PublicKey::try_from(entry.signing_key.as_slice()).map_err(|e| {
Error::BadSignerPublicKey(format!(
"Failed to convert signing key to ecdsa::PublicKey: {e}"
))
})?;
let signer_public_key = Point::try_from(&Compressed::from(ecdsa_pk.to_bytes()))
.map_err(|e| {
Error::BadSignerPublicKey(format!(
"Failed to convert signing key to wsts::Point: {e}"
))
})?;
let stacks_public_key = StacksPublicKey::from_slice(entry.signing_key.as_slice())
let signer_public_key = StacksPublicKey::from_slice(entry.signing_key.as_slice())
.map_err(|e| {
Error::BadSignerPublicKey(format!(
"Failed to convert signing key to StacksPublicKey: {e}"
))
})?;

let stacks_address = StacksAddress::p2pkh(is_mainnet, &stacks_public_key);
signer_ids.insert(stacks_address, signer_id);

signer_public_keys.insert(signer_id, signer_public_key);
let weight_start = weight_end;
weight_end = weight_start + entry.weight;
let key_ids: HashSet<u32> = (weight_start..weight_end).collect();
for key_id in key_ids.iter() {
wsts_key_ids.insert(*key_id, ecdsa_pk);
}
signer_key_ids.insert(signer_id, (weight_start..weight_end).collect());
coordinator_key_ids.insert(signer_id, key_ids);
wsts_signers.insert(signer_id, ecdsa_pk);
let stacks_address = StacksAddress::p2pkh(is_mainnet, &signer_public_key);
signer_addr_to_id.insert(stacks_address, signer_id);
signer_id_to_pk.insert(signer_id, signer_public_key);
signer_pk_to_id.insert(signer_public_key, signer_id);
signer_pks.push(signer_public_key);
signer_id_to_addr.insert(signer_id, stacks_address);
signer_addr_to_weight.insert(stacks_address, entry.weight);
signer_addresses.push(stacks_address);
}

Ok(Self {
signer_ids,
public_keys: PublicKeys {
signers: wsts_signers,
key_ids: wsts_key_ids,
},
signer_key_ids,
signer_public_keys,
coordinator_key_ids,
signer_addr_to_id,
signer_id_to_pk,
signer_pk_to_id,
signer_pks,
signer_id_to_addr,
signer_addr_to_weight,
signer_addresses,
})
}

/// Return the number of Key IDs in the WSTS group signature
pub fn count_keys(&self) -> Result<u32, Error> {
self.public_keys
.key_ids
.len()
.try_into()
.map_err(|_| Error::SignerCountOverflow)
}

/// Return the number of Key IDs in the WSTS group signature
pub fn count_signers(&self) -> Result<u32, Error> {
self.public_keys
.signers
.len()
.try_into()
.map_err(|_| Error::SignerCountOverflow)
}

/// Return the number of Key IDs required to sign a message with the WSTS group signature
pub fn get_signing_threshold(&self) -> Result<u32, Error> {
let num_keys = self.count_keys()?;
Ok((num_keys as f64 * 7_f64 / 10_f64).ceil() as u32)
}

/// Return the number of Key IDs required to sign a message with the WSTS group signature
pub fn get_dkg_threshold(&self) -> Result<u32, Error> {
let num_keys = self.count_keys()?;
Ok((num_keys as f64 * 9_f64 / 10_f64).ceil() as u32)
}
}
12 changes: 6 additions & 6 deletions libsigner/src/tests/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::http::{decode_http_body, decode_http_request, decode_http_response, r

#[test]
fn test_decode_http_request_ok() {
let tests = vec![
let tests = [
("GET /foo HTTP/1.1\r\nHost: localhost:6270\r\n\r\n",
("GET", "/foo", vec![("host", "localhost:6270")])),
("POST asdf HTTP/1.1\r\nHost: core.blockstack.org\r\nFoo: Bar\r\n\r\n",
Expand Down Expand Up @@ -61,7 +61,7 @@ fn test_decode_http_request_ok() {

#[test]
fn test_decode_http_request_err() {
let tests = vec![
let tests = [
(
"GET /foo HTTP/1.1\r\n",
EventError::Deserialize("".to_string()),
Expand Down Expand Up @@ -99,7 +99,7 @@ fn test_decode_http_request_err() {

#[test]
fn test_decode_http_response_ok() {
let tests = vec![
let tests = [
("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: 123\r\nX-Request-ID: 0\r\n\r\n",
vec![("content-type", "application/octet-stream"), ("content-length", "123"), ("x-request-id", "0")]),
("HTTP/1.1 200 Ok\r\nContent-Type: application/octet-stream\r\nTransfer-encoding: chunked\r\nX-Request-ID: 0\r\n\r\n",
Expand All @@ -123,7 +123,7 @@ fn test_decode_http_response_ok() {

#[test]
fn test_decode_http_response_err() {
let tests = vec![
let tests = [
("HTTP/1.1 400 Bad Request\r\nContent-Type: application/json\r\nContent-Length: 456\r\nFoo: Bar\r\nX-Request-ID: 0\r\n\r\n",
RPCError::HttpError(400)),
("HTTP/1.1 200",
Expand Down Expand Up @@ -223,7 +223,7 @@ impl Write for MockHTTPSocket {

#[test]
fn test_run_http_request_with_body() {
let tests = vec![
let tests = [
("GET", "/test-no-content-type-and-no-body", None, vec![]),
(
"GET",
Expand Down Expand Up @@ -288,7 +288,7 @@ fn test_run_http_request_with_body() {

#[test]
fn test_run_http_request_no_body() {
let tests = vec![
let tests = [
("GET", "/test-no-content-type-and-no-body", None, vec![]),
(
"GET",
Expand Down
Loading

0 comments on commit a50f955

Please sign in to comment.