From b14a37a32a427556c65003a9a2325ed8798a769d Mon Sep 17 00:00:00 2001 From: Sosthene Date: Fri, 20 Jun 2025 10:41:22 +0200 Subject: [PATCH] Add updates mod --- src/lib.rs | 1 + src/updates.rs | 203 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 src/updates.rs diff --git a/src/lib.rs b/src/lib.rs index 3bd6036..cabb26e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ pub mod secrets; pub mod serialization; pub mod signature; pub mod silentpayments; +pub mod updates; pub const MAX_PRD_PAYLOAD_SIZE: usize = u16::MAX as usize; // 64KiB sounds reasonable for now diff --git a/src/updates.rs b/src/updates.rs new file mode 100644 index 0000000..6e277fd --- /dev/null +++ b/src/updates.rs @@ -0,0 +1,203 @@ +use std::{mem, sync::{mpsc::{self, Receiver, Sender}, Arc, RwLock}}; +use anyhow::Result; +use serde::{Serialize, Deserialize}; +// use wasm_bindgen::prelude::*; +// use sp_client::bitcoin::absolute::Height; +// use sp_client::bitcoin::BlockHash; +use sp_client::{bitcoin::{absolute::Height, BlockHash, OutPoint}, OwnedOutput, Updater}; +use std::collections::{HashMap, HashSet}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct ScanProgress { + pub start: u32, + pub current: u32, + pub end: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum StateUpdate { + NoUpdate { + blkheight: Height, + }, + Update { + blkheight: Height, + blkhash: BlockHash, + found_outputs: HashMap, + found_inputs: HashSet, + }, +} + +// #[cfg(target_arch = "wasm32")] +// #[wasm_bindgen] +// extern "C" { +// #[wasm_bindgen(js_namespace = window)] +// fn sendScanProgress(progress: JsValue); + +// #[wasm_bindgen(js_namespace = window)] +// fn sendStateUpdate(update: JsValue); +// } + +pub trait UpdateSink: Send + Sync { + fn send_scan_progress(&self, progress: ScanProgress) -> Result<()>; + fn send_state_update(&self, update: StateUpdate) -> Result<()>; +} + +#[cfg(not(target_arch = "wasm32"))] +pub struct NativeUpdateSink { + scan_tx: Sender, + state_tx: Sender, +} + +#[cfg(not(target_arch = "wasm32"))] +impl NativeUpdateSink { + pub fn new() -> (Self, Receiver, Receiver) { + let (scan_tx, scan_rx) = mpsc::channel(); + let (state_tx, state_rx) = mpsc::channel(); + + (Self { scan_tx, state_tx }, scan_rx, state_rx) + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl UpdateSink for NativeUpdateSink { + fn send_scan_progress(&self, progress: ScanProgress) -> Result<()> { + self.scan_tx.send(progress)?; + Ok(()) + } + + fn send_state_update(&self, update: StateUpdate) -> Result<()> { + self.state_tx.send(update)?; + Ok(()) + } +} + +// #[cfg(target_arch = "wasm32")] +// pub struct WasmUpdateSink; + +// #[cfg(target_arch = "wasm32")] +// impl UpdateSink for WasmUpdateSink { +// fn send_scan_progress(&self, progress: ScanProgress) -> Result<()> { +// let js_value = serde_wasm_bindgen::to_value(&progress)?; +// sendScanProgress(js_value); +// Ok(()) +// } + +// fn send_state_update(&self, update: StateUpdate) -> Result<()> { +// let js_value = serde_wasm_bindgen::to_value(&update)?; +// sendStateUpdate(js_value); +// Ok(()) +// } +// } + +// Global sink instance +static UPDATE_SINK: RwLock>> = RwLock::new(None); + +pub fn init_update_sink(sink: Arc) { + let mut sink_guard = UPDATE_SINK.write().unwrap(); + *sink_guard = Some(sink); +} + +pub fn get_update_sink() -> Option> { + UPDATE_SINK.read().unwrap().clone() +} + +#[derive(Debug)] +pub struct StateUpdater { + update: bool, + blkhash: Option, + blkheight: Option, + found_outputs: HashMap, + found_inputs: HashSet, +} + +impl StateUpdater { + pub fn new() -> Self { + Self { + update: false, + blkheight: None, + blkhash: None, + found_outputs: HashMap::new(), + found_inputs: HashSet::new(), + } + } + + pub fn to_update(&mut self) -> Result { + let blkheight = self + .blkheight + .ok_or(anyhow::Error::msg("blkheight not filled"))?; + + if self.update { + self.update = false; + + let blkhash = self.blkhash.ok_or(anyhow::Error::msg("blkhash not set"))?; + + self.blkheight = None; + self.blkhash = None; + + // take results, and insert new empty values + let found_inputs = mem::take(&mut self.found_inputs); + let found_outputs = mem::take(&mut self.found_outputs); + + Ok(StateUpdate::Update { + blkheight, + blkhash, + found_outputs, + found_inputs, + }) + } else { + Ok(StateUpdate::NoUpdate { blkheight }) + } + } +} + +impl Updater for StateUpdater { + fn record_scan_progress(&mut self, start: Height, current: Height, end: Height) -> Result<()> { + self.blkheight = Some(current); + + if let Some(sink) = get_update_sink() { + sink.send_scan_progress(ScanProgress { + start: start.to_consensus_u32(), + current: current.to_consensus_u32(), + end: end.to_consensus_u32(), + })?; + } + + Ok(()) + } + + fn record_block_outputs( + &mut self, + height: Height, + blkhash: BlockHash, + found_outputs: HashMap, + ) -> Result<()> { + // may have already been written by record_block_inputs + self.update = true; + self.found_outputs = found_outputs; + self.blkhash = Some(blkhash); + self.blkheight = Some(height); + + Ok(()) + } + + fn record_block_inputs( + &mut self, + blkheight: Height, + blkhash: BlockHash, + found_inputs: HashSet, + ) -> Result<()> { + self.update = true; + self.blkheight = Some(blkheight); + self.blkhash = Some(blkhash); + self.found_inputs = found_inputs; + + Ok(()) + } + + fn save_to_persistent_storage(&mut self) -> Result<()> { + if let Some(sink) = get_update_sink() { + sink.send_state_update(self.to_update()?)?; + } + Ok(()) + } +}