hbbft/src/common_coin.rs

238 lines
7.8 KiB
Rust
Raw Normal View History

//! # A Cryptographic Common Coin
//!
2018-07-05 08:51:55 -07:00
//! The Common Coin produces a pseudorandom binary value that the correct nodes agree on, and that
//! cannot be known beforehand.
//!
//! Every Common Coin instance has a _nonce_ that determines the value, without giving it away: It
2018-07-05 08:51:55 -07:00
//! is not feasible to compute the output from the nonce alone, and the output is uniformly
//! distributed.
//!
2018-07-02 06:42:31 -07:00
//! The nodes input a signal (no data, just `()`), and after _2 f + 1_ nodes have provided input,
//! everyone receives the output value. In particular, the adversary cannot know the output value
//! before at least one correct node has provided input.
//!
//! ## How it works
//!
//! The algorithm uses a threshold signature scheme with the uniqueness property: For each public
2018-07-02 23:53:08 -07:00
//! key and message, there is exactly one valid signature. This group signature is produced using
//! signature shares from any combination of _2 f + 1_ secret key share holders.
//!
//! * On input, a node signs the nonce and sends its signature share to everyone else.
2018-07-02 06:42:31 -07:00
//! * When a node has received _2 f + 1_ shares, it computes the main signature and outputs the XOR
//! of its bits.
2018-06-08 11:43:27 -07:00
use std::collections::{BTreeMap, VecDeque};
use std::fmt::Debug;
use std::sync::Arc;
2018-06-08 11:43:27 -07:00
use crypto::error as cerror;
2018-07-17 06:54:12 -07:00
use crypto::{Signature, SignatureShare};
use fault_log::{FaultKind, FaultLog};
2018-07-18 05:15:47 -07:00
use messaging::{DistAlgorithm, NetworkInfo, Step, Target};
error_chain! {
2018-06-08 11:43:27 -07:00
links {
Crypto(cerror::Error, cerror::ErrorKind);
}
errors {
2018-06-08 11:43:27 -07:00
UnknownSender {
description("unknown sender")
}
VerificationFailed {
description("signature verification failed")
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
2018-07-17 06:54:12 -07:00
pub struct CommonCoinMessage(SignatureShare);
2018-06-09 13:50:36 -07:00
impl CommonCoinMessage {
2018-07-17 06:54:12 -07:00
pub fn new(sig: SignatureShare) -> Self {
2018-06-09 13:50:36 -07:00
CommonCoinMessage(sig)
}
2018-07-17 06:54:12 -07:00
pub fn to_sig(&self) -> &SignatureShare {
2018-06-09 13:50:36 -07:00
&self.0
}
}
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.
#[derive(Debug)]
pub struct CommonCoin<NodeUid, T> {
netinfo: Arc<NetworkInfo<NodeUid>>,
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.
2018-07-17 06:54:12 -07:00
received_shares: BTreeMap<NodeUid, SignatureShare>,
/// Whether we provided input to the common coin.
had_input: bool,
2018-06-08 11:43:27 -07:00
/// Termination flag.
terminated: bool,
}
type CommonCoinStepResult<NodeUid, T> = Result<Step<CommonCoin<NodeUid, T>>>;
2018-07-09 04:35:26 -07:00
impl<NodeUid, T> DistAlgorithm for CommonCoin<NodeUid, T>
where
NodeUid: Clone + Debug + Ord,
2018-06-08 11:43:27 -07:00
T: Clone + AsRef<[u8]>,
{
type NodeUid = NodeUid;
type Input = ();
2018-06-08 11:43:27 -07:00
type Output = bool;
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) -> CommonCoinStepResult<NodeUid, T> {
2018-07-09 04:35:26 -07:00
let fault_log = if !self.had_input {
self.had_input = true;
2018-07-09 04:35:26 -07:00
self.get_coin()?
2018-06-08 11:43:27 -07:00
} else {
2018-07-09 04:35:26 -07:00
FaultLog::new()
};
self.step(fault_log)
}
2018-06-08 11:43:27 -07:00
/// Receives input from a remote node.
fn handle_message(
&mut self,
sender_id: &Self::NodeUid,
message: Self::Message,
) -> CommonCoinStepResult<NodeUid, T> {
2018-07-09 04:35:26 -07:00
let fault_log = if !self.terminated {
let CommonCoinMessage(share) = message;
self.handle_share(sender_id, share)?
} else {
2018-07-13 14:53:44 -07:00
FaultLog::new()
2018-07-09 04:35:26 -07:00
};
self.step(fault_log)
}
/// Whether the algorithm has terminated.
fn terminated(&self) -> bool {
2018-06-08 11:43:27 -07:00
self.terminated
}
fn our_id(&self) -> &Self::NodeUid {
self.netinfo.our_uid()
}
}
impl<NodeUid, T> CommonCoin<NodeUid, T>
where
NodeUid: Clone + Debug + Ord,
2018-06-08 11:43:27 -07:00
T: Clone + AsRef<[u8]>,
{
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, nonce: T) -> Self {
CommonCoin {
netinfo,
2018-06-08 11:43:27 -07:00
nonce,
output: None,
messages: VecDeque::new(),
2018-06-08 11:43:27 -07:00
received_shares: BTreeMap::new(),
had_input: false,
2018-06-08 11:43:27 -07:00
terminated: false,
}
}
2018-06-07 09:38:27 -07:00
fn step(&mut self, fault_log: FaultLog<NodeUid>) -> CommonCoinStepResult<NodeUid, T> {
Ok(Step::new(
self.output.take().into_iter().collect(),
fault_log,
2018-07-18 05:15:47 -07:00
self.messages
.drain(..)
.map(|msg| Target::All.message(msg))
.collect(),
))
2018-07-09 04:35:26 -07:00
}
fn get_coin(&mut self) -> Result<FaultLog<NodeUid>> {
if !self.netinfo.is_validator() {
self.try_output()?;
return Ok(FaultLog::new());
}
2018-07-17 06:54:12 -07:00
let share = self.netinfo.secret_key_share().sign(&self.nonce);
2018-06-09 13:50:36 -07:00
self.messages.push_back(CommonCoinMessage(share.clone()));
2018-06-08 11:43:27 -07:00
let id = self.netinfo.our_uid().clone();
self.handle_share(&id, share)
}
2018-07-17 06:54:12 -07:00
fn handle_share(
&mut self,
sender_id: &NodeUid,
share: SignatureShare,
) -> Result<FaultLog<NodeUid>> {
2018-06-27 05:25:32 -07:00
if let Some(pk_i) = self.netinfo.public_key_share(sender_id) {
if !pk_i.verify(&share, &self.nonce) {
// Log the faulty node and ignore the invalid share.
let fault_kind = FaultKind::UnverifiedSignatureShareSender;
let fault_log = FaultLog::init(sender_id.clone(), fault_kind);
return Ok(fault_log);
}
self.received_shares.insert(sender_id.clone(), share);
2018-06-08 11:43:27 -07:00
} else {
return Err(ErrorKind::UnknownSender.into());
}
self.try_output()?;
Ok(FaultLog::new())
}
fn try_output(&mut self) -> Result<()> {
let received_shares = &self.received_shares;
debug!(
"{:?} received {} shares, had_input = {}",
self.netinfo.our_uid(),
received_shares.len(),
self.had_input
);
if self.had_input && received_shares.len() > self.netinfo.num_faulty() {
let sig = self.combine_and_verify_sig()?;
// Output the parity of the verified signature.
let parity = sig.parity();
debug!("{:?} output {}", self.netinfo.our_uid(), parity);
self.output = Some(parity);
self.terminated = true;
2018-06-08 11:43:27 -07:00
}
Ok(())
2018-06-07 09:38:27 -07:00
}
2018-06-14 04:28:38 -07:00
2018-06-21 08:31:15 -07:00
fn combine_and_verify_sig(&self) -> Result<Signature> {
2018-06-14 04:28:38 -07:00
// Pass the indices of sender nodes to `combine_signatures`.
2018-07-17 06:54:12 -07:00
let ids_shares: BTreeMap<&NodeUid, &SignatureShare> = self.received_shares.iter().collect();
let ids_u64: BTreeMap<&NodeUid, u64> = ids_shares
2018-06-14 04:28:38 -07:00
.keys()
2018-07-17 06:54:12 -07:00
.map(|&id| (id, self.netinfo.node_index(id).unwrap() as u64))
2018-06-14 04:28:38 -07:00
.collect();
// Convert indices to `u64` which is an interface type for `pairing`.
2018-07-17 06:54:12 -07:00
let shares: BTreeMap<&u64, &SignatureShare> = ids_shares
2018-06-14 04:28:38 -07:00
.iter()
.map(|(id, &share)| (&ids_u64[id], share))
.collect();
let sig = self.netinfo.public_key_set().combine_signatures(shares)?;
if !self
.netinfo
.public_key_set()
.public_key()
.verify(&sig, &self.nonce)
{
// Abort
error!(
"{:?} main public key verification failed",
self.netinfo.our_uid()
);
Err(ErrorKind::VerificationFailed.into())
} else {
Ok(sig)
}
}
}