Repository: tangle-network/gadget (language: rust)

Issue:
[TASK] Add support for more protocols in the shell
- [x] [dfns-cggmp21](https://github.com/webb-tools/gadget/tree/main/protocols/dfns-cggmp21)
- [x] [zcash-frost](https://github.com/webb-tools/gadget/tree/main/protocols/zcash-frost)
- [x] [silent-shard-dkls23-ll](https://github.com/webb-tools/gadget/tree/main/protocols/silent-shard-dkls23-ll)
- [ ] [zk-saas](https://github.com/webb-tools/gadget/tree/main/protocols/zk-saas)
- [x] [bls](https://github.com/webb-tools/gadget/tree/main/protocols/bls)


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 6262835fb..2862b1a2a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4107,6 +4107,7 @@ dependencies = [
  "scale-info",
  "serde",
  "sha2 0.10.8",
+ "silent-shard-dkls23-ll-protocol",
  "sp-application-crypto",
  "sp-core",
  "sp-io",
@@ -4114,11 +4115,14 @@ dependencies = [
  "structopt",
  "tangle-subxt",
  "test-utils",
+ "threshold-bls-protocol",
  "tokio",
  "toml 0.8.11",
  "tracing",
  "tracing-subscriber 0.3.18",
  "url",
+ "zcash-frost-protocol",
+ "zk-saas-protocol",
 ]
 
 [[package]]
diff --git a/protocols/bls/src/constants.rs b/protocols/bls/src/constants.rs
new file mode 100644
index 000000000..ad04220a3
--- /dev/null
+++ b/protocols/bls/src/constants.rs
@@ -0,0 +1,4 @@
+pub const GENNARO_DKG_BLS_381_KEYGEN_PROTOCOL_NAME: &str =
+    "/tangle/gennaro-dkg-bls-381/keygen/1.0.0";
+pub const GENNARO_DKG_BLS_381_SIGNING_PROTOCOL_NAME: &str =
+    "/tangle/gennaro-dkg-bls-381/signing/1.0.0";
diff --git a/protocols/bls/src/lib.rs b/protocols/bls/src/lib.rs
index 6a51b9662..e914a60de 100644
--- a/protocols/bls/src/lib.rs
+++ b/protocols/bls/src/lib.rs
@@ -6,6 +6,7 @@ use gadget_common::{generate_protocol, generate_setup_and_run_command};
 use protocol::signing::BlsSigningAdditionalParams;
 use protocol_macros::protocol;
 
+pub mod constants;
 pub mod protocol;
 
 generate_protocol!(
diff --git a/protocols/silent-shard-dkls23-ll/src/constants.rs b/protocols/silent-shard-dkls23-ll/src/constants.rs
new file mode 100644
index 000000000..174359924
--- /dev/null
+++ b/protocols/silent-shard-dkls23-ll/src/constants.rs
@@ -0,0 +1,4 @@
+pub const SILENT_SHARED_DKLS23_KEYGEN_PROTOCOL_NAME: &str =
+    "/tangle/silent-shared-dkls23/keygen/1.0.0";
+pub const SILENT_SHARED_DKLS23_SIGNING_PROTOCOL_NAME: &str =
+    "/tangle/silent-shared-dkls23/signing/1.0.0";
diff --git a/protocols/silent-shard-dkls23-ll/src/lib.rs b/protocols/silent-shard-dkls23-ll/src/lib.rs
index 4bd20621b..2799478b2 100644
--- a/protocols/silent-shard-dkls23-ll/src/lib.rs
+++ b/protocols/silent-shard-dkls23-ll/src/lib.rs
@@ -6,6 +6,7 @@ use gadget_common::prelude::*;
 use gadget_common::{generate_protocol, generate_setup_and_run_command};
 use protocol_macros::protocol;
 
+pub mod constants;
 pub mod protocol;
 pub mod rounds;
 
diff --git a/protocols/zcash-frost/src/constants.rs b/protocols/zcash-frost/src/constants.rs
index 8db1d8d75..5c1713eaa 100644
--- a/protocols/zcash-frost/src/constants.rs
+++ b/protocols/zcash-frost/src/constants.rs
@@ -1,6 +1,6 @@
 // ================= Common ======================== //
-pub const ZCASH_FROST_KEYGEN_PROTOCOL_NAME: &str = "/tangle/zcash-frost/keygen/1";
-pub const ZCASH_FROST_SIGNING_PROTOCOL_NAME: &str = "/tangle/zcash-frost/signing/1";
+pub const ZCASH_FROST_KEYGEN_PROTOCOL_NAME: &str = "/tangle/zcash-frost/keygen/1.0.0";
+pub const ZCASH_FROST_SIGNING_PROTOCOL_NAME: &str = "/tangle/zcash-frost/signing/1.0.0";
 
 // ============= Signing Protocol ======================= //
 
diff --git a/shell/Cargo.toml b/shell/Cargo.toml
index 18c1c96b1..e288a8c26 100644
--- a/shell/Cargo.toml
+++ b/shell/Cargo.toml
@@ -40,6 +40,10 @@ futures = { workspace = true }
 bincode2 = { workspace = true }
 # Protocols
 dfns-cggmp21-protocol = { workspace = true }
+zcash-frost-protocol = { workspace = true }
+silent-shard-dkls23-ll-protocol = { workspace = true }
+threshold-bls-protocol = { workspace = true }
+zk-saas-protocol = { workspace = true, features = ["parallel"] }
 
 [dependencies.libp2p]
 workspace = true
diff --git a/shell/src/network/gossip.rs b/shell/src/network/gossip.rs
index 3c67a4d30..6f6c06b23 100644
--- a/shell/src/network/gossip.rs
+++ b/shell/src/network/gossip.rs
@@ -386,7 +386,7 @@ mod tests {
     use crate::config::{KeystoreConfig, ShellConfig, SubxtConfig};
     use crate::network::setup::setup_libp2p_network;
     use crate::shell::wait_for_connection_to_bootnodes;
-    use gadget_common::prelude::{DebugLogger, GadgetProtocolMessage, Network, WorkManager};
+    use gadget_common::prelude::{DebugLogger, GadgetProtocolMessage, WorkManager};
     use gadget_core::job_manager::WorkManagerInterface;
     use sp_core::{ecdsa, Pair};
 
diff --git a/shell/src/network/handlers/identify.rs b/shell/src/network/handlers/identify.rs
index 5b028bc23..847ed25ad 100644
--- a/shell/src/network/handlers/identify.rs
+++ b/shell/src/network/handlers/identify.rs
@@ -7,7 +7,7 @@ impl NetworkService<'_> {
         match event {
             Received { peer_id, info } => {
                 // TODO: Verify the peer info, for example the protocol version, agent version, etc.
-                let info_lines = vec![
+                let info_lines = [
                     format!("Protocol Version: {}", info.protocol_version),
                     format!("Agent Version: {}", info.agent_version),
                     format!("Supported Protocols: {:?}", info.protocols),
@@ -23,7 +23,7 @@ impl NetworkService<'_> {
                     .trace(format!("Sent identify event to peer: {peer_id}"));
             }
             Pushed { peer_id, info } => {
-                let info_lines = vec![
+                let info_lines = [
                     format!("Protocol Version: {}", info.protocol_version),
                     format!("Agent Version: {}", info.agent_version),
                     format!("Supported Protocols: {:?}", info.protocols),
diff --git a/shell/src/network/setup.rs b/shell/src/network/setup.rs
index fb59434f2..53da2d3b0 100644
--- a/shell/src/network/setup.rs
+++ b/shell/src/network/setup.rs
@@ -26,7 +26,7 @@ pub async fn setup_libp2p_network(
     logger: DebugLogger,
     networks: Vec<&'static str>,
     role_key: ecdsa::Pair,
-) -> Result<(Vec<GossipHandle>, JoinHandle<()>), Box<dyn Error>> {
+) -> Result<(HashMap<&'static str, GossipHandle>, JoinHandle<()>), Box<dyn Error>> {
     // Setup both QUIC (UDP) and TCP transports the increase the chances of NAT traversal
     let mut swarm = libp2p::SwarmBuilder::with_existing_identity(identity)
         .with_tokio()
@@ -121,7 +121,7 @@ pub async fn setup_libp2p_network(
     let (tx_to_outbound, mut rx_to_outbound) =
         tokio::sync::mpsc::unbounded_channel::<IntraNodePayload>();
     let ecdsa_peer_id_to_libp2p_id = Arc::new(RwLock::new(HashMap::new()));
-    let mut handles_ret = vec![];
+    let mut handles_ret = HashMap::with_capacity(networks.len());
     for network in networks {
         let topic = IdentTopic::new(network);
         swarm.behaviour_mut().gossipsub.subscribe(&topic)?;
@@ -129,14 +129,17 @@ pub async fn setup_libp2p_network(
         let connected_peers = Arc::new(AtomicU32::new(0));
         inbound_mapping.push((topic.clone(), inbound_tx, connected_peers.clone()));
 
-        handles_ret.push(GossipHandle {
-            connected_peers,
-            topic,
-            tx_to_outbound: tx_to_outbound.clone(),
-            rx_from_inbound: Arc::new(Mutex::new(inbound_rx)),
-            logger: logger.clone(),
-            ecdsa_peer_id_to_libp2p_id: ecdsa_peer_id_to_libp2p_id.clone(),
-        })
+        handles_ret.insert(
+            network,
+            GossipHandle {
+                connected_peers,
+                topic,
+                tx_to_outbound: tx_to_outbound.clone(),
+                rx_from_inbound: Arc::new(Mutex::new(inbound_rx)),
+                logger: logger.clone(),
+                ecdsa_peer_id_to_libp2p_id: ecdsa_peer_id_to_libp2p_id.clone(),
+            },
+        );
     }
 
     swarm
diff --git a/shell/src/shell.rs b/shell/src/shell.rs
index 0592df053..81c673d7c 100644
--- a/shell/src/shell.rs
+++ b/shell/src/shell.rs
@@ -7,6 +7,7 @@ use crate::{
     tangle::{TangleConfig, TangleRuntime},
 };
 use color_eyre::eyre::OptionExt;
+use gadget_common::keystore::KeystoreBackend;
 use gadget_common::{
     client::{PairSigner, SubxtPalletSubmitter},
     config::{ClientWithApi, DebugLogger, PrometheusConfig},
@@ -14,20 +15,28 @@ use gadget_common::{
     keystore::{ECDSAKeyStore, InMemoryBackend},
 };
 use gadget_core::gadget::substrate::Client;
-use parity_scale_codec::Encode;
 use sp_core::{ecdsa, ed25519, sr25519, ByteArray, Pair};
 use sp_keystore::Keystore;
 use tangle_subxt::tangle_runtime::api::runtime_types::tangle_primitives::roles::tss::ThresholdSignatureRoleType;
+use tangle_subxt::{subxt, tangle_runtime::api::runtime_types::tangle_primitives::roles::RoleType};
 
 use crate::config::ShellConfig;
 use crate::network::gossip::GossipHandle;
 use crate::tangle::crypto;
+
 use dfns_cggmp21_protocol::constants::{
     DFNS_CGGMP21_KEYGEN_PROTOCOL_NAME, DFNS_CGGMP21_KEYREFRESH_PROTOCOL_NAME,
     DFNS_CGGMP21_KEYROTATE_PROTOCOL_NAME, DFNS_CGGMP21_SIGNING_PROTOCOL_NAME,
 };
-use gadget_common::keystore::KeystoreBackend;
-use tangle_subxt::{subxt, tangle_runtime::api::runtime_types::tangle_primitives::roles::RoleType};
+use silent_shard_dkls23_ll_protocol::constants::{
+    SILENT_SHARED_DKLS23_KEYGEN_PROTOCOL_NAME, SILENT_SHARED_DKLS23_SIGNING_PROTOCOL_NAME,
+};
+use threshold_bls_protocol::constants::{
+    GENNARO_DKG_BLS_381_KEYGEN_PROTOCOL_NAME, GENNARO_DKG_BLS_381_SIGNING_PROTOCOL_NAME,
+};
+use zcash_frost_protocol::constants::{
+    ZCASH_FROST_KEYGEN_PROTOCOL_NAME, ZCASH_FROST_SIGNING_PROTOCOL_NAME,
+};
 
 /// The version of the shell
 pub const AGENT_VERSION: &str = "tangle/gadget-shell/1.0.0";
@@ -49,10 +58,20 @@ pub async fn run_forever(config: ShellConfig) -> color_eyre::Result<()> {
         &config,
         logger.clone(),
         vec![
+            // dfns-cggmp21
             DFNS_CGGMP21_KEYGEN_PROTOCOL_NAME,
             DFNS_CGGMP21_SIGNING_PROTOCOL_NAME,
             DFNS_CGGMP21_KEYREFRESH_PROTOCOL_NAME,
             DFNS_CGGMP21_KEYROTATE_PROTOCOL_NAME,
+            // zcash-frost
+            ZCASH_FROST_KEYGEN_PROTOCOL_NAME,
+            ZCASH_FROST_SIGNING_PROTOCOL_NAME,
+            // silent-shared-dkls23
+            SILENT_SHARED_DKLS23_KEYGEN_PROTOCOL_NAME,
+            SILENT_SHARED_DKLS23_SIGNING_PROTOCOL_NAME,
+            // gennaro-dkg-bls381
+            GENNARO_DKG_BLS_381_KEYGEN_PROTOCOL_NAME,
+            GENNARO_DKG_BLS_381_SIGNING_PROTOCOL_NAME,
         ],
         role_key,
     )
@@ -88,7 +107,7 @@ pub async fn run_forever(config: ShellConfig) -> color_eyre::Result<()> {
 pub fn start_protocol_by_role<KBE>(
     role: RoleType,
     runtime: TangleRuntime,
-    networks: Vec<GossipHandle>,
+    networks: HashMap<&'static str, GossipHandle>,
     account_id: sr25519::Public,
     logger: DebugLogger,
     pallet_tx: Arc<SubxtPalletSubmitter<TangleConfig, PairSigner<TangleConfig>>>,
@@ -101,7 +120,7 @@ where
     use ThresholdSignatureRoleType::*;
     let handle = match role {
         Tss(DfnsCGGMP21Stark) | Tss(DfnsCGGMP21Secp256r1) | Tss(DfnsCGGMP21Secp256k1) => {
-            let node_input = NodeInput {
+            tokio::spawn(dfns_cggmp21_protocol::setup_node(NodeInput {
                 clients: vec![
                     TangleRuntime::new(runtime.client()),
                     TangleRuntime::new(runtime.client()),
@@ -115,15 +134,114 @@ where
                 node_index: 0,
                 additional_params: (),
                 prometheus_config: PrometheusConfig::Disabled,
-                networks,
-            };
+                networks: vec![
+                    networks
+                        .get(DFNS_CGGMP21_KEYGEN_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                    networks
+                        .get(DFNS_CGGMP21_SIGNING_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                    networks
+                        .get(DFNS_CGGMP21_KEYREFRESH_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                    networks
+                        .get(DFNS_CGGMP21_KEYROTATE_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                ],
+            }))
+        }
+
+        Tss(ZcashFrostEd25519)
+        | Tss(ZcashFrostEd448)
+        | Tss(ZcashFrostP256)
+        | Tss(ZcashFrostP384)
+        | Tss(ZcashFrostSecp256k1)
+        | Tss(ZcashFrostRistretto255) => {
+            tokio::spawn(zcash_frost_protocol::setup_node(NodeInput {
+                clients: vec![
+                    TangleRuntime::new(runtime.client()),
+                    TangleRuntime::new(runtime.client()),
+                ],
+                account_id,
+                logger,
+                pallet_tx,
+                keystore,
+                node_index: 0,
+                additional_params: (),
+                prometheus_config: PrometheusConfig::Disabled,
+                networks: vec![
+                    networks
+                        .get(ZCASH_FROST_KEYGEN_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                    networks
+                        .get(ZCASH_FROST_SIGNING_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                ],
+            }))
+        }
 
-            tokio::spawn(dfns_cggmp21_protocol::setup_node(node_input))
+        Tss(SilentShardDKLS23Secp256k1) => {
+            tokio::spawn(silent_shard_dkls23_ll_protocol::setup_node(NodeInput {
+                clients: vec![
+                    TangleRuntime::new(runtime.client()),
+                    TangleRuntime::new(runtime.client()),
+                ],
+                account_id,
+                logger,
+                pallet_tx,
+                keystore,
+                node_index: 0,
+                additional_params: (),
+                prometheus_config: PrometheusConfig::Disabled,
+                networks: vec![
+                    networks
+                        .get(SILENT_SHARED_DKLS23_KEYGEN_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                    networks
+                        .get(SILENT_SHARED_DKLS23_SIGNING_PROTOCOL_NAME)
+                        .cloned()
+                        .unwrap(),
+                ],
+            }))
+        }
+        Tss(GennaroDKGBls381) => tokio::spawn(threshold_bls_protocol::setup_node(NodeInput {
+            clients: vec![
+                TangleRuntime::new(runtime.client()),
+                TangleRuntime::new(runtime.client()),
+            ],
+            account_id,
+            logger,
+            pallet_tx,
+            keystore,
+            node_index: 0,
+            additional_params: (),
+            prometheus_config: PrometheusConfig::Disabled,
+            networks: vec![
+                networks
+                    .get(GENNARO_DKG_BLS_381_KEYGEN_PROTOCOL_NAME)
+                    .cloned()
+                    .unwrap(),
+                networks
+                    .get(GENNARO_DKG_BLS_381_SIGNING_PROTOCOL_NAME)
+                    .cloned()
+                    .unwrap(),
+            ],
+        })),
+        ZkSaaS(_) => {
+            return Err(color_eyre::eyre::eyre!(
+                "ZkSaaS is not supported by the shell",
+            ))
         }
-        _ => {
+        LightClientRelaying => {
             return Err(color_eyre::eyre::eyre!(
-                "Role {:?} is not supported by the shell",
-                role
+                "LightClientRelaying is not supported by the shell",
             ))
         }
     };
@@ -133,7 +251,7 @@ where
 
 pub async fn start_required_protocols<KBE>(
     subxt_config: &crate::config::SubxtConfig,
-    networks: Vec<GossipHandle>,
+    networks: HashMap<&'static str, GossipHandle>,
     acco_key: sr25519::Pair,
     logger: DebugLogger,
     keystore: ECDSAKeyStore<KBE>,
@@ -160,7 +278,9 @@ where
         let roles = runtime
             .query_restaker_roles(notification.hash, sub_account_id.clone())
             .await?;
+        logger.trace(format!("Got roles: {roles:?}"));
         if roles == current_roles {
+            logger.trace("Roles have not changed, skipping");
             continue;
         }
         let diff = vec_diff(
@@ -168,11 +288,14 @@ where
             roles.iter().cloned().map(HashedRoleTypeWrapper),
         );
         if diff.is_empty() {
+            logger.trace("No roles diff, skipping");
             continue;
         }
+        logger.trace(format!("Roles diff: {diff:?}"));
         for d in diff {
             match d {
                 Diff::Added(role) => {
+                    logger.debug(format!("Trying to start protocol for role {:?}", role.0));
                     let handle = start_protocol_by_role(
                         role.0.clone(),
                         TangleRuntime::new(runtime.client()),
@@ -245,7 +368,7 @@ pub fn load_keys_from_keystore(
 
 pub async fn wait_for_connection_to_bootnodes(
     config: &ShellConfig,
-    handles: &[GossipHandle],
+    handles: &HashMap<&'static str, GossipHandle>,
     logger: &DebugLogger,
 ) -> color_eyre::Result<()> {
     let n_required = config.bootnodes.len();
@@ -271,7 +394,7 @@ pub async fn wait_for_connection_to_bootnodes(
         }
     };
 
-    for handle in handles.iter() {
+    for handle in handles.values() {
         tasks.spawn(wait_for_peers(handle.clone(), n_required, logger.clone()));
     }
     // Wait for all tasks to finish
@@ -286,12 +409,48 @@ enum Diff<T> {
     Removed(T),
 }
 
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Eq, Clone)]
 struct HashedRoleTypeWrapper(RoleType);
 
+impl std::fmt::Debug for HashedRoleTypeWrapper {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{:?}", self.0)
+    }
+}
+
+impl PartialEq for HashedRoleTypeWrapper {
+    fn eq(&self, other: &Self) -> bool {
+        use std::hash::Hasher;
+        let mut hasher = std::hash::DefaultHasher::new();
+        Self::hash(self, &mut hasher);
+        let lhs = hasher.finish();
+        let mut hasher = std::hash::DefaultHasher::new();
+        Self::hash(other, &mut hasher);
+        let rhs = hasher.finish();
+        lhs == rhs
+    }
+}
+
 impl Hash for HashedRoleTypeWrapper {
     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        self.0.encode().hash(state);
+        use RoleType::*;
+        use ThresholdSignatureRoleType::*;
+        match self.0 {
+            Tss(DfnsCGGMP21Stark) | Tss(DfnsCGGMP21Secp256r1) | Tss(DfnsCGGMP21Secp256k1) => {
+                "DfnsCGGMP21".hash(state)
+            }
+            Tss(ZcashFrostEd25519)
+            | Tss(ZcashFrostEd448)
+            | Tss(ZcashFrostP256)
+            | Tss(ZcashFrostP384)
+            | Tss(ZcashFrostSecp256k1)
+            | Tss(ZcashFrostRistretto255) => "ZcashFrost".hash(state),
+
+            Tss(SilentShardDKLS23Secp256k1) => "SilentShardDKLS23Secp256k1".hash(state),
+            Tss(GennaroDKGBls381) => "GennaroDKGBls381".hash(state),
+            ZkSaaS(_) => "ZkSaaS".hash(state),
+            LightClientRelaying => "LightClientRelaying".hash(state),
+        }
     }
 }
 
@@ -312,3 +471,16 @@ fn vec_diff<T: PartialEq + Eq + Hash + Clone, I: Iterator<Item = T>>(a: I, b: I)
         .collect::<Vec<_>>();
     [removed, added].concat()
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn diff() {
+        let a = [1, 2, 3, 4];
+        let b = [2, 3, 4, 5];
+        let diff = vec_diff(a.iter().cloned(), b.iter().cloned());
+        assert_eq!(diff, vec![Diff::Removed(1), Diff::Added(5)]);
+    }
+}


Test patch: