1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
// Copyright 2022 Webb Technologies Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Webb Custom DKG Gossip Engine.
use auto_impl::auto_impl;
use dkg_primitives::{
crypto::AuthoritySignature,
types::{DKGError, SignedDKGMessage},
};
use dkg_runtime_primitives::crypto::AuthorityId;
use sc_network::PeerId;
use sp_application_crypto::RuntimeAppPublic;
use sp_arithmetic::traits::AtLeast32BitUnsigned;
use tokio::sync::mpsc::UnboundedReceiver;
/// A Gossip Engine for DKG, that uses [`sc_network::NetworkService`] as a backend.
mod network;
pub use network::{GossipHandler, GossipHandlerController, NetworkGossipEngineBuilder};
use crate::{debug_logger::DebugLogger, worker::KeystoreExt, DKGKeystore};
/// A GossipEngine that can be used to send DKG messages.
///
/// in the core it is very simple, just two methods:
/// - `send` which will send a DKG message to a specific peer.
/// - `gossip` which will send a DKG message to all peers.
/// - `stream` which will return a stream of DKG messages.
#[auto_impl(Arc,Box,&)]
pub trait GossipEngineIface: Send + Sync + 'static {
type Clock: AtLeast32BitUnsigned + Send + Sync + 'static;
/// Send a DKG message to a specific peer.
fn send(
&self,
recipient: PeerId,
message: SignedDKGMessage<AuthorityId>,
) -> Result<(), DKGError>;
/// Send a DKG message to all peers.
fn gossip(&self, message: SignedDKGMessage<AuthorityId>) -> Result<(), DKGError>;
/// A stream that sends messages. Should only return once with Some, then None thereafter
/// to reinforce a single read stream rather than multiple points in the codebase
fn get_stream(&self) -> Option<UnboundedReceiver<SignedDKGMessage<AuthorityId>>>;
fn local_peer_id(&self) -> PeerId;
fn logger(&self) -> &DebugLogger;
}
/// A Stub implementation of the GossipEngineIface.
impl GossipEngineIface for () {
type Clock = u32;
fn send(
&self,
_recipient: PeerId,
_message: SignedDKGMessage<AuthorityId>,
) -> Result<(), DKGError> {
Ok(())
}
fn gossip(&self, _message: SignedDKGMessage<AuthorityId>) -> Result<(), DKGError> {
Ok(())
}
fn get_stream(&self) -> Option<UnboundedReceiver<SignedDKGMessage<AuthorityId>>> {
None
}
fn local_peer_id(&self) -> PeerId {
PeerId::random()
}
fn logger(&self) -> &DebugLogger {
panic!()
}
}
/// A Handshake message that is sent when a peer connects to us, to verify that the peer Id (which
/// is the sender) is the owner of the authority id.
/// This is used to prevent a malicious peer from impersonating another authority.
/// **Notes:**
/// - The peer id is the id of the peer that sent the handshake message.
/// - The authority id is the id of the authority that the peer claims to be.
/// - The signature is the signature of (peer id, authority id) by the authority id.
/// - The signature is used to verify that the authority id is the owner of the peer id.
#[derive(Debug, Clone, PartialEq, Eq, codec::Encode, codec::Decode)]
pub struct HandshakeMessage {
pub authority_id: AuthorityId,
pub peer_id: Vec<u8>,
pub signature: AuthoritySignature,
}
impl HandshakeMessage {
/// Create a new handshake message.
///
/// This will sign the peer id and authority id with the authority id.
/// Returns an error if the authority id does not have a corresponding private key in the
/// keystore.
pub fn try_new(keystore: &DKGKeystore, peer_id: PeerId) -> Result<Self, DKGError> {
let peer_id = peer_id.to_bytes();
let authority_id = keystore.get_authority_public_key();
let msg = peer_id.clone().into_iter().chain(authority_id.to_raw_vec()).collect::<Vec<_>>();
let signature = keystore.sign(&authority_id, &msg).map_err(|e| {
DKGError::CriticalError { reason: format!("Failed to sign handshake message: {e}") }
})?;
Ok(Self { authority_id, peer_id, signature })
}
/// Verify that the handshake message is valid.
///
/// This will check that:
/// 1. The peer id is the same as the sender peer id.
/// 2. The signature is valid.
/// 3. The authority id is the owner of the peer id.
pub fn is_valid(&self, sender_peer_id: PeerId) -> Result<bool, DKGError> {
// Check that the peer id is the same as the sender peer id.
let msg_peer_id = match PeerId::try_from(self.peer_id.clone()) {
Ok(peer_id) => peer_id,
Err(_) => return Err(DKGError::InvalidPeerId),
};
if msg_peer_id != sender_peer_id {
return Ok(false)
}
let msg = self
.peer_id
.clone()
.into_iter()
.chain(self.authority_id.to_raw_vec())
.collect::<Vec<_>>();
// Verify the signature.
let msg = dkg_primitives::keccak_256(&msg);
let valid = sp_core::ecdsa::Pair::verify_prehashed(
&self.signature.clone().into(),
&msg,
&self.authority_id.clone().into(),
);
Ok(valid)
}
}
#[derive(Debug, Clone, codec::Decode, codec::Encode)]
#[cfg_attr(feature = "scale-info", derive(scale_info::TypeInfo))]
pub enum DKGNetworkMessage {
Handshake(HandshakeMessage),
DKGMessage(SignedDKGMessage<AuthorityId>),
}