use std::collections::{HashMap, HashSet};
use itertools::Itertools;
use crate::structs::{self, Address, Weighting};
use crate::structs::{InitiateMultipartyKeygenRequest, LocalKeyShare, MultipartyIdentifier, PublicKey, SupportedCurrency};
use crate::parties::{PartyInfo, PartyInstance, PartyMembership, PartyMetadata, PartyParticipation, PartyState};
impl PartyInfo {
pub fn active(&self) -> bool {
self.state == PartyState::Active as i32
}
pub fn clear_sensitive(&mut self) -> &mut Self {
self.local_key_share = None;
self
}
pub fn not_debug(&self) -> bool {
self.initiate.as_ref()
.map(|p| p.purpose())
.filter(|p| p != &structs::PartyPurpose::DebugPurpose)
.is_some()
}
pub fn identifier(&self) -> Option<&MultipartyIdentifier> {
self.initiate.as_ref().and_then(|i| i.identifier.as_ref())
}
pub fn local_share(&self) -> Option<&String> {
self.local_key_share.as_ref().and_then(|l| l.local_share.as_ref())
}
pub fn host_public_key(&self) -> Option<&PublicKey> {
self.identifier().and_then(|i| i.party_keys.get(0))
}
pub fn time(&self) -> Option<i64> {
self.initiate.as_ref().map(|i| i.time)
}
pub fn prior_keys(&self) -> Option<&Vec<PublicKey>> {
self.initiate.as_ref().map(|i| i.prior_keys.as_ref())
}
pub fn new_from(initiate: InitiateMultipartyKeygenRequest, local_share: String, self_initiated: bool) -> Self {
Self {
initiate: Some(initiate.clone()),
local_key_share: Some(LocalKeyShare{
local_share: Some(local_share.clone()),
share_type: 0,
version: None,
}),
party_key: None,
self_initiated: Some(self_initiated),
expired_time: None,
successor_key: None,
state: PartyState::Active as i32,
}
}
}
impl PartyMetadata {
pub fn combine(&mut self, other: &PartyMetadata) {
for inst in other.instances.iter() {
if let Some(a) = inst.address.as_ref() {
if self.instance_of_address(a).is_none() {
self.instances.push(inst.clone());
}
}
}
for member in other.memberships.iter() {
if let Some(pk) = member.public_key.as_ref() {
if let Some(existing) = self.memberships.iter_mut()
.find(|m| m.public_key.as_ref() == Some(pk)) {
for part in member.participate.iter() {
if existing.participate.iter().find(|p| p.address == part.address).is_none() {
existing.participate.push(part.clone());
}
}
} else {
self.memberships.push(member.clone());
}
}
}
}
pub fn members_of(&self, address: &Address) -> HashSet<PublicKey> {
self.memberships.iter()
.filter(|m| m.participate.iter().any(|p| p.address.as_ref() == Some(address)))
.map(|m| m.public_key.clone().unwrap())
.collect()
}
pub fn address_by_currency(&self) -> HashMap<SupportedCurrency, Vec<Address>> {
self.instances.iter()
.group_by(|a| a.address.as_ref().map(|a| a.currency()))
.into_iter()
.filter(|(k, _)| k.is_some())
.map(|(k, v)| (k.clone().unwrap(), v
.sorted_by(|a, b| a.creation_time.cmp(&b.creation_time))
.map(|a| a.address.as_ref().unwrap()).cloned().collect()))
.collect()
}
pub fn address_by_currency_latest(&self) -> HashMap<SupportedCurrency, Address> {
self.address_by_currency().into_iter()
.map(|(k, v)| (k, v.last().cloned().unwrap()))
.collect()
}
pub fn earliest_time(&self) -> i64 {
self.instances.iter().filter_map(|i| i.creation_time).min().unwrap_or(0)
}
pub fn group_by_proposer(&self) -> HashMap<PublicKey, PartyMetadata> {
self.instances.iter()
.filter_map(|i| i.proposer.as_ref())
.unique()
.map(|p| (p.clone(), self.filter_by_proposer(p)))
.collect()
}
pub fn filter_by_proposer(&self, key: &PublicKey) -> PartyMetadata {
let mut ret = self.clone();
ret.instances = self.instances_proposed_by(key);
let address = ret.instances.iter()
.flat_map(|i| i.address.as_ref())
.collect::<HashSet<&Address>>();
ret.memberships = self.memberships.iter()
.map(|m| {
let mut updated = m.clone();
updated.participate = updated.participate
.iter()
.filter(|p| p.address.as_ref().map(|a| address.contains(a)).unwrap_or(false))
.cloned()
.collect();
updated
})
.collect_vec();
ret.memberships.retain(|m| m.participate.len() > 0);
ret
}
pub fn instances_proposed_by(&self, key: &PublicKey) -> Vec<PartyInstance> {
self.instances.iter()
.filter(|i| i.proposer.as_ref() == Some(key))
.cloned()
.collect()
}
pub fn active_proposed_by(&self, key: &PublicKey) -> Vec<PartyInstance> {
self.instances.iter()
.filter(|i| i.proposer.as_ref() == Some(key))
.filter(|x| x.is_active())
.cloned()
.collect()
}
pub fn active(&self) -> Vec<PartyInstance> {
self.instances.iter()
.filter(|i| i.is_active())
.cloned()
.collect()
}
pub fn has_instance(&self, cur: SupportedCurrency) -> bool {
self.instances.iter()
.flat_map(|i| i.address.as_ref())
.any(|a| a.currency() == cur)
}
pub fn instances_of(&self, cur: &SupportedCurrency) -> impl Iterator<Item=&PartyInstance> {
let cur = cur.clone() as i32;
self.instances.iter()
.filter(move |i| i.address.as_ref().map(|a| a.currency == cur).unwrap_or(false))
}
pub fn instance_of_address(&self, addr: &Address) -> Option<&PartyInstance> {
self.instances.iter().find(|i| i.address.as_ref() == Some(addr))
}
pub fn latest_instance_by(&self, cur: SupportedCurrency) -> Option<&PartyInstance> {
self.instances_of(&cur).max_by_key(|i| i.creation_time)
}
pub fn address(&self, cur: &SupportedCurrency) -> Option<Address> {
self.address_by_currency().get(cur).and_then(|a| a.last().map(|a| a.clone()))
}
pub fn all_address(&self) -> HashSet<Address> {
self.instances.iter().flat_map(|i| i.address.clone()).collect()
}
pub fn add_instance_equal_members(&mut self, instance: &PartyInstance, equal_members: &Vec<PublicKey>) {
let addr = instance.address.clone();
self.instances.push(instance.clone());
self.add_members(equal_members, addr);
}
pub fn add_members(&mut self, equal_members: &Vec<PublicKey>, addr: Option<Address>) {
let basis = equal_members.len() as i64;
let participate = PartyParticipation {
address: addr.clone(),
weight: Some(Weighting::from_int_basis(1, basis))
};
for member in equal_members.iter() {
if let Some(existing) = self.memberships.iter_mut()
.find(|m| m.public_key.as_ref() == Some(member)) {
existing.participate.push(participate.clone());
} else {
self.memberships.push(PartyMembership {
public_key: Some(member.clone()),
participate: vec![participate.clone()],
});
}
}
}
}
impl PartyInstance {
pub fn is_active(&self) -> bool {
self.state() == PartyState::Active
}
pub fn currency(&self) -> SupportedCurrency {
self.address.as_ref().map(|a| a.currency()).unwrap_or(SupportedCurrency::Redgold)
}
}