use std::collections::HashMap;
use crate::transaction::beef::Beef;
use crate::transaction::beef_tx::BeefTx;
use crate::transaction::error::TransactionError;
#[derive(Debug, Clone)]
pub struct BeefParty {
pub beef: Beef,
pub known_to: HashMap<String, HashMap<String, bool>>,
}
impl BeefParty {
pub fn new(parties: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
let mut bp = BeefParty {
beef: Beef::new(crate::transaction::beef::BEEF_V2),
known_to: HashMap::new(),
};
for party in parties {
bp.known_to
.insert(party.as_ref().to_string(), HashMap::new());
}
bp
}
pub fn from_beef(beef: Beef) -> Self {
BeefParty {
beef,
known_to: HashMap::new(),
}
}
pub fn is_party(&self, party: &str) -> bool {
self.known_to.contains_key(party)
}
pub fn add_party(&mut self, party: &str) -> Result<(), TransactionError> {
if self.is_party(party) {
return Err(TransactionError::BeefError(format!(
"Party {} already exists.",
party
)));
}
self.known_to.insert(party.to_string(), HashMap::new());
Ok(())
}
pub fn get_known_txids_for_party(&self, party: &str) -> Result<Vec<String>, TransactionError> {
let known = self
.known_to
.get(party)
.ok_or_else(|| TransactionError::BeefError(format!("Party {} is unknown.", party)))?;
Ok(known.keys().cloned().collect())
}
pub fn add_known_txids_for_party(&mut self, party: &str, txids: &[String]) {
let known = self.known_to.entry(party.to_string()).or_default();
for txid in txids {
known.insert(txid.clone(), true);
if !self.beef.txs.iter().any(|t| t.txid == *txid) {
self.beef.txs.push(BeefTx::from_txid(txid.clone()));
}
}
}
pub fn get_trimmed_beef_for_party(&self, party: &str) -> Result<Beef, TransactionError> {
let known_txids = self.get_known_txids_for_party(party)?;
let mut trimmed = self.beef.clone();
trimmed.txs.retain(|tx| !known_txids.contains(&tx.txid));
Ok(trimmed)
}
pub fn merge(&mut self, other: &Beef) -> Result<(), TransactionError> {
for bump in &other.bumps {
let already_exists = self.beef.bumps.iter().any(|b| {
b.block_height == bump.block_height
&& b.compute_root(None).ok() == bump.compute_root(None).ok()
});
if !already_exists {
self.beef.bumps.push(bump.clone());
}
}
for tx in &other.txs {
if !self.beef.txs.iter().any(|t| t.txid == tx.txid) {
self.beef.txs.push(tx.clone());
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_beef_party_new_with_str_slices() {
let bp = BeefParty::new(&["alice", "bob"]);
assert!(bp.is_party("alice"));
assert!(bp.is_party("bob"));
assert!(!bp.is_party("charlie"));
}
#[test]
fn test_beef_party_new_empty() {
let empty: &[&str] = &[];
let bp = BeefParty::new(empty);
assert!(bp.known_to.is_empty());
}
#[test]
fn test_beef_party_new_with_owned_strings() {
let bp = BeefParty::new(vec!["charlie".to_string()]);
assert!(bp.is_party("charlie"));
assert_eq!(bp.known_to.len(), 1);
}
}