Skip to content
Draft
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
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ members = [
"substrate/client/consensus/pow",
"substrate/client/consensus/slots",
"substrate/client/db",
"substrate/client/dkg",
"substrate/client/executor",
"substrate/client/executor/common",
"substrate/client/executor/polkavm",
Expand Down
56 changes: 56 additions & 0 deletions substrate/client/dkg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[package]
name = "sc-dkg"
version = "1.0.0"
authors.workspace = true
edition.workspace = true
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
repository.workspace = true
description = "DKG Client gadget for substrate"
homepage.workspace = true

[lints]
workspace = true

[dependencies]
array-bytes = { workspace = true, default-features = true }
async-channel = { workspace = true }
async-trait = { workspace = true }
codec = { features = ["derive"], workspace = true, default-features = true }
futures = { workspace = true }
log = { workspace = true, default-features = true }
parking_lot = { workspace = true, default-features = true }
prometheus-endpoint = { workspace = true, default-features = true }
scale-info = { features = ["derive"], workspace = true }
sc-client-api = { workspace = true, default-features = true }
sc-consensus = { workspace = true, default-features = true }
sc-network = { workspace = true, default-features = true }
sc-network-gossip = { workspace = true, default-features = true }
sc-network-sync = { workspace = true, default-features = true }
sc-network-types = { workspace = true, default-features = true }
sc-utils = { workspace = true, default-features = true }
sp-api = { workspace = true, default-features = true }
sp-application-crypto = { workspace = true, default-features = true }
sp-arithmetic = { workspace = true, default-features = true }
sp-blockchain = { workspace = true, default-features = true }
sp-consensus = { workspace = true, default-features = true }
sp-core = { workspace = true, default-features = true }
sp-keystore = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
thiserror = { workspace = true }
tokio = { workspace = true, default-features = true }
wasm-timer = { workspace = true }

[dev-dependencies]
sc-block-builder = { workspace = true, default-features = true }
sc-network-test = { workspace = true }
serde = { workspace = true, default-features = true }
sp-tracing = { workspace = true, default-features = true }
substrate-test-runtime-client = { workspace = true }

[features]
# This feature adds BLS crypto primitives. It should not be used in production since
# the BLS implementation and interface may still be subject to significant change.
bls-experimental = [
"sp-application-crypto/bls-experimental",
"sp-core/bls-experimental",
]
143 changes: 143 additions & 0 deletions substrate/client/dkg/src/communication/gossip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use crate::{
communication::{benefit, cost, peers::KnownPeers, notification::Dealing},
LOG_TARGET,
};

use sp_runtime::traits::Hash;

use std::{fmt::Display, marker::PhantomData, sync::Arc, time::Duration};

use sp_application_crypto::{AppPublic, RuntimeAppPublic};

use codec::{Encode, Decode, DecodeWithMemTracking};
use scale_info::TypeInfo;

use sc_network::{NetworkPeers, ReputationChange};

use parking_lot::{Mutex, RwLock};
use wasm_timer::Instant;

const REBROADCAST_AFTER: Duration = Duration::from_secs(5);

#[derive(Debug, PartialEq)]
pub(super) enum Action<H> {
// repropagate under given topic, to the given peers, applying cost/benefit to originator.
Keep(H, ReputationChange),
// discard, applying cost/benefit to originator.
Discard(ReputationChange),
// ignore, no cost/benefit applied to originator.
DiscardNoReport,
}

/// An outcome of examining a message.
#[derive(Debug, PartialEq, Clone, Copy)]
enum Consider {
/// Accept the message.
Accept,
/// Message cannot be evaluated. Reject.
CannotEvaluate,
}

/// DKG Dealing Message.
///
/// A Dealing message is the dealing of a single participant in our DKG scheme with a signature attached
#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
pub struct DealingMessage<Dealing, Id, Signature> {
/// Commit to information extracted from a finalized block
pub dealing: Dealing,
/// Node authority id
pub id: Id,
/// Node signature
pub signature: Signature,
}

/// DKG gossip message type that gets encoded and sent on the network.
#[derive(Debug, Encode, Decode)]
pub(crate) enum GossipMessage<AuthorityId: AuthorityIdBound> {
/// DKG message with commitment and single signature.
Dealing(DealingMessage<Dealing<Vec<u8>>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>),
// TODO: Add another message?
Temp,
}

pub trait AuthorityIdBound:
Ord
+ AppPublic
+ Display
+ RuntimeAppPublic
{

}

impl<AuthorityId: AuthorityIdBound> GossipMessage<AuthorityId> {
pub fn unwrap_dealing(
self
) -> Option<DealingMessage<Dealing<Vec<u8>>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>> {
match self {
GossipMessage::Dealing(dealing) => Some(dealing),
GossipMessage::Temp => None,
}
}
}

/// Gossip engine dealings messages topic
pub(crate) fn dealings_topic<H: Hash>() -> H::Output
where
H: Hash,
{
H::hash_of(b"dkg-dealing")
}

// TEMP replace with BLS-381..
// TODO: Goes in a primitive folder for DKG
/// DKG cryptographic types for ECDSA crypto
///
/// This module basically introduces four crypto types:
/// - `ecdsa_crypto::Pair`
/// - `ecdsa_crypto::Public`
/// - `ecdsa_crypto::Signature`
/// - `ecdsa_crypto::AuthorityId`
///
/// Your code should use the above types as concrete types for all crypto related
/// functionality.
pub mod ecdsa_crypto {
use super::{AuthorityIdBound, RuntimeAppPublic};
use sp_application_crypto::{app_crypto, ecdsa};

// TODO: Put this in Keytypes module in crypto similar to babe beefy etc..
// sp_application_crypto::key_types
/// Key type for DKG module.
pub const DKG: sp_core::crypto::KeyTypeId = sp_core::crypto::KeyTypeId(*b"dkgg");

app_crypto!(ecdsa, DKG);

/// Identity of a DKG authority using ECDSA as its crypto.
pub type AuthorityId = Public;

/// Signature for a DKG authority using ECDSA as its crypto.
pub type AuthoritySignature = Signature;

impl AuthorityIdBound for AuthorityId {

}
}


// TODO: Add Gossip Filters ? Which to Add?

pub struct Filter<AuthorityId>(PhantomData<AuthorityId>);


/// DKG gossip validator
///
/// Validate DKG gossip messages and produce dealings.
pub(crate) struct GossipValidator<H, N, AuthorityId: AuthorityIdBound>
where
H: Hash
{
dealings_topic: H,
gossip_filter: RwLock<Filter<AuthorityId>>,
next_rebroadcast: Mutex<Instant>,
known_peers: Arc<Mutex<KnownPeers<H>>>,
network: Arc<N>,
}
83 changes: 83 additions & 0 deletions substrate/client/dkg/src/communication/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
pub mod notification;

pub(crate) mod gossip;
pub(crate) mod peers;

pub(crate) mod dkg_protocol_name {
use array_bytes::bytes2hex;
use sc_network::ProtocolName;

// TODO: What are we exactly gossiping to other validators in the DKG? What are we sending? Are we broadcasting something or just sending to individuals?
/// DKG dealings gossip protocol name suffix.
const GOSSIP_NAME: &str = "/dkg/2";

/// Name of the dealings gossip protocol used by the DKG.
///
/// Must be registered towards the networking in order for DKG participant to properly function.
pub fn gossip_protocol_name<Hash: AsRef<[u8]>>(
genesis_hash: Hash,
// TODO: Do we need the Fork ID here?
fork_id: Option<&str>,
) -> ProtocolName {
let genesis_hash = genesis_hash.as_ref();
if let Some(fork_id) = fork_id {
format!("/{}/{}{}", bytes2hex("", genesis_hash), fork_id, GOSSIP_NAME).into()
} else {
format!("/{}{}", bytes2hex("", genesis_hash), GOSSIP_NAME).into()
}
}
}

/// Returns the configuration value to put in
/// [`sc_network::config::FullNetworkConfiguration`].
/// For standard protocol name see [`dkg_protocol_name::gossip_protocol_name`].
pub fn dkg_peers_set_config<
B: sp_runtime::traits::Block,
N: sc_network::NetworkBackend<B, <B as sp_runtime::traits::Block>::Hash>,
>(
gossip_protocol_name: sc_network::ProtocolName,
metrics: sc_network::service::NotificationMetrics,
peer_store_handle: std::sync::Arc<dyn sc_network::peer_store::PeerStoreProvider>,
) -> (N::NotificationProtocolConfig, Box<dyn sc_network::NotificationService>) {
let (cfg, notification_service) = N::notification_config(
gossip_protocol_name,
Vec::new(),
1024 * 1024,
None,
sc_network::config::SetConfig {
in_peers: 25, // TODO: This should be all the validators so how do we change this to be all validators?
out_peers: 25, // TODO: This should be all the validators so how do we change this to be all validators?
reserved_nodes: Vec::new(),
non_reserved_mode: sc_network::config::NonReservedPeerMode::Accept,
},
metrics,
peer_store_handle,
);
(cfg, notification_service)
}

// cost scalars for reporting peers.
mod cost {
use sc_network::ReputationChange as Rep;
// Message that's for an outdated round.
pub(super) const OUTDATED_MESSAGE: Rep = Rep::new(-50, "DKG: Past message");
// Message that's from the future relative to our current set-id.
pub(super) const FUTURE_MESSAGE: Rep = Rep::new(-100, "DKG: Future message");
// DKG message dealing containing bad signature.
pub(super) const BAD_SIGNATURE: Rep = Rep::new(-100, "DKG: Bad signature");
// Message received with dealing from participant not in validator set.
pub(super) const UNKNOWN_PARTICIPANT: Rep = Rep::new(-150, "DKG: Unknown participant");
// Message containing invalid dealing.
pub(super) const INVALID_DEALING: Rep = Rep::new(-5000, "DKG: Invalid dealing");
// Reputation cost per signature checked for invalid dealing.
pub(super) const PER_SIGNATURE_CHECKED: i32 = -25;
// Reputation cost per byte for un-decodable message.
pub(super) const PER_UNDECODABLE_BYTE: i32 = -5;
}

// benefit scalars for reporting peers.
mod benefit {
use sc_network::ReputationChange as Rep;
pub(super) const DEALING_MESSAGE: Rep = Rep::new(100, "DKG: dealing message");
pub(super) const NOT_INTERESTED: Rep = Rep::new(10, "DKG: Not interested in round");
}
26 changes: 26 additions & 0 deletions substrate/client/dkg/src/communication/notification.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use sc_utils::notification::{NotificationSender, NotificationStream, TracingKeyStr};
use sp_runtime::{scale_info::TypeInfo, traits::Hash};

// TODO: Put this somewhere else
/// DKG Dealing
#[derive(Clone, Debug, PartialEq, Eq, TypeInfo)]
pub struct Dealing<T> {
pub dealing: T,
}

/// The sending half of the notifications channel(s) used to send
/// notifications about DKG dealing.
pub type DkgDealingSender = NotificationSender<Dealing<Vec<u8>>>;

/// The receiving half of a notifications channel used to receive
/// notifications about DKG dealings.
pub type DkgDealingReceiver =
NotificationStream<Dealing<Vec<u8>>, DkgDealingTracingKey>;

/// Provides tracing key for DKG dealing stream.
#[derive(Clone)]
pub struct DkgDealingTracingKey;
impl TracingKeyStr for DkgDealingTracingKey {
const TRACING_KEY: &'static str = "mpsc_dkg_dealing_notification_stream";
}

Loading
Loading