use std::collections::HashMap; use frost::{Curve, MultisigKeys}; use crate::{CoinError, Coin}; pub struct WalletKeys { keys: MultisigKeys, creation_height: usize } impl WalletKeys { pub fn new(keys: MultisigKeys, creation_height: usize) -> WalletKeys { WalletKeys { keys, creation_height } } // Bind this key to a specific network by applying an additive offset // While it would be fine to just C::id(), including the group key creates distinct // offsets instead of static offsets. Under a statically offset system, a BTC key could // have X subtracted to find the potential group key, and then have Y added to find the // potential ETH group key. While this shouldn't be an issue, as this isn't a private // system, there are potentially other benefits to binding this to a specific group key // It's no longer possible to influence group key gen to key cancel without breaking the hash // function, although that degree of influence means key gen is broken already fn bind(&self, chain: &[u8]) -> MultisigKeys { self.keys.offset( C::hash_to_F( &[ b"Serai Processor Wallet", chain, &C::G_to_bytes(&self.keys.group_key()) ].concat() ) ) } } pub struct CoinDb { // Height this coin has been scanned to scanned_height: usize, // Acknowledged height for a given canonical height acknowledged_heights: HashMap } pub struct Wallet { db: CoinDb, coin: C, keys: Vec>, pending: Vec<(usize, MultisigKeys)>, outputs: Vec } impl Wallet { pub fn new(coin: C) -> Wallet { Wallet { db: CoinDb { scanned_height: 0, acknowledged_heights: HashMap::new(), }, coin, keys: vec![], pending: vec![], outputs: vec![] } } pub fn scanned_height(&self) -> usize { self.db.scanned_height } pub fn acknowledge_height(&mut self, canonical: usize, height: usize) { debug_assert!(!self.db.acknowledged_heights.contains_key(&canonical)); self.db.acknowledged_heights.insert(canonical, height); } pub fn acknowledged_height(&self, canonical: usize) -> usize { self.db.acknowledged_heights[&canonical] } pub fn add_keys(&mut self, keys: &WalletKeys) { // Doesn't use +1 as this is height, not block index, and poll moves by block index self.pending.push((self.acknowledged_height(keys.creation_height), keys.bind(C::id()))); } pub async fn poll(&mut self) -> Result<(), CoinError> { let confirmed_height = self.coin.get_height().await? - C::confirmations(); for h in self.scanned_height() .. confirmed_height { let mut k = 0; while k < self.pending.len() { if h == self.pending[k].0 { self.keys.push(self.pending.swap_remove(k).1); } else { k += 1; } } let block = self.coin.get_block(h).await?; for keys in &self.keys { let outputs = self.coin.get_outputs(&block, keys.group_key()); } } Ok(()) } }