2018-06-08 11:43:27 -07:00
|
|
|
//! Common coin from a given set of keys based on a `pairing` threshold signature scheme.
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
use std::collections::{BTreeMap, VecDeque};
|
2018-06-04 14:00:38 -07:00
|
|
|
use std::fmt::Debug;
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
use pairing::bls12_381::Bls12;
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
use crypto::error as cerror;
|
|
|
|
use crypto::Signature;
|
2018-06-04 14:00:38 -07:00
|
|
|
use messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage};
|
|
|
|
|
|
|
|
error_chain! {
|
2018-06-08 11:43:27 -07:00
|
|
|
links {
|
|
|
|
Crypto(cerror::Error, cerror::ErrorKind);
|
|
|
|
}
|
|
|
|
|
2018-06-04 14:00:38 -07:00
|
|
|
errors {
|
2018-06-08 11:43:27 -07:00
|
|
|
UnknownSender {
|
|
|
|
description("unknown sender")
|
|
|
|
}
|
2018-06-04 14:00:38 -07:00
|
|
|
NotImplemented {
|
|
|
|
description("not implemented")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
#[cfg_attr(feature = "serialization-serde", derive(Serialize, Deserialize))]
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
pub enum CommonCoinMessage {
|
2018-06-07 09:38:27 -07:00
|
|
|
Share(Signature<Bls12>),
|
|
|
|
}
|
2018-06-04 14:00:38 -07:00
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// A common coin algorithm instance. On input, broadcasts our threshold signature share. Upon
|
|
|
|
/// receiving at least `num_faulty + 1` shares, attempts to combine them into a signature. If that
|
|
|
|
/// signature is valid, the instance outputs it and terminates; otherwise the instance aborts.
|
2018-06-04 14:00:38 -07:00
|
|
|
#[derive(Debug)]
|
2018-06-08 11:43:27 -07:00
|
|
|
pub struct CommonCoin<N, T>
|
2018-06-04 14:00:38 -07:00
|
|
|
where
|
|
|
|
N: Debug,
|
|
|
|
{
|
|
|
|
netinfo: Rc<NetworkInfo<N>>,
|
2018-06-08 11:43:27 -07:00
|
|
|
/// The name of this common coin. It is required to be unique for each common coin round.
|
|
|
|
nonce: T,
|
|
|
|
/// The result of combination of at least `num_faulty + 1` threshold signature shares.
|
|
|
|
output: Option<bool>,
|
|
|
|
/// Outgoing message queue.
|
|
|
|
messages: VecDeque<CommonCoinMessage>,
|
|
|
|
/// All received threshold signature shares.
|
|
|
|
received_shares: BTreeMap<N, Signature<Bls12>>,
|
|
|
|
/// Termination flag.
|
|
|
|
terminated: bool,
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
impl<N, T> DistAlgorithm for CommonCoin<N, T>
|
2018-06-04 14:00:38 -07:00
|
|
|
where
|
|
|
|
N: Clone + Debug + Ord,
|
2018-06-08 11:43:27 -07:00
|
|
|
T: Clone + AsRef<[u8]>,
|
2018-06-04 14:00:38 -07:00
|
|
|
{
|
|
|
|
type NodeUid = N;
|
|
|
|
type Input = ();
|
2018-06-08 11:43:27 -07:00
|
|
|
type Output = bool;
|
2018-06-04 14:00:38 -07:00
|
|
|
type Message = CommonCoinMessage;
|
|
|
|
type Error = Error;
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Sends our threshold signature share if not yet sent.
|
|
|
|
fn input(&mut self, _input: Self::Input) -> Result<()> {
|
|
|
|
let share_sent = self.received_shares.keys().fold(false, |result, k| {
|
|
|
|
if !result && k == self.netinfo.our_uid() {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
result
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if !share_sent {
|
|
|
|
self.get_coin()
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Receives input from a remote node.
|
2018-06-04 14:00:38 -07:00
|
|
|
fn handle_message(&mut self, sender_id: &Self::NodeUid, message: Self::Message) -> Result<()> {
|
|
|
|
// FIXME
|
2018-06-08 11:43:27 -07:00
|
|
|
let CommonCoinMessage::Share(share) = message;
|
|
|
|
self.handle_share(sender_id, share)
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Takes the next share of a threshold signature message for multicasting to all other nodes.
|
2018-06-04 14:00:38 -07:00
|
|
|
fn next_message(&mut self) -> Option<TargetedMessage<Self::Message, Self::NodeUid>> {
|
2018-06-08 11:43:27 -07:00
|
|
|
self.messages
|
|
|
|
.pop_front()
|
|
|
|
.map(|msg| Target::All.message(msg))
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
/// Consumes the output. Once consumed, the output stays `None` forever.
|
2018-06-04 14:00:38 -07:00
|
|
|
fn next_output(&mut self) -> Option<Self::Output> {
|
|
|
|
self.output.take()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Whether the algorithm has terminated.
|
|
|
|
fn terminated(&self) -> bool {
|
2018-06-08 11:43:27 -07:00
|
|
|
self.terminated
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn our_id(&self) -> &Self::NodeUid {
|
|
|
|
self.netinfo.our_uid()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
impl<N, T> CommonCoin<N, T>
|
2018-06-04 14:00:38 -07:00
|
|
|
where
|
|
|
|
N: Clone + Debug + Ord,
|
2018-06-08 11:43:27 -07:00
|
|
|
T: Clone + AsRef<[u8]>,
|
2018-06-04 14:00:38 -07:00
|
|
|
{
|
2018-06-08 11:43:27 -07:00
|
|
|
pub fn new(netinfo: Rc<NetworkInfo<N>>, nonce: T) -> Self {
|
2018-06-04 14:00:38 -07:00
|
|
|
CommonCoin {
|
|
|
|
netinfo,
|
2018-06-08 11:43:27 -07:00
|
|
|
nonce,
|
2018-06-04 14:00:38 -07:00
|
|
|
output: None,
|
|
|
|
messages: VecDeque::new(),
|
2018-06-08 11:43:27 -07:00
|
|
|
received_shares: BTreeMap::new(),
|
|
|
|
terminated: false,
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|
|
|
|
}
|
2018-06-07 09:38:27 -07:00
|
|
|
|
2018-06-08 11:43:27 -07:00
|
|
|
fn get_coin(&mut self) -> Result<()> {
|
|
|
|
let share = self.netinfo.secret_key().sign(&self.nonce);
|
2018-06-07 09:38:27 -07:00
|
|
|
self.messages
|
2018-06-08 11:43:27 -07:00
|
|
|
.push_back(CommonCoinMessage::Share(share.clone()));
|
|
|
|
let id = self.netinfo.our_uid().clone();
|
|
|
|
self.handle_share(&id, share)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_share(&mut self, sender_id: &N, share: Signature<Bls12>) -> Result<()> {
|
|
|
|
let node_indices = self.netinfo.node_indices();
|
|
|
|
if let Some(i) = node_indices.get(sender_id) {
|
|
|
|
let pk_i = self.netinfo.public_key_set().public_key_share(*i);
|
|
|
|
if !pk_i.verify(&share, &self.nonce) {
|
|
|
|
// Silently ignore the invalid share.
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.received_shares.insert(sender_id.clone(), share);
|
|
|
|
let received_shares = &self.received_shares;
|
|
|
|
if received_shares.len() > self.netinfo.num_faulty() {
|
|
|
|
// Pass the indices of sender nodes to `combine_signatures`.
|
|
|
|
let shares: BTreeMap<&u64, &Signature<Bls12>> = self
|
|
|
|
.netinfo
|
|
|
|
.all_uids()
|
|
|
|
.iter()
|
|
|
|
.map(|id| (&node_indices[id], received_shares.get(id)))
|
|
|
|
.filter(|(_, share)| share.is_some())
|
|
|
|
.map(|(n, share)| (n, share.unwrap()))
|
|
|
|
.collect();
|
|
|
|
let sig = self.netinfo.public_key_set().combine_signatures(shares)?;
|
|
|
|
// Verify the successfully combined signature with the main public key.
|
|
|
|
if self
|
|
|
|
.netinfo
|
|
|
|
.public_key_set()
|
|
|
|
.public_key()
|
|
|
|
.verify(&sig, &self.nonce)
|
|
|
|
{
|
|
|
|
// Output the parity of the verified signature.
|
|
|
|
self.output = Some(sig.parity());
|
|
|
|
self.terminated = true;
|
|
|
|
} else {
|
|
|
|
// Abort
|
|
|
|
self.terminated = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(ErrorKind::UnknownSender.into())
|
|
|
|
}
|
2018-06-07 09:38:27 -07:00
|
|
|
}
|
2018-06-04 14:00:38 -07:00
|
|
|
}
|