diff --git a/README.md b/README.md
index 58bfe2a01..73293dafa 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
![Webb Logo](./assets/webb_banner_light.png#gh-light-mode-only)
![Webb Logo](./assets/webb_banner_dark.png#gh-dark-mode-only)
@@ -29,7 +29,7 @@
Testing
Contributing
License
-
+
Getting Started 🎉
@@ -131,3 +131,13 @@ If you have a contribution in mind, please check out our [Contribution Guide](./
Licensed under
GNU General Public License v3.0.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the GNU General Public License v3.0 license, shall be licensed as above, without any additional terms or conditions.
+
+
+## Troubleshooting
+The linking phase may fail due to not finding libgmp (i.e., "could not find library -lgmp") when building on a mac M1. To fix this problem, run:
+
+```bash
+brew install gmp
+# make sure to run the commands below each time when starting a new env, or, append them to .zshrc
+export LIBRARY_PATH=$LIBRARY_PATH:/opt/homebrew/lib
+export INCLUDE_PATH=$INCLUDE_PATH:/opt/homebrew/include
diff --git a/dkg-gadget/src/async_protocols/mod.rs b/dkg-gadget/src/async_protocols/mod.rs
index 790cef693..b7f52607a 100644
--- a/dkg-gadget/src/async_protocols/mod.rs
+++ b/dkg-gadget/src/async_protocols/mod.rs
@@ -680,6 +680,7 @@ where
recipient_id: maybe_recipient_id,
payload,
session_id: params.session_id,
+ ssid: params.handle.ssid,
};
if let Err(err) = params.engine.sign_and_send_msg(unsigned_dkg_message) {
params
diff --git a/dkg-gadget/src/async_protocols/remote.rs b/dkg-gadget/src/async_protocols/remote.rs
index 8da204078..6898b42fc 100644
--- a/dkg-gadget/src/async_protocols/remote.rs
+++ b/dkg-gadget/src/async_protocols/remote.rs
@@ -37,6 +37,8 @@ pub struct AsyncProtocolRemote
{
pub(crate) current_round_blame_tx: Arc>,
pub(crate) session_id: SessionId,
pub(crate) associated_block_id: u64,
+ /// The signing set index. For keygen, this is always 0
+ pub(crate) ssid: u8,
pub(crate) logger: DebugLogger,
status_history: Arc>>,
}
@@ -74,6 +76,7 @@ impl Clone for AsyncProtocolRemote {
logger: self.logger.clone(),
status_history: self.status_history.clone(),
associated_block_id: self.associated_block_id,
+ ssid: self.ssid,
}
}
}
@@ -101,6 +104,7 @@ impl AsyncProtocolRemote {
session_id: SessionId,
logger: DebugLogger,
associated_block_id: u64,
+ ssid: u8,
) -> Self {
let (stop_tx, stop_rx) = tokio::sync::mpsc::unbounded_channel();
let (tx_keygen_signing, rx_keygen_signing) = tokio::sync::mpsc::unbounded_channel();
@@ -113,31 +117,6 @@ impl AsyncProtocolRemote {
let status = Arc::new(Atomic::new(MetaHandlerStatus::Beginning));
let status_history = Arc::new(Mutex::new(vec![MetaHandlerStatus::Beginning]));
- // let status_debug = status.clone();
- // let status_history_debug = status_history.clone();
- // let logger_debug = logger.clone();
-
- // The purpose of this task is to log the status of the meta handler
- // in the case that it is stalled/not-progressing. This is useful for debugging.
- // tokio::task::spawn(async move {
- // loop {
- // tokio::time::sleep(std::time::Duration::from_secs(2)).await;
- // let status = status_debug.load(Ordering::Relaxed);
- // if [MetaHandlerStatus::Terminated, MetaHandlerStatus::Complete].contains(&status) {
- // break
- // }
- // let status_history = status_history_debug.lock();
-
- // if status == MetaHandlerStatus::Beginning && status_history.len() == 1 {
- // continue
- // }
-
- // logger_debug.debug(format!(
- // "AsyncProtocolRemote status: {status:?} ||||| history: {status_history:?} |||||
- // session_id: {session_id:?}", ));
- // }
- // });
-
Self {
status,
tx_keygen_signing,
@@ -156,6 +135,7 @@ impl AsyncProtocolRemote {
is_primary_remote: false,
session_id,
associated_block_id,
+ ssid,
}
}
diff --git a/dkg-gadget/src/async_protocols/sign/handler.rs b/dkg-gadget/src/async_protocols/sign/handler.rs
index c16e1467f..2579bd115 100644
--- a/dkg-gadget/src/async_protocols/sign/handler.rs
+++ b/dkg-gadget/src/async_protocols/sign/handler.rs
@@ -255,6 +255,7 @@ where
recipient_id: None,
payload,
session_id: params.session_id,
+ ssid: params.handle.ssid,
};
params.engine.sign_and_send_msg(unsigned_dkg_message.clone())?;
diff --git a/dkg-gadget/src/dkg_modules/mod.rs b/dkg-gadget/src/dkg_modules/mod.rs
index bb4cac9b0..0d0192310 100644
--- a/dkg-gadget/src/dkg_modules/mod.rs
+++ b/dkg-gadget/src/dkg_modules/mod.rs
@@ -54,6 +54,7 @@ pub enum SigningProtocolSetupParameters {
>,
signing_set: Vec,
associated_block_id: NumberFor,
+ ssid: u8,
},
WTFrost {},
}
diff --git a/dkg-gadget/src/dkg_modules/mp_ecdsa.rs b/dkg-gadget/src/dkg_modules/mp_ecdsa.rs
index 2e108fb3e..fd3597524 100644
--- a/dkg-gadget/src/dkg_modules/mp_ecdsa.rs
+++ b/dkg-gadget/src/dkg_modules/mp_ecdsa.rs
@@ -50,6 +50,7 @@ where
keygen_protocol_hash,
} = params
{
+ const KEYGEN_SSID: u8 = 0;
match self.dkg_worker.generate_async_proto_params(
best_authorities,
authority_public_key,
@@ -58,9 +59,10 @@ where
stage,
crate::DKG_KEYGEN_PROTOCOL_NAME,
associated_block,
+ KEYGEN_SSID,
) {
Ok(async_proto_params) => {
- let err_handler_tx = self.dkg_worker.error_handler.clone();
+ let err_handler_tx = self.dkg_worker.error_handler_channel.tx.clone();
let remote = async_proto_params.handle.clone();
let keygen_manager = self.dkg_worker.keygen_manager.clone();
@@ -152,6 +154,7 @@ where
unsigned_proposal_batch,
signing_set,
associated_block_id,
+ ssid,
} = params
{
self.dkg_worker.logger.debug(format!("{party_i:?} All Parameters: {best_authorities:?} | authority_pub_key: {authority_public_key:?} | session_id: {session_id:?} | threshold: {threshold:?} | stage: {stage:?} | unsigned_proposal_batch: {unsigned_proposal_batch:?} | signing_set: {signing_set:?} | associated_block_id: {associated_block_id:?}"));
@@ -163,11 +166,12 @@ where
stage,
crate::DKG_SIGNING_PROTOCOL_NAME,
associated_block_id,
+ ssid,
)?;
let handle = async_proto_params.handle.clone();
- let err_handler_tx = self.dkg_worker.error_handler.clone();
+ let err_handler_tx = self.dkg_worker.error_handler_channel.tx.clone();
let meta_handler = GenericAsyncHandler::setup_signing(
async_proto_params,
threshold,
diff --git a/dkg-gadget/src/gossip_messages/misbehaviour_report.rs b/dkg-gadget/src/gossip_messages/misbehaviour_report.rs
index 244cf446a..3df12afd0 100644
--- a/dkg-gadget/src/gossip_messages/misbehaviour_report.rs
+++ b/dkg-gadget/src/gossip_messages/misbehaviour_report.rs
@@ -150,6 +150,7 @@ where
recipient_id: None,
session_id: report.session_id,
payload,
+ ssid: 0,
};
let encoded_dkg_message = message.encode();
diff --git a/dkg-gadget/src/gossip_messages/public_key_gossip.rs b/dkg-gadget/src/gossip_messages/public_key_gossip.rs
index 7a2e1c094..e96ebe7cb 100644
--- a/dkg-gadget/src/gossip_messages/public_key_gossip.rs
+++ b/dkg-gadget/src/gossip_messages/public_key_gossip.rs
@@ -149,6 +149,7 @@ pub(crate) fn gossip_public_key(
recipient_id: None,
session_id: msg.session_id,
payload,
+ ssid: 0,
};
let encoded_dkg_message = message.encode();
diff --git a/dkg-gadget/src/lib.rs b/dkg-gadget/src/lib.rs
index b1bc304f9..d2e3dd294 100644
--- a/dkg-gadget/src/lib.rs
+++ b/dkg-gadget/src/lib.rs
@@ -34,7 +34,7 @@ pub mod keystore;
pub mod gossip_engine;
mod keygen_manager;
-mod signing_manager;
+pub mod signing_manager;
// mod meta_async_rounds;
pub mod db;
mod metrics;
diff --git a/dkg-gadget/src/signing_manager/mod.rs b/dkg-gadget/src/signing_manager/mod.rs
index 07a454681..5a12dcaba 100644
--- a/dkg-gadget/src/signing_manager/mod.rs
+++ b/dkg-gadget/src/signing_manager/mod.rs
@@ -56,6 +56,7 @@ const MAX_RUNNING_TASKS: usize = 4;
const MAX_ENQUEUED_TASKS: usize = 20;
// How often to poll the jobs to check completion status
const JOB_POLL_INTERVAL_IN_MILLISECONDS: u64 = 500;
+pub const MAX_POTENTIAL_SIGNING_SETS_PER_PROPOSAL: u8 = 2;
impl SigningManager
where
@@ -200,65 +201,70 @@ where
if we are not, we continue the loop.
*/
let unsigned_proposal_bytes = batch.encode();
- let concat_data = dkg_pub_key
- .clone()
- .into_iter()
- //.chain(at.encode())
- .chain(unsigned_proposal_bytes)
- .collect::>();
- let seed = sp_core::keccak_256(&concat_data);
let unsigned_proposal_hash = batch.hash().expect("unable to hash proposal");
- let maybe_set = self
- .generate_signers(&seed, threshold, best_authorities.clone(), dkg_worker)
- .ok();
- if let Some(signing_set) = maybe_set {
- // if we are in the set, send to work manager
- if signing_set.contains(&party_i) {
- dkg_worker.logger.info(format!(
- "🕸️ Session Id {:?} | {}-out-of-{} signers: ({:?})",
- session_id,
- threshold,
- best_authorities.len(),
- signing_set,
- ));
+ for ssid in 0..MAX_POTENTIAL_SIGNING_SETS_PER_PROPOSAL {
+ let concat_data = dkg_pub_key
+ .clone()
+ .into_iter()
+ .chain(unsigned_proposal_bytes.clone())
+ .chain(ssid.encode())
+ .collect::>();
+ let seed = sp_core::keccak_256(&concat_data);
- let params = SigningProtocolSetupParameters::MpEcdsa {
- best_authorities: best_authorities.clone(),
- authority_public_key: authority_public_key.clone(),
- party_i,
- session_id,
- threshold,
- stage: ProtoStageType::Signing { unsigned_proposal_hash },
- unsigned_proposal_batch: batch,
- signing_set,
- associated_block_id: *header.number(),
- };
+ let maybe_set = self
+ .generate_signers(&seed, threshold, best_authorities.clone(), dkg_worker)
+ .ok();
+ if let Some(signing_set) = maybe_set {
+ // if we are in the set, send to work manager
+ if signing_set.contains(&party_i) {
+ dkg_worker.logger.info(format!(
+ "🕸️ Session Id {:?} | SSID {} | {}-out-of-{} signers: ({:?})",
+ session_id,
+ ssid,
+ threshold,
+ best_authorities.len(),
+ signing_set,
+ ));
- let signing_protocol = dkg_worker
- .dkg_modules
- .get_signing_protocol(¶ms)
- .expect("Standard signing protocol should exist");
- match signing_protocol.initialize_signing_protocol(params).await {
- Ok((handle, task)) => {
- // Send task to the work manager. Force start if the type chain ID is
- // None, implying this is a proposal needed for rotating sessions and
- // thus a priority
- let force_start = typed_chain_id == TypedChainId::None;
- self.work_manager.push_task(
- unsigned_proposal_hash,
- force_start,
- handle,
- task,
- )?;
- },
- Err(err) => {
- dkg_worker
- .logger
- .error(format!("Error creating signing protocol: {:?}", &err));
- dkg_worker.handle_dkg_error(err.clone()).await;
- return Err(err)
- },
+ let params = SigningProtocolSetupParameters::MpEcdsa {
+ best_authorities: best_authorities.clone(),
+ authority_public_key: authority_public_key.clone(),
+ party_i,
+ session_id,
+ threshold,
+ stage: ProtoStageType::Signing { unsigned_proposal_hash },
+ unsigned_proposal_batch: batch.clone(),
+ signing_set,
+ associated_block_id: *header.number(),
+ ssid,
+ };
+
+ let signing_protocol = dkg_worker
+ .dkg_modules
+ .get_signing_protocol(¶ms)
+ .expect("Standard signing protocol should exist");
+ match signing_protocol.initialize_signing_protocol(params).await {
+ Ok((handle, task)) => {
+ // Send task to the work manager. Force start if the type chain ID
+ // is None, implying this is a proposal needed for rotating sessions
+ // and thus a priority
+ let force_start = typed_chain_id == TypedChainId::None;
+ self.work_manager.push_task(
+ unsigned_proposal_hash,
+ force_start,
+ handle,
+ task,
+ )?;
+ },
+ Err(err) => {
+ dkg_worker
+ .logger
+ .error(format!("Error creating signing protocol: {:?}", &err));
+ dkg_worker.handle_dkg_error(err.clone()).await;
+ return Err(err)
+ },
+ }
}
}
}
diff --git a/dkg-gadget/src/signing_manager/work_manager.rs b/dkg-gadget/src/signing_manager/work_manager.rs
index ea70b6c04..3566b10fa 100644
--- a/dkg-gadget/src/signing_manager/work_manager.rs
+++ b/dkg-gadget/src/signing_manager/work_manager.rs
@@ -42,7 +42,8 @@ pub struct WorkManager {
pub struct WorkManagerInner {
pub active_tasks: HashSet>,
pub enqueued_tasks: VecDeque>,
- pub enqueued_messages: HashMap<[u8; 32], VecDeque>>,
+ // task hash => SSID => enqueued messages
+ pub enqueued_messages: HashMap<[u8; 32], HashMap>>>,
}
#[derive(Debug)]
@@ -218,30 +219,41 @@ impl WorkManager {
// Next, remove any outdated enqueued messages to prevent RAM bloat
let mut to_remove = vec![];
for (hash, queue) in lock.enqueued_messages.iter_mut() {
- let before = queue.len();
- // Only keep the messages that are not outdated
- queue.retain(|msg| {
- associated_block_id_acceptable(now.saturated_into(), msg.msg.associated_block_id)
- });
- let after = queue.len();
-
- if before != after {
- self.logger.info(format!(
- "[worker] Removed {} outdated enqueued messages from the queue for {:?}",
- before - after,
- hex::encode(*hash)
- ));
- }
+ for (ssid, queue) in queue.iter_mut() {
+ let before = queue.len();
+ // Only keep the messages that are not outdated
+ queue.retain(|msg| {
+ associated_block_id_acceptable(
+ now.saturated_into(),
+ msg.msg.associated_block_id,
+ )
+ });
+ let after = queue.len();
+
+ if before != after {
+ self.logger.info(format!(
+ "[worker] Removed {} outdated enqueued messages from the queue for {:?}",
+ before - after,
+ hex::encode(*hash)
+ ));
+ }
- if queue.is_empty() {
- to_remove.push(*hash);
+ if queue.is_empty() {
+ to_remove.push((*hash, *ssid));
+ }
}
}
- // Finally, to prevent the existence of piling-up empty queues, remove them
- for hash in to_remove {
- lock.enqueued_messages.remove(&hash);
+ // Next, to prevent the existence of piling-up empty *inner* queues, remove them
+ for (hash, ssid) in to_remove {
+ lock.enqueued_messages
+ .get_mut(&hash)
+ .expect("Should be available")
+ .remove(&ssid);
}
+
+ // Finally, remove any empty outer maps
+ lock.enqueued_messages.retain(|_, v| !v.is_empty());
}
fn start_job_unconditional(&self, job: Job, lock: &mut WorkManagerInner) {
@@ -252,24 +264,33 @@ impl WorkManager {
.error(format!("Failed to start job {:?}: {err:?}", hex::encode(job.task_hash)));
} else {
// deliver all the enqueued messages to the protocol now
- if let Some(mut enqueued_messages) = lock.enqueued_messages.remove(&job.task_hash) {
- self.logger.info(format!(
- "Will now deliver {} enqueued message(s) to the async protocol for {:?}",
- enqueued_messages.len(),
- hex::encode(job.task_hash)
- ));
- while let Some(message) = enqueued_messages.pop_front() {
- if should_deliver(&job, &message, job.task_hash) {
- if let Err(err) = job.handle.deliver_message(message) {
- self.logger.error(format!(
- "Unable to deliver message for job {:?}: {err:?}",
- hex::encode(job.task_hash)
- ));
+ if let Some(mut enqueued_messages_map) = lock.enqueued_messages.remove(&job.task_hash) {
+ let job_ssid = job.handle.ssid;
+ if let Some(mut enqueued_messages) = enqueued_messages_map.remove(&job_ssid) {
+ self.logger.info(format!(
+ "Will now deliver {} enqueued message(s) to the async protocol for {:?}",
+ enqueued_messages.len(),
+ hex::encode(job.task_hash)
+ ));
+
+ while let Some(message) = enqueued_messages.pop_front() {
+ if should_deliver(&job, &message, job.task_hash) {
+ if let Err(err) = job.handle.deliver_message(message) {
+ self.logger.error(format!(
+ "Unable to deliver message for job {:?}: {err:?}",
+ hex::encode(job.task_hash)
+ ));
+ }
+ } else {
+ self.logger.warn("Will not deliver enqueued message to async protocol since the message is no longer acceptable")
}
- } else {
- self.logger.warn("Will not deliver enqueued message to async protocol since the message is no longer acceptable")
}
}
+
+ // If there are any other messages for other SSIDs, put them back in the map
+ if !enqueued_messages_map.is_empty() {
+ lock.enqueued_messages.insert(job.task_hash, enqueued_messages_map);
+ }
}
}
let task = job.task.clone();
@@ -336,7 +357,12 @@ impl WorkManager {
lock.enqueued_tasks.iter().map(|job| job.handle.session_id).collect();
self.logger
.info(format!("Enqueuing message for {:?} | current_running_session_ids: {current_running_session_ids:?} | enqueued_session_ids: {enqueued_session_ids:?}", hex::encode(message_task_hash)));
- lock.enqueued_messages.entry(message_task_hash).or_default().push_back(msg)
+ lock.enqueued_messages
+ .entry(message_task_hash)
+ .or_default()
+ .entry(msg.msg.ssid)
+ .or_default()
+ .push_back(msg)
}
}
@@ -399,6 +425,7 @@ fn should_deliver(
) -> bool {
task.handle.session_id == msg.msg.session_id &&
task.task_hash == message_task_hash &&
+ task.handle.ssid == msg.msg.ssid &&
associated_block_id_acceptable(
task.handle.associated_block_id,
msg.msg.associated_block_id,
diff --git a/dkg-gadget/src/worker.rs b/dkg-gadget/src/worker.rs
index c9135d464..bb71f4b57 100644
--- a/dkg-gadget/src/worker.rs
+++ b/dkg-gadget/src/worker.rs
@@ -27,7 +27,7 @@ use sp_consensus::SyncOracle;
use crate::signing_manager::SigningManager;
use futures::StreamExt;
use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2020::state_machine::keygen::LocalKey;
-use parking_lot::RwLock;
+use parking_lot::{Mutex, RwLock};
use sc_client_api::{Backend, FinalityNotification};
use sc_keystore::LocalKeystore;
use sp_arithmetic::traits::SaturatedConversion;
@@ -38,7 +38,7 @@ use std::{
marker::PhantomData,
sync::Arc,
};
-use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+use tokio::sync::mpsc::UnboundedSender;
use dkg_primitives::{
types::{DKGError, DKGMessage, NetworkMsgPayload, SessionId, SignedDKGMessage},
@@ -132,11 +132,8 @@ where
pub aggregated_public_keys: Shared,
/// Tracking for the misbehaviour reports
pub aggregated_misbehaviour_reports: Shared,
- pub misbehaviour_tx: Option>,
/// Concrete type that points to the actual local keystore if it exists
pub local_keystore: Shared