fedimint_wallet_client/backup/
recovery_history_tracker.rsuse std::collections::{BTreeMap, BTreeSet, VecDeque};
use fedimint_core::encoding::{Decodable, Encodable};
use fedimint_logging::LOG_CLIENT_MODULE_WALLET;
use tracing::debug;
use crate::backup::FEDERATION_RECOVER_MAX_GAP;
use crate::client_db::TweakIdx;
use crate::WalletClientModuleData;
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct ConsensusPegInTweakIdxesUsedTracker {
used_tweak_idxes: BTreeSet<TweakIdx>,
pending_pubkey_scripts: BTreeMap<bitcoin::ScriptBuf, TweakIdx>,
next_pending_tweak_idx: TweakIdx,
decoys: VecDeque<bitcoin::ScriptBuf>,
decoy_session_threshold: u64,
}
impl ConsensusPegInTweakIdxesUsedTracker {
pub(crate) fn new(
previous_next_unused_idx: TweakIdx,
start_session_idx: u64,
current_session_count: u64,
data: &WalletClientModuleData,
) -> Self {
debug_assert!(start_session_idx <= current_session_count);
let mut s = Self {
next_pending_tweak_idx: previous_next_unused_idx,
pending_pubkey_scripts: BTreeMap::new(),
decoys: VecDeque::new(),
decoy_session_threshold: current_session_count
.saturating_sub((current_session_count.saturating_sub(current_session_count)) / 20),
used_tweak_idxes: BTreeSet::new(),
};
s.init(data);
s
}
fn init(&mut self, data: &WalletClientModuleData) {
for _ in 0..super::ONCHAIN_RECOVER_MAX_GAP {
self.generate_next_pending_tweak_idx(data);
}
debug_assert_eq!(
self.pending_pubkey_scripts.len(),
super::ONCHAIN_RECOVER_MAX_GAP as usize
);
}
pub fn used_tweak_idxes(&self) -> &BTreeSet<TweakIdx> {
&self.used_tweak_idxes
}
fn generate_next_pending_tweak_idx(&mut self, data: &WalletClientModuleData) {
let (script, _address, _tweak_key, _operation_id) =
data.derive_peg_in_script(self.next_pending_tweak_idx);
self.pending_pubkey_scripts
.insert(script, self.next_pending_tweak_idx);
self.next_pending_tweak_idx = self.next_pending_tweak_idx.next();
}
fn refill_pending_pool_up_to_tweak_idx(
&mut self,
data: &WalletClientModuleData,
tweak_idx: TweakIdx,
) {
while self.next_pending_tweak_idx < tweak_idx {
self.generate_next_pending_tweak_idx(data);
}
}
pub(crate) fn handle_script(
&mut self,
data: &WalletClientModuleData,
script: &bitcoin::ScriptBuf,
session_idx: u64,
) {
if let Some(tweak_idx) = self.pending_pubkey_scripts.get(script).copied() {
debug!(target: LOG_CLIENT_MODULE_WALLET, %session_idx, ?tweak_idx, "Found previously used tweak_idx in federation history");
self.used_tweak_idxes.insert(tweak_idx);
self.refill_pending_pool_up_to_tweak_idx(
data,
tweak_idx.advance(FEDERATION_RECOVER_MAX_GAP),
);
} else if self.decoy_session_threshold < session_idx {
self.push_decoy(script);
}
}
fn push_decoy(&mut self, script: &bitcoin::ScriptBuf) {
self.decoys.push_front(script.clone());
if 50 < self.decoys.len() {
self.decoys.pop_back();
}
}
pub(crate) fn pop_decoy(&mut self) -> Option<bitcoin::ScriptBuf> {
self.decoys.pop_front()
}
}