From c27713ec2fce91df08f24a0b148f4ffec2c4963a Mon Sep 17 00:00:00 2001 From: benthecarman Date: Mon, 24 Oct 2022 16:23:30 -0500 Subject: [PATCH 1/8] Implement BDK Database types for gloo::LocalStorage --- node-manager/Cargo.toml | 5 +- node-manager/src/error.rs | 86 +++ node-manager/src/lib.rs | 12 + node-manager/src/localstorage.rs | 959 +++++++++++++++++++++++++++++++ node-manager/src/nodemanager.rs | 6 +- 5 files changed, 1063 insertions(+), 5 deletions(-) create mode 100644 node-manager/src/error.rs create mode 100644 node-manager/src/localstorage.rs diff --git a/node-manager/Cargo.toml b/node-manager/Cargo.toml index 4857f03b4..8fb40078e 100644 --- a/node-manager/Cargo.toml +++ b/node-manager/Cargo.toml @@ -10,8 +10,11 @@ crate-type = ["cdylib"] cfg-if = "1.0.0" wasm-bindgen = "0.2.83" bip39 = { version = "1.0.1" } -bitcoin = "0.29.1" +bitcoin = "0.28.1" +bdk = { git = "https://www.nakamoto.codes/BitcoinDevShop/bdk", branch="feat/use-external-esplora-client", default-features = false} getrandom = { version = "0.2", features = ["js"] } +serde = { version = "^1.0", features = ["derive"] } +serde_json = { version = "^1.0" } gloo-storage = "0.2.2" # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/node-manager/src/error.rs b/node-manager/src/error.rs new file mode 100644 index 000000000..9f485bd94 --- /dev/null +++ b/node-manager/src/error.rs @@ -0,0 +1,86 @@ +use gloo_storage::errors::StorageError; +use std::fmt; + +#[derive(Debug)] +// copied from LDK lite +/// An error that possibly needs to be handled by the user. +pub enum Error { + /// Returned when trying to start Mutiny while it is already running. + AlreadyRunning, + /// Returned when trying to stop Mutiny while it is not running. + NotRunning, + /// The funding transaction could not be created. + FundingTxCreationFailed, + /// A network connection has been closed. + ConnectionFailed, + /// Payment of the given invoice has already been initiated. + NonUniquePaymentHash, + /// The given invoice is invalid. + InvoiceInvalid, + /// Invoice creation failed. + InvoiceCreationFailed, + /// No route for the given target could be found. + RoutingFailed, + /// A given peer info could not be parsed. + PeerInfoParseFailed, + /// A channel could not be opened. + ChannelCreationFailed, + /// A channel could not be closed. + ChannelClosingFailed, + /// Persistence failed. + PersistenceFailed, + /// A wallet operation failed. + WalletOperationFailed, + /// A signing operation failed. + WalletSigningFailed, + /// A chain access operation failed. + ChainAccessFailed, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::AlreadyRunning => write!(f, "Mutiny is already running."), + Self::NotRunning => write!(f, "Mutiny is not running."), + Self::FundingTxCreationFailed => { + write!(f, "Funding transaction could not be created.") + } + Self::ConnectionFailed => write!(f, "Network connection closed."), + Self::NonUniquePaymentHash => write!(f, "An invoice must not get payed twice."), + Self::InvoiceInvalid => write!(f, "The given invoice is invalid."), + Self::InvoiceCreationFailed => write!(f, "Failed to create invoice."), + Self::RoutingFailed => write!(f, "Failed to find route."), + Self::PeerInfoParseFailed => write!(f, "Failed to parse the given peer information."), + Self::ChannelCreationFailed => write!(f, "Failed to create channel."), + Self::ChannelClosingFailed => write!(f, "Failed to close channel."), + Self::PersistenceFailed => write!(f, "Failed to persist data."), + Self::WalletOperationFailed => write!(f, "Failed to conduct wallet operation."), + Self::WalletSigningFailed => write!(f, "Failed to sign given transaction."), + Self::ChainAccessFailed => write!(f, "Failed to conduct chain access operation."), + } + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(e: bdk::Error) -> Self { + match e { + bdk::Error::Signer(_) => Self::WalletSigningFailed, + _ => Self::WalletOperationFailed, + } + } +} + +// todo uncomment when we add esplora stuff +// impl From for Error { +// fn from(_e: esplora::EsploraError) -> Self { +// Self::ChainAccessFailed +// } +// } + +impl From for Error { + fn from(_e: StorageError) -> Self { + Self::PersistenceFailed + } +} diff --git a/node-manager/src/lib.rs b/node-manager/src/lib.rs index ff736018a..2cc065b6e 100644 --- a/node-manager/src/lib.rs +++ b/node-manager/src/lib.rs @@ -2,6 +2,8 @@ // wasm_bindgen uses improper casing and it needs to be turned off: // https://github.com/rustwasm/wasm-bindgen/issues/2882 +mod error; +mod localstorage; mod nodemanager; mod seedgen; mod storage; @@ -26,3 +28,13 @@ pub async fn main_js() -> Result<(), JsValue> { debug!("Main function ends"); Ok(()) } + +#[cfg(test)] +mod test { + use crate::storage::delete_mnemonic; + use gloo_storage::{LocalStorage, Storage}; + + pub(crate) fn cleanup_test() -> () { + LocalStorage::clear(); + } +} diff --git a/node-manager/src/localstorage.rs b/node-manager/src/localstorage.rs new file mode 100644 index 000000000..95a149e9f --- /dev/null +++ b/node-manager/src/localstorage.rs @@ -0,0 +1,959 @@ +use bdk::database::{BatchDatabase, BatchOperations, Database, SyncTime}; +use bdk::{KeychainKind, LocalUtxo, TransactionDetails}; +use bitcoin::consensus::deserialize; +use bitcoin::consensus::encode::serialize; +use bitcoin::hash_types::Txid; +use bitcoin::{OutPoint, Script, Transaction}; +use gloo_storage::{LocalStorage, Storage}; + +use bitcoin::hashes::hex::{FromHex, ToHex}; +use log::{error, info}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; + +pub struct MutinyBrowserStorage {} + +macro_rules! log { + ( $( $t:tt )* ) => { + web_sys::console::log_1(&format!( $( $t )* ).into()); + } + } + +impl MutinyBrowserStorage { + pub fn new() -> Self { + MutinyBrowserStorage {} + } + + // A wrapper for LocalStorage::set that converts the error to bdk::Error + fn set(&self, key: impl AsRef, value: T) -> Result<(), bdk::Error> + where + T: Serialize, + { + LocalStorage::set(key, value) + .map_err(|_| bdk::Error::Generic(String::from(format!("Storage error")))) + } + + // mostly a copy of LocalStorage::get_all() + fn scan_prefix(&self, prefix: String) -> Map { + let local_storage = LocalStorage::raw(); + let length = LocalStorage::length(); + let mut map = Map::with_capacity(length as usize); + for index in 0..length { + let key_opt: Option = local_storage.key(index).unwrap(); + + if let Some(key) = key_opt { + if key.starts_with(String::as_str(&prefix)) { + let value: Value = LocalStorage::get(&key).unwrap(); + map.insert(key, value); + } + } + } + + map + } +} + +// path -> script p{i,e} -> script +// script -> path s