openethereum/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_ses...

732 lines
25 KiB
Rust

// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use std::collections::{BTreeSet, BTreeMap};
use bigint::hash::H256;
use ethkey::Secret;
use parking_lot::{Mutex, Condvar};
use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare};
use key_server_cluster::cluster::Cluster;
use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession};
use key_server_cluster::decryption_session::SessionImpl as DecryptionSession;
use key_server_cluster::signing_session::SessionImpl as SigningSession;
use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, KeyVersions};
use key_server_cluster::admin_sessions::ShareChangeSessionMeta;
// TODO [Opt]: change sessions so that versions are sent by chunks.
/// Number of versions sent in single message.
const VERSIONS_PER_MESSAGE: usize = 32;
/// Key version negotiation transport.
pub trait SessionTransport {
/// Send message to given node.
fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error>;
}
/// Key version negotiation result computer.
pub trait SessionResultComputer: Send + Sync {
/// Compute result of session, if possible.
fn compute_result(&self, threshold: Option<usize>, confirmations: &BTreeSet<NodeId>, versions: &BTreeMap<H256, BTreeSet<NodeId>>) -> Option<Result<(H256, NodeId), Error>>;
}
/// Key discovery session API.
pub struct SessionImpl<T: SessionTransport> {
/// Session core.
core: SessionCore<T>,
/// Session data.
data: Mutex<SessionData>,
}
/// Action after key version is negotiated.
#[derive(Clone)]
pub enum ContinueAction {
/// Decryption session + is_shadow_decryption.
Decrypt(Arc<DecryptionSession>, bool),
/// Signing session + message hash.
Sign(Arc<SigningSession>, H256),
}
/// Immutable session data.
struct SessionCore<T: SessionTransport> {
/// Session meta.
pub meta: ShareChangeSessionMeta,
/// Sub-session id.
pub sub_session: Secret,
/// Key share.
pub key_share: Option<DocumentKeyShare>,
/// Session result computer.
pub result_computer: Arc<SessionResultComputer>,
/// Session transport.
pub transport: T,
/// Session nonce.
pub nonce: u64,
/// SessionImpl completion condvar.
pub completed: Condvar,
}
/// Mutable session data.
struct SessionData {
/// Session state.
pub state: SessionState,
/// Initialization confirmations.
pub confirmations: Option<BTreeSet<NodeId>>,
/// Key threshold.
pub threshold: Option<usize>,
/// { Version => Nodes }
pub versions: Option<BTreeMap<H256, BTreeSet<NodeId>>>,
/// Session result.
pub result: Option<Result<(H256, NodeId), Error>>,
/// Continue action.
pub continue_with: Option<ContinueAction>,
}
/// SessionImpl creation parameters
pub struct SessionParams<T: SessionTransport> {
/// Session meta.
pub meta: ShareChangeSessionMeta,
/// Sub-session id.
pub sub_session: Secret,
/// Key share.
pub key_share: Option<DocumentKeyShare>,
/// Session result computer.
pub result_computer: Arc<SessionResultComputer>,
/// Session transport to communicate to other cluster nodes.
pub transport: T,
/// Session nonce.
pub nonce: u64,
}
/// Signing session state.
#[derive(Debug, PartialEq)]
enum SessionState {
/// Waiting for initialization.
WaitingForInitialization,
/// Waiting for responses.
WaitingForResponses,
/// Session is completed.
Finished,
}
/// Isolated session transport.
pub struct IsolatedSessionTransport {
/// Cluster.
pub cluster: Arc<Cluster>,
/// Key id.
pub key_id: SessionId,
/// Sub session id.
pub sub_session: Secret,
/// Session-level nonce.
pub nonce: u64,
}
/// Fastest session result computer. Computes first possible version that can be recovered on this node.
/// If there's no such version, selects version with the most support.
pub struct FastestResultComputer {
/// This node id.
self_node_id: NodeId,
/// Threshold (if known).
threshold: Option<usize>,
}
/// Selects version with most support, waiting for responses from all nodes.
pub struct LargestSupportResultComputer;
impl<T> SessionImpl<T> where T: SessionTransport {
/// Create new session.
pub fn new(params: SessionParams<T>) -> Self {
SessionImpl {
core: SessionCore {
meta: params.meta,
sub_session: params.sub_session,
key_share: params.key_share,
result_computer: params.result_computer,
transport: params.transport,
nonce: params.nonce,
completed: Condvar::new(),
},
data: Mutex::new(SessionData {
state: SessionState::WaitingForInitialization,
confirmations: None,
threshold: None,
versions: None,
result: None,
continue_with: None,
})
}
}
/// Return session meta.
pub fn meta(&self) -> &ShareChangeSessionMeta {
&self.core.meta
}
/// Return key threshold.
pub fn key_threshold(&self) -> Result<usize, Error> {
Ok(self.data.lock().threshold.clone().ok_or(Error::InvalidStateForRequest)?)
}
/// Return result computer reference.
pub fn version_holders(&self, version: &H256) -> Result<BTreeSet<NodeId>, Error> {
Ok(self.data.lock().versions.as_ref().ok_or(Error::InvalidStateForRequest)?
.get(version).ok_or(Error::KeyStorage("key version not found".into()))?
.clone())
}
/// Set continue action.
pub fn set_continue_action(&self, action: ContinueAction) {
self.data.lock().continue_with = Some(action);
}
/// Get continue action.
pub fn continue_action(&self) -> Option<ContinueAction> {
self.data.lock().continue_with.clone()
}
/// Wait for session completion.
pub fn wait(&self) -> Result<(H256, NodeId), Error> {
Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone())
}
/// Initialize session.
pub fn initialize(&self, connected_nodes: BTreeSet<NodeId>) -> Result<(), Error> {
// check state
let mut data = self.data.lock();
if data.state != SessionState::WaitingForInitialization {
return Err(Error::InvalidStateForRequest);
}
// update state
let mut confirmations = connected_nodes;
let mut versions: BTreeMap<H256, BTreeSet<NodeId>> = BTreeMap::new();
let received_own_confirmation = confirmations.remove(&self.core.meta.self_node_id);
if received_own_confirmation {
if let Some(key_share) = self.core.key_share.as_ref() {
for version in &key_share.versions {
versions.entry(version.hash.clone())
.or_insert_with(Default::default)
.insert(self.core.meta.self_node_id.clone());
}
}
}
// update state
let no_confirmations_required = confirmations.is_empty();
data.state = SessionState::WaitingForResponses;
data.confirmations = Some(confirmations);
data.versions = Some(versions);
// try to complete session
Self::try_complete(&self.core, &mut *data);
if no_confirmations_required && data.state != SessionState::Finished {
return Err(Error::MissingKeyShare);
} else if data.state == SessionState::Finished {
return Ok(());
}
// send requests
let confirmations = data.confirmations.as_ref().expect("dilled couple of lines above; qed");
for connected_node in confirmations {
self.core.transport.send(connected_node, KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions {
session: self.core.meta.id.clone().into(),
sub_session: self.core.sub_session.clone().into(),
session_nonce: self.core.nonce,
}))?;
}
Ok(())
}
/// Process single message.
pub fn process_message(&self, sender: &NodeId, message: &KeyVersionNegotiationMessage) -> Result<(), Error> {
if self.core.nonce != message.session_nonce() {
return Err(Error::ReplayProtection);
}
match message {
&KeyVersionNegotiationMessage::RequestKeyVersions(ref message) =>
self.on_key_versions_request(sender, message),
&KeyVersionNegotiationMessage::KeyVersions(ref message) =>
self.on_key_versions(sender, message),
&KeyVersionNegotiationMessage::KeyVersionsError(ref message) => {
self.on_session_error(sender, Error::Io(message.error.clone()));
Ok(())
},
}
}
/// Process key versions request.
pub fn on_key_versions_request(&self, sender: &NodeId, _message: &RequestKeyVersions) -> Result<(), Error> {
debug_assert!(sender != &self.core.meta.self_node_id);
// check message
if *sender != self.core.meta.master_node_id {
return Err(Error::InvalidMessage);
}
// check state
let mut data = self.data.lock();
if data.state != SessionState::WaitingForInitialization {
return Err(Error::InvalidStateForRequest);
}
// send response
self.core.transport.send(sender, KeyVersionNegotiationMessage::KeyVersions(KeyVersions {
session: self.core.meta.id.clone().into(),
sub_session: self.core.sub_session.clone().into(),
session_nonce: self.core.nonce,
threshold: self.core.key_share.as_ref().map(|key_share| key_share.threshold),
versions: self.core.key_share.as_ref().map(|key_share|
key_share.versions.iter().rev()
.filter(|v| v.id_numbers.contains_key(sender))
.chain(key_share.versions.iter().rev().filter(|v| !v.id_numbers.contains_key(sender)))
.map(|v| v.hash.clone().into())
.take(VERSIONS_PER_MESSAGE)
.collect())
.unwrap_or_else(|| Default::default())
}))?;
// update state
data.state = SessionState::Finished;
Ok(())
}
/// Process key versions response.
pub fn on_key_versions(&self, sender: &NodeId, message: &KeyVersions) -> Result<(), Error> {
debug_assert!(sender != &self.core.meta.self_node_id);
// check state
let mut data = self.data.lock();
if data.state != SessionState::WaitingForResponses && data.state != SessionState::Finished {
return Err(Error::InvalidStateForRequest);
}
let reason = "this field is filled on master node when initializing; this is initialized master node; qed";
if !data.confirmations.as_mut().expect(reason).remove(sender) {
return Err(Error::InvalidMessage);
}
// remember versions that sender have
{
match message.threshold.clone() {
Some(threshold) if data.threshold.is_none() => {
data.threshold = Some(threshold);
},
Some(threshold) if data.threshold.as_ref() == Some(&threshold) => (),
Some(_) => return Err(Error::InvalidMessage),
None if message.versions.is_empty() => (),
None => return Err(Error::InvalidMessage),
}
let versions = data.versions.as_mut().expect(reason);
for version in &message.versions {
versions.entry(version.clone().into())
.or_insert_with(Default::default)
.insert(sender.clone());
}
}
// try to compute result
if data.state != SessionState::Finished {
Self::try_complete(&self.core, &mut *data);
}
Ok(())
}
/// Try to complete result && finish session.
fn try_complete(core: &SessionCore<T>, data: &mut SessionData) {
let reason = "this field is filled on master node when initializing; try_complete is only called on initialized master node; qed";
let confirmations = data.confirmations.as_ref().expect(reason);
let versions = data.versions.as_ref().expect(reason);
if let Some(result) = core.result_computer.compute_result(data.threshold.clone(), confirmations, versions) {
data.state = SessionState::Finished;
data.result = Some(result);
core.completed.notify_all();
}
}
}
impl<T> ClusterSession for SessionImpl<T> where T: SessionTransport {
type Id = SessionIdWithSubSession;
fn type_name() -> &'static str {
"version negotiation"
}
fn id(&self) -> SessionIdWithSubSession {
SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.sub_session.clone())
}
fn is_finished(&self) -> bool {
self.data.lock().state == SessionState::Finished
}
fn on_session_timeout(&self) {
let mut data = self.data.lock();
if data.confirmations.is_some() {
data.confirmations.as_mut().expect("checked a line above; qed").clear();
Self::try_complete(&self.core, &mut *data);
if data.state != SessionState::Finished {
warn!("{}: key version negotiation session failed with timeout", self.core.meta.self_node_id);
data.result = Some(Err(Error::ConsensusUnreachable));
self.core.completed.notify_all();
}
}
}
fn on_node_timeout(&self, node: &NodeId) {
self.on_session_error(node, Error::NodeDisconnected)
}
fn on_session_error(&self, node: &NodeId, error: Error) {
let mut data = self.data.lock();
if data.confirmations.is_some() {
let is_waiting_for_confirmation = data.confirmations.as_mut().expect("checked a line above; qed").remove(node);
if is_waiting_for_confirmation {
Self::try_complete(&self.core, &mut *data);
if data.state != SessionState::Finished {
warn!("{}: key version negotiation session failed because {} connection has timeouted", self.core.meta.self_node_id, node);
data.state = SessionState::Finished;
data.result = Some(Err(error));
self.core.completed.notify_all();
}
}
}
}
fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> {
match *message {
Message::KeyVersionNegotiation(ref message) => self.process_message(sender, message),
_ => unreachable!("cluster checks message to be correct before passing; qed"),
}
}
}
impl SessionTransport for IsolatedSessionTransport {
fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> {
self.cluster.send(node, Message::KeyVersionNegotiation(message))
}
}
impl FastestResultComputer {
pub fn new(self_node_id: NodeId, key_share: Option<&DocumentKeyShare>) -> Self {
let threshold = key_share.map(|ks| ks.threshold);
FastestResultComputer {
self_node_id: self_node_id,
threshold: threshold,
}
}}
impl SessionResultComputer for FastestResultComputer {
fn compute_result(&self, threshold: Option<usize>, confirmations: &BTreeSet<NodeId>, versions: &BTreeMap<H256, BTreeSet<NodeId>>) -> Option<Result<(H256, NodeId), Error>> {
match self.threshold.or(threshold) {
// if there's no versions at all && we're not waiting for confirmations anymore
_ if confirmations.is_empty() && versions.is_empty() => Some(Err(Error::MissingKeyShare)),
// if we have key share on this node
Some(threshold) => {
// select version this node have, with enough participants
let has_key_share = self.threshold.is_some();
let version = versions.iter().find(|&(_, ref n)| !has_key_share || n.contains(&self.self_node_id) && n.len() >= threshold + 1);
// if there's no such version, wait for more confirmations
match version {
Some((version, nodes)) => Some(Ok((version.clone(), if has_key_share { self.self_node_id.clone() } else { nodes.iter().cloned().nth(0)
.expect("version is only inserted when there's at least one owner; qed") }))),
None if !confirmations.is_empty() => None,
// otherwise - try to find any version
None => Some(versions.iter()
.find(|&(_, ref n)| n.len() >= threshold + 1)
.map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0)
.expect("version is only inserted when there's at least one owner; qed"))))
.unwrap_or(Err(Error::ConsensusUnreachable))),
}
},
// if we do not have share, then wait for all confirmations
None if !confirmations.is_empty() => None,
// ...and select version with largest support
None => Some(versions.iter()
.max_by_key(|&(_, ref n)| n.len())
.map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0)
.expect("version is only inserted when there's at least one owner; qed"))))
.unwrap_or(Err(Error::ConsensusUnreachable))),
}
}
}
impl SessionResultComputer for LargestSupportResultComputer {
fn compute_result(&self, _threshold: Option<usize>, confirmations: &BTreeSet<NodeId>, versions: &BTreeMap<H256, BTreeSet<NodeId>>) -> Option<Result<(H256, NodeId), Error>> {
if !confirmations.is_empty() {
return None;
}
if versions.is_empty() {
return Some(Err(Error::MissingKeyShare));
}
versions.iter()
.max_by_key(|&(_, ref n)| n.len())
.map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0)
.expect("version is only inserted when there's at least one owner; qed"))))
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::collections::{VecDeque, BTreeMap, BTreeSet};
use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage, DocumentKeyShare, DocumentKeyShareVersion};
use key_server_cluster::math;
use key_server_cluster::cluster::Cluster;
use key_server_cluster::cluster::tests::DummyCluster;
use key_server_cluster::admin_sessions::ShareChangeSessionMeta;
use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, KeyVersions};
use super::{SessionImpl, SessionTransport, SessionParams, FastestResultComputer, LargestSupportResultComputer,
SessionResultComputer, SessionState};
struct DummyTransport {
cluster: Arc<DummyCluster>,
}
impl SessionTransport for DummyTransport {
fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> {
self.cluster.send(node, Message::KeyVersionNegotiation(message))
}
}
struct Node {
pub cluster: Arc<DummyCluster>,
pub key_storage: Arc<DummyKeyStorage>,
pub session: SessionImpl<DummyTransport>,
}
struct MessageLoop {
pub session_id: SessionId,
pub nodes: BTreeMap<NodeId, Node>,
pub queue: VecDeque<(NodeId, NodeId, Message)>,
}
impl MessageLoop {
pub fn prepare_nodes(nodes_num: usize) -> BTreeMap<NodeId, Arc<DummyKeyStorage>> {
(0..nodes_num).map(|_| (math::generate_random_point().unwrap(),
Arc::new(DummyKeyStorage::default()))).collect()
}
pub fn empty(nodes_num: usize) -> Self {
Self::new(Self::prepare_nodes(nodes_num))
}
pub fn new(nodes: BTreeMap<NodeId, Arc<DummyKeyStorage>>) -> Self {
let master_node_id = nodes.keys().cloned().nth(0).unwrap();
let sub_sesion = math::generate_random_scalar().unwrap();
let all_nodes_ids: BTreeSet<_> = nodes.keys().cloned().collect();
MessageLoop {
session_id: Default::default(),
nodes: nodes.iter().map(|(node_id, key_storage)| {
let cluster = Arc::new(DummyCluster::new(node_id.clone()));
cluster.add_nodes(all_nodes_ids.iter().cloned());
(node_id.clone(), Node {
cluster: cluster.clone(),
key_storage: key_storage.clone(),
session: SessionImpl::new(SessionParams {
meta: ShareChangeSessionMeta {
id: Default::default(),
self_node_id: node_id.clone(),
master_node_id: master_node_id.clone(),
},
sub_session: sub_sesion.clone(),
key_share: key_storage.get(&Default::default()).unwrap(),
result_computer: Arc::new(FastestResultComputer::new(
node_id.clone(),
key_storage.get(&Default::default()).unwrap().as_ref(),
)),
transport: DummyTransport {
cluster: cluster,
},
nonce: 0,
}),
})
}).collect(),
queue: VecDeque::new(),
}
}
pub fn node_id(&self, idx: usize) -> &NodeId {
self.nodes.keys().nth(idx).unwrap()
}
pub fn session(&self, idx: usize) -> &SessionImpl<DummyTransport> {
&self.nodes.values().nth(idx).unwrap().session
}
}
#[test]
fn negotiation_fails_if_initialized_twice() {
let ml = MessageLoop::empty(1);
assert_eq!(ml.session(0).initialize(BTreeSet::new()), Ok(()));
assert_eq!(ml.session(0).initialize(BTreeSet::new()), Err(Error::InvalidStateForRequest));
}
#[test]
fn negotiation_fails_if_message_contains_wrong_nonce() {
let ml = MessageLoop::empty(2);
assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 100,
})), Err(Error::ReplayProtection));
}
#[test]
fn negotiation_fails_if_versions_request_received_from_non_master() {
let ml = MessageLoop::empty(3);
assert_eq!(ml.session(2).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
})), Err(Error::InvalidMessage));
}
#[test]
fn negotiation_fails_if_versions_request_received_twice() {
let ml = MessageLoop::empty(2);
assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
})), Ok(()));
assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
})), Err(Error::InvalidStateForRequest));
}
#[test]
fn negotiation_fails_if_versions_received_before_initialization() {
let ml = MessageLoop::empty(2);
assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
threshold: Some(10),
versions: Vec::new(),
})), Err(Error::InvalidStateForRequest));
}
#[test]
fn negotiation_does_not_fails_if_versions_received_after_completion() {
let ml = MessageLoop::empty(3);
ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap();
assert_eq!(ml.session(0).data.lock().state, SessionState::WaitingForResponses);
let version_id = (*math::generate_random_scalar().unwrap()).clone();
assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
threshold: Some(0),
versions: vec![version_id.clone().into()]
})), Ok(()));
assert_eq!(ml.session(0).data.lock().state, SessionState::Finished);
assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
threshold: Some(0),
versions: vec![version_id.clone().into()]
})), Ok(()));
assert_eq!(ml.session(0).data.lock().state, SessionState::Finished);
}
#[test]
fn negotiation_fails_if_wrong_threshold_sent() {
let ml = MessageLoop::empty(3);
ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap();
let version_id = (*math::generate_random_scalar().unwrap()).clone();
assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
threshold: Some(1),
versions: vec![version_id.clone().into()]
})), Ok(()));
assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
threshold: Some(2),
versions: vec![version_id.clone().into()]
})), Err(Error::InvalidMessage));
}
#[test]
fn negotiation_fails_if_threshold_empty_when_versions_are_not_empty() {
let ml = MessageLoop::empty(2);
ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap();
let version_id = (*math::generate_random_scalar().unwrap()).clone();
assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions {
session: Default::default(),
sub_session: math::generate_random_scalar().unwrap().into(),
session_nonce: 0,
threshold: None,
versions: vec![version_id.clone().into()]
})), Err(Error::InvalidMessage));
}
#[test]
fn fast_negotiation_does_not_completes_instantly_when_enough_share_owners_are_connected() {
let nodes = MessageLoop::prepare_nodes(2);
let version_id = (*math::generate_random_scalar().unwrap()).clone();
nodes.values().nth(0).unwrap().insert(Default::default(), DocumentKeyShare {
author: Default::default(),
threshold: 1,
public: Default::default(),
common_point: None,
encrypted_point: None,
versions: vec![DocumentKeyShareVersion {
hash: version_id,
id_numbers: vec![(nodes.keys().cloned().nth(0).unwrap(), math::generate_random_scalar().unwrap())].into_iter().collect(),
secret_share: math::generate_random_scalar().unwrap(),
}],
}).unwrap();
let ml = MessageLoop::new(nodes);
ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap();
// we can't be sure that node has given key version because previous ShareAdd session could fail
assert!(ml.session(0).data.lock().state != SessionState::Finished);
}
#[test]
fn fastest_computer_returns_missing_share_if_no_versions_returned() {
let computer = FastestResultComputer {
self_node_id: Default::default(),
threshold: None,
};
assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::MissingKeyShare)));
}
#[test]
fn largest_computer_returns_missing_share_if_no_versions_returned() {
let computer = LargestSupportResultComputer;
assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::MissingKeyShare)));
}
}