From b32903065a13d659d9e04ccffc82ff2fb6b5c483 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Thu, 9 Nov 2023 16:59:44 -0600 Subject: [PATCH 01/17] wip add bitcoind backend --- Cargo.lock | 111 ++++++++++++++++++++++++++++++++++++++++++++++----- Cargo.toml | 4 +- src/main.rs | 72 ++++++++++++++++++++++++++++++--- src/state.rs | 6 +++ 4 files changed, 177 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f95781f..ef79aab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ version = "1.0.0-alpha.1" source = "git+https://github.com/bitcoindevkit/bdk?rev=8f38e96e4542db2378e2e64cd9289638ee86ba1a#8f38e96e4542db2378e2e64cd9289638ee86ba1a" dependencies = [ "bdk_chain", - "bitcoin", + "bitcoin 0.29.2", "getrandom", "js-sys", "log", @@ -145,7 +145,7 @@ name = "bdk_chain" version = "0.5.0" source = "git+https://github.com/bitcoindevkit/bdk?rev=8f38e96e4542db2378e2e64cd9289638ee86ba1a#8f38e96e4542db2378e2e64cd9289638ee86ba1a" dependencies = [ - "bitcoin", + "bitcoin 0.29.2", "miniscript", "serde", ] @@ -194,11 +194,31 @@ checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ "base64 0.13.1", "bech32", - "bitcoin_hashes", - "secp256k1", + "bitcoin_hashes 0.11.0", + "secp256k1 0.24.3", "serde", ] +[[package]] +name = "bitcoin" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e99ff7289b20a7385f66a0feda78af2fc119d28fb56aea8886a9cd0a4abdd75" +dependencies = [ + "bech32", + "bitcoin-private", + "bitcoin_hashes 0.12.0", + "hex_lit", + "secp256k1 0.27.0", + "serde", +] + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + [[package]] name = "bitcoin_hashes" version = "0.11.0" @@ -208,6 +228,40 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +dependencies = [ + "bitcoin-private", + "serde", +] + +[[package]] +name = "bitcoincore-rpc" +version = "0.17.0" +source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23806125586e999a48429d57d2d94a2a3cab696b" +dependencies = [ + "bitcoin-private", + "bitcoincore-rpc-json", + "jsonrpc", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "bitcoincore-rpc-json" +version = "0.17.0" +source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23806125586e999a48429d57d2d94a2a3cab696b" +dependencies = [ + "bitcoin 0.30.1", + "bitcoin-private", + "serde", + "serde_json", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -318,7 +372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da3b630e345cdfc6f64315414b50815a9eeabbf12438413798bf09e9e79be8b8" dependencies = [ "anyhow", - "bitcoin", + "bitcoin 0.29.2", "bytes", "futures-util", "hex", @@ -415,7 +469,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e11244e7fd8b0beee0a3c62137c4bd9f756fe2c492ccf93171f81467b59200" dependencies = [ - "bitcoin", + "bitcoin 0.29.2", "log", "reqwest", "serde", @@ -617,6 +671,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + [[package]] name = "home" version = "0.5.5" @@ -755,6 +815,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpc" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8128f36b47411cd3f044be8c1f5cc0c9e24d1d1bfdc45f0a57897b32513053f2" +dependencies = [ + "base64 0.13.1", + "serde", + "serde_json", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -797,7 +868,7 @@ version = "9.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5b106477a0709e2da253e5559ba4ab20a272f8577f1eefff72f3a905b5d35f5" dependencies = [ - "bitcoin", + "bitcoin 0.29.2", "serde", ] @@ -1159,9 +1230,21 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.11.0", "rand", - "secp256k1-sys", + "secp256k1-sys 0.6.1", + "serde", +] + +[[package]] +name = "secp256k1" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +dependencies = [ + "bitcoin_hashes 0.12.0", + "rand", + "secp256k1-sys 0.8.1", "serde", ] @@ -1174,6 +1257,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -1258,6 +1350,7 @@ dependencies = [ "bdk", "bdk_esplora", "bdk_file_store", + "bitcoincore-rpc", "clap", "cln-plugin", "cln-rpc", diff --git a/Cargo.toml b/Cargo.toml index 1ddcaf6..b3a87fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,9 @@ bdk = { git = "https://github.com/bitcoindevkit/bdk", version = "1.0.0-alpha.1", bdk_esplora = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } # bdk_file_store = { version = "0.2.0" } # bdk_file_store = { path = "../bdk/crates/file_store" } -bdk_file_store ={ git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } +bdk_file_store = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } +# bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } +bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } clap = { version = "4.4.0", features = ["derive"] } cln-plugin = { git = "https://github.com/elementsproject/lightning", version = "0.1.4" } # cln-plugin = { path = "../../lightning/plugins" } diff --git a/src/main.rs b/src/main.rs index 43bfc0c..be20388 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ #[macro_use] extern crate serde_json; +use bitcoincore_rpc::bitcoincore_rpc_json::ScanBlocksRequest; use clap::error::ErrorKind; use clap::{CommandFactory, Parser, Subcommand}; use cln_rpc::model::DatastoreMode; @@ -30,15 +31,54 @@ use tokio; use bdk::TransactionDetails; use smaug::state::{Smaug, State}; + +fn scanblocks<'a>( + brpc_user: String, + brpc_pass: String, +) -> Result<(), Error> +{ + // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; + // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" + let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; + let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; + let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; + let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; + + extern crate bitcoincore_rpc; + + use bitcoincore_rpc::{Auth, Client, RpcApi}; + + let rpc = Client::new("http://localhost:18443", + Auth::UserPass(brpc_user, brpc_pass) + // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) + ).unwrap(); + let descriptor = ScanBlocksRequest::Extended { desc:_mutinynet_descriptor_ext.to_string(), range: None }; + let descriptors = &[descriptor]; + let res = rpc.scan_blocks_blocking(descriptors); + log::info!("scanblocks result: {:?}", res.unwrap()); + + return Ok(()); +} + #[tokio::main] // #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), anyhow::Error> { let builder = Builder::new(tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( - "wd_network", + "smaug_network", options::Value::OptString, "Which network to use: [bitcoin, testnet, signet, regtest, mutinynet]", )) + .option(options::ConfigOption::new( + "smaug_brpc_user", + options::Value::OptString, + "Bitcoind RPC user (Required)", + )) + .option(options::ConfigOption::new( + "smaug_brpc_pass", + options::Value::OptString, + "Bitcoind RPC password (Required)", + )) .notification(messages::NotificationTopic::new(UTXO_DEPOSIT_TAG)) .notification(messages::NotificationTopic::new(UTXO_SPENT_TAG)) .rpcmethod( @@ -58,17 +98,31 @@ async fn main() -> Result<(), anyhow::Error> { configured_plugin.configuration() ); log::debug!( - "wd_network = {:?}, cln_network = {}", - configured_plugin.option("wd_network"), + "smaug_network = {:?}, cln_network = {}", + configured_plugin.option("smaug_network"), configured_plugin.configuration().network ); - let network = match configured_plugin.option("wd_network") { - Some(wd_network) => match wd_network.as_str() { + let network = match configured_plugin.option("smaug_network") { + Some(smaug_network) => match smaug_network.as_str() { Some(wdn) => wdn.to_owned(), None => configured_plugin.configuration().network, }, None => configured_plugin.configuration().network, }; + let brpc_user = match configured_plugin.option("smaug_brpc_user") { + Some(smaug_brpc_user) => match smaug_brpc_user.as_str() { + Some(wdn) => wdn.to_owned(), + None => {return Err(anyhow!("must specify smaug_brpc_user"))}, + }, + None => {return Err(anyhow!("must specify smaug_brpc_user (your bitcoind instance rpcuser)"))}, + }; + let brpc_pass = match configured_plugin.option("smaug_brpc_pass") { + Some(smaug_brpc_pass) => match smaug_brpc_pass.as_str() { + Some(wdn) => wdn.to_owned(), + None => {return Err(anyhow!("must specify smaug_brpc_pass (your bitcoind instance rpcpassword)"))}, + }, + None => {return Err(anyhow!("must specify smaug_brpc_user"))}, + }; let ln_dir: PathBuf = configured_plugin.configuration().lightning_dir.into(); // Create data dir if it does not exist fs::create_dir_all(ln_dir.join(SMAUG_DATADIR)).unwrap_or_else(|e| { @@ -105,6 +159,8 @@ async fn main() -> Result<(), anyhow::Error> { let watch_descriptor = Smaug { wallets, network: network.clone(), + brpc_user: brpc_user.clone(), + brpc_pass: brpc_pass.clone(), db_dir: ln_dir.join(SMAUG_DATADIR), }; let plugin_state = Arc::new(Mutex::new(watch_descriptor.clone())); @@ -276,7 +332,11 @@ async fn list( plugin: Plugin, // _v: serde_json::Value, ) -> Result { - let wallets = &plugin.state().lock().await.wallets; + let state = &plugin.state().lock().await; + let brpc_user = state.brpc_user.clone(); + let brpc_pass = state.brpc_pass.clone(); + let wallets = state.wallets.clone(); + scanblocks(brpc_user, brpc_pass)?; let mut result = BTreeMap::::new(); for (wallet_name, wallet) in wallets { result.insert( diff --git a/src/state.rs b/src/state.rs index da72fe1..6da9880 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,6 +13,10 @@ pub struct Smaug { pub wallets: BTreeMap, // The network relevant to our wallets pub network: String, + // Bitcoind RPC user + pub brpc_user: String, + // Bitcoind RPC password + pub brpc_pass: String, // The db path relevant to our wallets pub db_dir: PathBuf, } @@ -22,6 +26,8 @@ impl Smaug { Self { wallets: BTreeMap::new(), network: bitcoin::Network::Bitcoin.to_string(), + brpc_user: String::from("bitcoin"), + brpc_pass: String::from("password"), db_dir: PathBuf::new(), } } From 500c4d70d8a9dd4813eee7c0a63522d6f60976ea Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Sat, 11 Nov 2023 17:41:08 -0600 Subject: [PATCH 02/17] wip fix timeout --- Cargo.lock | 2 -- Cargo.toml | 4 ++-- src/main.rs | 42 +++++++++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef79aab..8ec9f56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,7 +241,6 @@ dependencies = [ [[package]] name = "bitcoincore-rpc" version = "0.17.0" -source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23806125586e999a48429d57d2d94a2a3cab696b" dependencies = [ "bitcoin-private", "bitcoincore-rpc-json", @@ -254,7 +253,6 @@ dependencies = [ [[package]] name = "bitcoincore-rpc-json" version = "0.17.0" -source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23806125586e999a48429d57d2d94a2a3cab696b" dependencies = [ "bitcoin 0.30.1", "bitcoin-private", diff --git a/Cargo.toml b/Cargo.toml index b3a87fb..b774886 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ bdk_esplora = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e454 # bdk_file_store = { version = "0.2.0" } # bdk_file_store = { path = "../bdk/crates/file_store" } bdk_file_store = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } -# bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } -bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } +bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } +# bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } clap = { version = "4.4.0", features = ["derive"] } cln-plugin = { git = "https://github.com/elementsproject/lightning", version = "0.1.4" } # cln-plugin = { path = "../../lightning/plugins" } diff --git a/src/main.rs b/src/main.rs index be20388..47743ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; +use std::time::Duration; use tokio::sync::Mutex; use anyhow::Ok; @@ -31,12 +32,7 @@ use tokio; use bdk::TransactionDetails; use smaug::state::{Smaug, State}; - -fn scanblocks<'a>( - brpc_user: String, - brpc_pass: String, -) -> Result<(), Error> -{ +fn scanblocks<'a>(brpc_user: String, brpc_pass: String) -> Result<(), Error> { // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; @@ -48,11 +44,19 @@ fn scanblocks<'a>( use bitcoincore_rpc::{Auth, Client, RpcApi}; - let rpc = Client::new("http://localhost:18443", - Auth::UserPass(brpc_user, brpc_pass) - // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) - ).unwrap(); - let descriptor = ScanBlocksRequest::Extended { desc:_mutinynet_descriptor_ext.to_string(), range: None }; + let brpc_host = "127.0.0.1"; + let brpc_port = 18443; + + let rpc = Client::new_with_timeout( + &format!("http://{}:{}", brpc_host, brpc_port), + Auth::UserPass(brpc_user, brpc_pass), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) + Duration::from_secs(3600), + ) + .unwrap(); + let descriptor = ScanBlocksRequest::Extended { + desc: _mutinynet_descriptor_ext.to_string(), + range: None, + }; let descriptors = &[descriptor]; let res = rpc.scan_blocks_blocking(descriptors); log::info!("scanblocks result: {:?}", res.unwrap()); @@ -112,16 +116,24 @@ async fn main() -> Result<(), anyhow::Error> { let brpc_user = match configured_plugin.option("smaug_brpc_user") { Some(smaug_brpc_user) => match smaug_brpc_user.as_str() { Some(wdn) => wdn.to_owned(), - None => {return Err(anyhow!("must specify smaug_brpc_user"))}, + None => return Err(anyhow!("must specify smaug_brpc_user")), }, - None => {return Err(anyhow!("must specify smaug_brpc_user (your bitcoind instance rpcuser)"))}, + None => { + return Err(anyhow!( + "must specify smaug_brpc_user (your bitcoind instance rpcuser)" + )) + } }; let brpc_pass = match configured_plugin.option("smaug_brpc_pass") { Some(smaug_brpc_pass) => match smaug_brpc_pass.as_str() { Some(wdn) => wdn.to_owned(), - None => {return Err(anyhow!("must specify smaug_brpc_pass (your bitcoind instance rpcpassword)"))}, + None => { + return Err(anyhow!( + "must specify smaug_brpc_pass (your bitcoind instance rpcpassword)" + )) + } }, - None => {return Err(anyhow!("must specify smaug_brpc_user"))}, + None => return Err(anyhow!("must specify smaug_brpc_user")), }; let ln_dir: PathBuf = configured_plugin.configuration().lightning_dir.into(); // Create data dir if it does not exist From 20be9ed6d2c05a3527de50032c2bec420e597104 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Sat, 11 Nov 2023 18:22:23 -0600 Subject: [PATCH 03/17] allow host/port from options --- Cargo.lock | 5 ++--- src/main.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++----- src/state.rs | 6 ++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ec9f56..240d5e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -815,9 +815,8 @@ dependencies = [ [[package]] name = "jsonrpc" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8128f36b47411cd3f044be8c1f5cc0c9e24d1d1bfdc45f0a57897b32513053f2" +version = "0.14.0" +source = "git+https://github.com/chrisguida/rust-jsonrpc?branch=feat/simple-http-timeout#4b3156e02bcad67e9e11ca972dadc323700188b9" dependencies = [ "base64 0.13.1", "serde", diff --git a/src/main.rs b/src/main.rs index 47743ca..67a64d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,12 @@ use tokio; use bdk::TransactionDetails; use smaug::state::{Smaug, State}; -fn scanblocks<'a>(brpc_user: String, brpc_pass: String) -> Result<(), Error> { +fn scanblocks<'a>( + brpc_host: String, + brpc_port: u16, + brpc_user: String, + brpc_pass: String, +) -> Result<(), Error> { // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; @@ -44,9 +49,6 @@ fn scanblocks<'a>(brpc_user: String, brpc_pass: String) -> Result<(), Error> { use bitcoincore_rpc::{Auth, Client, RpcApi}; - let brpc_host = "127.0.0.1"; - let brpc_port = 18443; - let rpc = Client::new_with_timeout( &format!("http://{}:{}", brpc_host, brpc_port), Auth::UserPass(brpc_user, brpc_pass), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) @@ -73,6 +75,16 @@ async fn main() -> Result<(), anyhow::Error> { options::Value::OptString, "Which network to use: [bitcoin, testnet, signet, regtest, mutinynet]", )) + .option(options::ConfigOption::new( + "smaug_brpc_host", + options::Value::String("127.0.0.1".to_owned()), + "Bitcoind RPC host (default 127.0.0.1)", + )) + .option(options::ConfigOption::new( + "smaug_brpc_port", + options::Value::Integer(8332), + "Bitcoind RPC port (default 8332)", + )) .option(options::ConfigOption::new( "smaug_brpc_user", options::Value::OptString, @@ -113,6 +125,28 @@ async fn main() -> Result<(), anyhow::Error> { }, None => configured_plugin.configuration().network, }; + let brpc_host = match configured_plugin.option("smaug_brpc_host") { + Some(smaug_brpc_host) => match smaug_brpc_host.as_str() { + Some(sbh) => sbh.to_owned(), + None => return Err(anyhow!("must specify smaug_brpc_host")), + }, + None => { + return Err(anyhow!( + "must specify smaug_brpc_host (your bitcoind instance rpc host)" + )) + } + }; + let brpc_port: u16 = match configured_plugin.option("smaug_brpc_port") { + Some(smaug_brpc_port) => match smaug_brpc_port.as_i64() { + Some(sbp) => u16::try_from(sbp)?, + None => { + return Err(anyhow!( + "must specify smaug_brpc_port (your bitcoind instance rpcport)" + )) + } + }, + None => return Err(anyhow!("must specify smaug_brpc_port")), + }; let brpc_user = match configured_plugin.option("smaug_brpc_user") { Some(smaug_brpc_user) => match smaug_brpc_user.as_str() { Some(wdn) => wdn.to_owned(), @@ -171,6 +205,8 @@ async fn main() -> Result<(), anyhow::Error> { let watch_descriptor = Smaug { wallets, network: network.clone(), + brpc_host: brpc_host.clone(), + brpc_port: brpc_port.clone(), brpc_user: brpc_user.clone(), brpc_pass: brpc_pass.clone(), db_dir: ln_dir.join(SMAUG_DATADIR), @@ -345,10 +381,14 @@ async fn list( // _v: serde_json::Value, ) -> Result { let state = &plugin.state().lock().await; + let brpc_host = state.brpc_host.clone(); + let brpc_port = state.brpc_port.clone(); let brpc_user = state.brpc_user.clone(); let brpc_pass = state.brpc_pass.clone(); + // let brpc_host = "127.0.0.1"; + // let brpc_port: u16 = 18443; let wallets = state.wallets.clone(); - scanblocks(brpc_user, brpc_pass)?; + scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass)?; let mut result = BTreeMap::::new(); for (wallet_name, wallet) in wallets { result.insert( diff --git a/src/state.rs b/src/state.rs index 6da9880..41ba3b6 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,6 +13,10 @@ pub struct Smaug { pub wallets: BTreeMap, // The network relevant to our wallets pub network: String, + // Bitcoind RPC host + pub brpc_host: String, + // Bitcoind RPC port + pub brpc_port: u16, // Bitcoind RPC user pub brpc_user: String, // Bitcoind RPC password @@ -26,6 +30,8 @@ impl Smaug { Self { wallets: BTreeMap::new(), network: bitcoin::Network::Bitcoin.to_string(), + brpc_host: String::from("127.0.0.1"), + brpc_port: 8332, brpc_user: String::from("bitcoin"), brpc_pass: String::from("password"), db_dir: PathBuf::new(), From 0b165c14636d7fd049eb044812fb5994f2585268 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Sun, 12 Nov 2023 15:30:25 -0600 Subject: [PATCH 04/17] wip call scanblocks from add not ls --- src/main.rs | 139 +++++++++++++++++++++++++++----------------------- src/state.rs | 13 ++--- src/wallet.rs | 46 ++++++++++++++++- 3 files changed, 127 insertions(+), 71 deletions(-) diff --git a/src/main.rs b/src/main.rs index 67a64d9..b088a05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ use std::sync::Arc; use std::time::Duration; use tokio::sync::Mutex; -use anyhow::Ok; +// use anyhow::Ok; use smaug::wallet::{AddArgs, DescriptorWallet, SMAUG_DATADIR, UTXO_DEPOSIT_TAG, UTXO_SPENT_TAG}; use cln_plugin::{anyhow, messages, options, Builder, Error, Plugin}; @@ -32,39 +32,40 @@ use tokio; use bdk::TransactionDetails; use smaug::state::{Smaug, State}; -fn scanblocks<'a>( - brpc_host: String, - brpc_port: u16, - brpc_user: String, - brpc_pass: String, -) -> Result<(), Error> { - // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; - // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" - let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; - let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; - let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; - let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - - extern crate bitcoincore_rpc; - - use bitcoincore_rpc::{Auth, Client, RpcApi}; - - let rpc = Client::new_with_timeout( - &format!("http://{}:{}", brpc_host, brpc_port), - Auth::UserPass(brpc_user, brpc_pass), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) - Duration::from_secs(3600), - ) - .unwrap(); - let descriptor = ScanBlocksRequest::Extended { - desc: _mutinynet_descriptor_ext.to_string(), - range: None, - }; - let descriptors = &[descriptor]; - let res = rpc.scan_blocks_blocking(descriptors); - log::info!("scanblocks result: {:?}", res.unwrap()); - - return Ok(()); -} +// fn scanblocks<'a>( +// brpc_host: String, +// brpc_port: u16, +// brpc_user: String, +// brpc_pass: String, +// ) -> Result<(), Error> { +// // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; +// // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" +// let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; +// let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; +// let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; +// let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; +// let nifty_mainnet_descriptor = "wsh(sortedmulti(2,[40c37b12/58'/0'/0'/2']xpub6FNNNqYaptuqxRkpa63obgb3Agy9hrtSkReQ4mrNhCoQBRSia6EN7kdYEZsSJK5ccEzpfpPCMcardC8Q3HEPJnE9hRCFGTKRz1KcPVSmprB/0/*,[adbeab5e/58'/0'/0'/2']xpub6ETPKtSyEY14DciERKCyd4g5YT7Cdn6zFAngcNRCH6K4Rn3ccp1GYXCkm3uawmHE5bhHgdgctGosNaqnZNvVchB3BNgbTY895WTShzXe4Fj/0/*,[d2903891/58'/0'/0'/2']xpub6F7yv4S2GMr4rffSPTpQJauPer2JhGhuj9kR9Js4AbwDdctvES5gVtAV8d3iQReKhF9JzVihJTKKRfGoNy4TXvJsPj2wmvDrTTXZ7aWdG2Y/0/*))#vwave986"; + +// extern crate bitcoincore_rpc; + +// use bitcoincore_rpc::{Auth, Client, RpcApi}; + +// let rpc = Client::new_with_timeout( +// &format!("http://{}:{}", brpc_host, brpc_port), +// Auth::UserPass(brpc_user, brpc_pass), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) +// Duration::from_secs(3600), +// ) +// .unwrap(); +// let descriptor = ScanBlocksRequest::Extended { +// desc: nifty_mainnet_descriptor.to_string(), +// range: None, +// }; +// let descriptors = &[descriptor]; +// let res = rpc.scan_blocks_blocking(descriptors); +// log::info!("scanblocks result: {:?}", res.unwrap()); + +// return Ok(()); +// } #[tokio::main] // #[tokio::main(flavor = "current_thread")] @@ -318,31 +319,47 @@ async fn add( .map_err(|e| anyhow!("error parsing args: {}", e))?; // dw.network = ); log::trace!("params = {:?}", dw); - let wallet = dw - .fetch_wallet(plugin.state().lock().await.db_dir.clone()) - .await?; - let bdk_transactions_iter = wallet.transactions(); - let mut transactions = Vec::::new(); - for bdk_transaction in bdk_transactions_iter { - log::trace!("BDK transaction = {:?}", bdk_transaction.node.tx); - transactions.push(wallet.get_tx(bdk_transaction.node.txid, true).unwrap()); - } - - if transactions.len() > 0 { - log::trace!("found some transactions: {:?}", transactions); - let new_txs = dw.update_transactions(transactions); - if new_txs.len() > 0 { - for tx in new_txs { - log::trace!("new tx found!: {:?}", tx); - dw.send_notifications_for_tx(&plugin, &wallet, tx).await?; + { + let state = &plugin.state().lock().await; + let brpc_host = state.brpc_host.clone(); + let brpc_port = state.brpc_port.clone(); + let brpc_user = state.brpc_user.clone(); + let brpc_pass = state.brpc_pass.clone(); + match dw.scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass) { + Ok(_) => { + log::info!("scan succeeded"); } - } else { - log::debug!("no new txs this time"); - } + Err(e) => { + log::info!("scan failed: {}", e); + } + }; } - log::trace!("waiting for wallet lock"); + // let wallet = dw + // .fetch_wallet(plugin.state().lock().await.db_dir.clone()) + // .await?; + // let bdk_transactions_iter = wallet.transactions(); + // let mut transactions = Vec::::new(); + // for bdk_transaction in bdk_transactions_iter { + // log::trace!("BDK transaction = {:?}", bdk_transaction.node.tx); + // transactions.push(wallet.get_tx(bdk_transaction.node.txid, true).unwrap()); + // } + + // if transactions.len() > 0 { + // log::trace!("found some transactions: {:?}", transactions); + // let new_txs = dw.update_transactions(transactions); + // if new_txs.len() > 0 { + // for tx in new_txs { + // log::trace!("new tx found!: {:?}", tx); + // dw.send_notifications_for_tx(&plugin, &wallet, tx).await?; + // } + // } else { + // log::debug!("no new txs this time"); + // } + // } + log::info!("waiting for wallet lock"); plugin.state().lock().await.add_descriptor_wallet(&dw)?; + log::info!("add_descriptor_wallet"); let wallets_str = json!(plugin.state().lock().await.wallets).to_string(); let rpc_file = plugin.configuration().rpc_file; let p = Path::new(&rpc_file); @@ -358,12 +375,12 @@ async fn add( })) .await .map_err(|e| anyhow!("Error calling listdatastore: {:?}", e))?; - log::trace!("wallet added"); + log::info!("wallet added"); let message = format!( "Wallet with deterministic name {} successfully added", &dw.get_name()? ); - log::trace!("returning"); + log::info!("returning"); Ok(json!(message)) } @@ -381,14 +398,8 @@ async fn list( // _v: serde_json::Value, ) -> Result { let state = &plugin.state().lock().await; - let brpc_host = state.brpc_host.clone(); - let brpc_port = state.brpc_port.clone(); - let brpc_user = state.brpc_user.clone(); - let brpc_pass = state.brpc_pass.clone(); - // let brpc_host = "127.0.0.1"; - // let brpc_port: u16 = 18443; + let wallets = state.wallets.clone(); - scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass)?; let mut result = BTreeMap::::new(); for (wallet_name, wallet) in wallets { result.insert( diff --git a/src/state.rs b/src/state.rs index 41ba3b6..0970212 100644 --- a/src/state.rs +++ b/src/state.rs @@ -11,17 +11,17 @@ pub type State = Arc>; pub struct Smaug { /// A collection of descriptors the plugin is watching. pub wallets: BTreeMap, - // The network relevant to our wallets + /// The network relevant to our wallets pub network: String, - // Bitcoind RPC host + /// Bitcoind RPC host pub brpc_host: String, - // Bitcoind RPC port + /// Bitcoind RPC port pub brpc_port: u16, - // Bitcoind RPC user + /// Bitcoind RPC user pub brpc_user: String, - // Bitcoind RPC password + /// Bitcoind RPC password pub brpc_pass: String, - // The db path relevant to our wallets + /// The db path relevant to our wallets pub db_dir: PathBuf, } @@ -42,6 +42,7 @@ impl Smaug { &mut self, wallet: &DescriptorWallet, ) -> Result<(), anyhow::Error> { + log::info!("add_descriptor_wallet called"); self.wallets.insert(wallet.get_name()?, wallet.clone()); Ok(()) } diff --git a/src/wallet.rs b/src/wallet.rs index e216579..c247a58 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -10,11 +10,15 @@ use bdk::{ }; use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_file_store::Store; +use bitcoincore_rpc::{ + bitcoin::BlockHash, + bitcoincore_rpc_json::{ScanBlocksRequest, ScanBlocksResult}, +}; use clap::{command, Parser}; use cln_plugin::{Error, Plugin}; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{collections::BTreeMap, fmt, io::Write, path::PathBuf}; +use std::{collections::BTreeMap, fmt, io::Write, path::PathBuf, time::Duration}; use crate::state::State; @@ -231,7 +235,9 @@ impl DescriptorWallet { } pub fn get_name(&self) -> Result { + log::info!("get_name called"); let network = get_network(self.network.clone()); + log::info!("get_network succeeded"); Ok(wallet_name_from_descriptor( &self.descriptor, self.change_descriptor.as_ref(), @@ -326,6 +332,44 @@ impl DescriptorWallet { return Ok(wallet); } + pub fn scanblocks<'a>( + &self, + brpc_host: String, + brpc_port: u16, + brpc_user: String, + brpc_pass: String, + ) -> Result<(), Error> { + // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; + // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" + let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; + let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; + let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; + let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; + let nifty_mainnet_descriptor = "wsh(sortedmulti(2,[40c37b12/58'/0'/0'/2']xpub6FNNNqYaptuqxRkpa63obgb3Agy9hrtSkReQ4mrNhCoQBRSia6EN7kdYEZsSJK5ccEzpfpPCMcardC8Q3HEPJnE9hRCFGTKRz1KcPVSmprB/0/*,[adbeab5e/58'/0'/0'/2']xpub6ETPKtSyEY14DciERKCyd4g5YT7Cdn6zFAngcNRCH6K4Rn3ccp1GYXCkm3uawmHE5bhHgdgctGosNaqnZNvVchB3BNgbTY895WTShzXe4Fj/0/*,[d2903891/58'/0'/0'/2']xpub6F7yv4S2GMr4rffSPTpQJauPer2JhGhuj9kR9Js4AbwDdctvES5gVtAV8d3iQReKhF9JzVihJTKKRfGoNy4TXvJsPj2wmvDrTTXZ7aWdG2Y/0/*))#vwave986"; + + extern crate bitcoincore_rpc; + + use bitcoincore_rpc::{Auth, Client, RpcApi}; + + let rpc = Client::new_with_timeout( + &format!("http://{}:{}", brpc_host, brpc_port), + Auth::UserPass(brpc_user, brpc_pass), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) + Duration::from_secs(3600), + ) + .unwrap(); + // let descriptor = ScanBlocksRequest::Extended { + // desc: nifty_mainnet_descriptor.to_string(), + // range: None, + // }; + // let descriptor = ScanBlocksRequest::Single(nifty_mainnet_descriptor.to_string()); + let descriptor = ScanBlocksRequest::Single(self.descriptor.clone()); + let descriptors = &[descriptor]; + let res = rpc.scan_blocks_blocking(descriptors); + log::info!("scanblocks result: {:?}", res.unwrap()); + + return Ok(()); + } + // assume we own all inputs, ie sent from our wallet. all inputs and outputs should generate coin movement bookkeeper events async fn spend_tx_notify<'a>( &self, From 103e266aaca6a78cc6366664f2c95f5a9b3ce1af Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Sun, 12 Nov 2023 16:50:07 -0600 Subject: [PATCH 05/17] filter false positives --- Cargo.lock | 2 ++ Cargo.toml | 4 ++-- src/main.rs | 40 +--------------------------------------- src/wallet.rs | 28 +++++++++++++++++----------- 4 files changed, 22 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 240d5e7..1aefd9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,6 +241,7 @@ dependencies = [ [[package]] name = "bitcoincore-rpc" version = "0.17.0" +source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23927f3e8b3bd413e0bf8b7c42b9531345bdd1a3" dependencies = [ "bitcoin-private", "bitcoincore-rpc-json", @@ -253,6 +254,7 @@ dependencies = [ [[package]] name = "bitcoincore-rpc-json" version = "0.17.0" +source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23927f3e8b3bd413e0bf8b7c42b9531345bdd1a3" dependencies = [ "bitcoin 0.30.1", "bitcoin-private", diff --git a/Cargo.toml b/Cargo.toml index b774886..b3a87fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ bdk_esplora = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e454 # bdk_file_store = { version = "0.2.0" } # bdk_file_store = { path = "../bdk/crates/file_store" } bdk_file_store = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } -bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } -# bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } +# bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } +bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } clap = { version = "4.4.0", features = ["derive"] } cln-plugin = { git = "https://github.com/elementsproject/lightning", version = "0.1.4" } # cln-plugin = { path = "../../lightning/plugins" } diff --git a/src/main.rs b/src/main.rs index b088a05..824b6c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ #[macro_use] extern crate serde_json; -use bitcoincore_rpc::bitcoincore_rpc_json::ScanBlocksRequest; use clap::error::ErrorKind; use clap::{CommandFactory, Parser, Subcommand}; use cln_rpc::model::DatastoreMode; @@ -20,10 +19,8 @@ use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; -use std::time::Duration; use tokio::sync::Mutex; -// use anyhow::Ok; use smaug::wallet::{AddArgs, DescriptorWallet, SMAUG_DATADIR, UTXO_DEPOSIT_TAG, UTXO_SPENT_TAG}; use cln_plugin::{anyhow, messages, options, Builder, Error, Plugin}; @@ -32,41 +29,6 @@ use tokio; use bdk::TransactionDetails; use smaug::state::{Smaug, State}; -// fn scanblocks<'a>( -// brpc_host: String, -// brpc_port: u16, -// brpc_user: String, -// brpc_pass: String, -// ) -> Result<(), Error> { -// // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; -// // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" -// let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; -// let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; -// let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; -// let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; -// let nifty_mainnet_descriptor = "wsh(sortedmulti(2,[40c37b12/58'/0'/0'/2']xpub6FNNNqYaptuqxRkpa63obgb3Agy9hrtSkReQ4mrNhCoQBRSia6EN7kdYEZsSJK5ccEzpfpPCMcardC8Q3HEPJnE9hRCFGTKRz1KcPVSmprB/0/*,[adbeab5e/58'/0'/0'/2']xpub6ETPKtSyEY14DciERKCyd4g5YT7Cdn6zFAngcNRCH6K4Rn3ccp1GYXCkm3uawmHE5bhHgdgctGosNaqnZNvVchB3BNgbTY895WTShzXe4Fj/0/*,[d2903891/58'/0'/0'/2']xpub6F7yv4S2GMr4rffSPTpQJauPer2JhGhuj9kR9Js4AbwDdctvES5gVtAV8d3iQReKhF9JzVihJTKKRfGoNy4TXvJsPj2wmvDrTTXZ7aWdG2Y/0/*))#vwave986"; - -// extern crate bitcoincore_rpc; - -// use bitcoincore_rpc::{Auth, Client, RpcApi}; - -// let rpc = Client::new_with_timeout( -// &format!("http://{}:{}", brpc_host, brpc_port), -// Auth::UserPass(brpc_user, brpc_pass), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) -// Duration::from_secs(3600), -// ) -// .unwrap(); -// let descriptor = ScanBlocksRequest::Extended { -// desc: nifty_mainnet_descriptor.to_string(), -// range: None, -// }; -// let descriptors = &[descriptor]; -// let res = rpc.scan_blocks_blocking(descriptors); -// log::info!("scanblocks result: {:?}", res.unwrap()); - -// return Ok(()); -// } - #[tokio::main] // #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), anyhow::Error> { @@ -315,7 +277,7 @@ async fn add( // v: serde_json::Value, args: AddArgs, ) -> Result { - let mut dw = DescriptorWallet::from_args(args, plugin.state().lock().await.network.clone()) + let dw = DescriptorWallet::from_args(args, plugin.state().lock().await.network.clone()) .map_err(|e| anyhow!("error parsing args: {}", e))?; // dw.network = ); log::trace!("params = {:?}", dw); diff --git a/src/wallet.rs b/src/wallet.rs index c247a58..4e3cb9c 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -12,7 +12,9 @@ use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_file_store::Store; use bitcoincore_rpc::{ bitcoin::BlockHash, - bitcoincore_rpc_json::{ScanBlocksRequest, ScanBlocksResult}, + bitcoincore_rpc_json::{ + ScanBlocksOptions, ScanBlocksRequest, ScanBlocksRequestDescriptor, ScanBlocksResult, + }, }; use clap::{command, Parser}; use cln_plugin::{Error, Plugin}; @@ -345,9 +347,6 @@ impl DescriptorWallet { let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - let nifty_mainnet_descriptor = "wsh(sortedmulti(2,[40c37b12/58'/0'/0'/2']xpub6FNNNqYaptuqxRkpa63obgb3Agy9hrtSkReQ4mrNhCoQBRSia6EN7kdYEZsSJK5ccEzpfpPCMcardC8Q3HEPJnE9hRCFGTKRz1KcPVSmprB/0/*,[adbeab5e/58'/0'/0'/2']xpub6ETPKtSyEY14DciERKCyd4g5YT7Cdn6zFAngcNRCH6K4Rn3ccp1GYXCkm3uawmHE5bhHgdgctGosNaqnZNvVchB3BNgbTY895WTShzXe4Fj/0/*,[d2903891/58'/0'/0'/2']xpub6F7yv4S2GMr4rffSPTpQJauPer2JhGhuj9kR9Js4AbwDdctvES5gVtAV8d3iQReKhF9JzVihJTKKRfGoNy4TXvJsPj2wmvDrTTXZ7aWdG2Y/0/*))#vwave986"; - - extern crate bitcoincore_rpc; use bitcoincore_rpc::{Auth, Client, RpcApi}; @@ -357,14 +356,21 @@ impl DescriptorWallet { Duration::from_secs(3600), ) .unwrap(); - // let descriptor = ScanBlocksRequest::Extended { - // desc: nifty_mainnet_descriptor.to_string(), - // range: None, - // }; - // let descriptor = ScanBlocksRequest::Single(nifty_mainnet_descriptor.to_string()); - let descriptor = ScanBlocksRequest::Single(self.descriptor.clone()); + let descriptor = ScanBlocksRequestDescriptor::Extended { + desc: self.descriptor.clone().to_string(), + range: None, + }; let descriptors = &[descriptor]; - let res = rpc.scan_blocks_blocking(descriptors); + let request = ScanBlocksRequest { + scanobjects: descriptors, + start_height: None, + stop_height: None, + filtertype: None, + options: Some(ScanBlocksOptions { + filter_false_positives: Some(true), + }), + }; + let res = rpc.scan_blocks_blocking(request); log::info!("scanblocks result: {:?}", res.unwrap()); return Ok(()); From b9cd910775dc0f8a90e948583253784d28fb3399 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Mon, 20 Nov 2023 16:12:13 -0600 Subject: [PATCH 06/17] wip change to use new scanblocks rpc, doesn't build --- Cargo.lock | 8 +- Cargo.toml | 12 +-- src/main.rs | 31 ++++--- src/wallet.rs | 229 ++++++++++++++++++++++++++++++-------------------- 4 files changed, 163 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1aefd9b..cc835f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.30.1" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e99ff7289b20a7385f66a0feda78af2fc119d28fb56aea8886a9cd0a4abdd75" +checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" dependencies = [ "bech32", "bitcoin-private", @@ -241,7 +241,6 @@ dependencies = [ [[package]] name = "bitcoincore-rpc" version = "0.17.0" -source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23927f3e8b3bd413e0bf8b7c42b9531345bdd1a3" dependencies = [ "bitcoin-private", "bitcoincore-rpc-json", @@ -254,9 +253,8 @@ dependencies = [ [[package]] name = "bitcoincore-rpc-json" version = "0.17.0" -source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23927f3e8b3bd413e0bf8b7c42b9531345bdd1a3" dependencies = [ - "bitcoin 0.30.1", + "bitcoin 0.30.2", "bitcoin-private", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index b3a87fb..d2e41f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,17 +9,17 @@ edition = "2021" anyhow = "1.0" base64 = "0.21.2" # bdk = "0.27.1" -# bdk = { version = "0.28", default-features=false, features = ["std", "key-value-db","async-interface", "use-esplora-async"] } +# bdk = { version = "0.29", default-features=false, features = ["std", "key-value-db","async-interface", "use-esplora-async", "rpc"] } bdk = { git = "https://github.com/bitcoindevkit/bdk", version = "1.0.0-alpha.1", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } -# bdk = { path = "../bdk/crates/bdk" } +# bdk = { path = "../../lib/bdk/crates/bdk" } # bdk_esplora = { version = "0.3.0", features = ["async-https"] } -# bdk_esplora = { path = "../bdk/crates/esplora", features = ["async-https"] } +# bdk_esplora = { path = "../../lib/bdk/crates/esplora", features = ["async-https"] } bdk_esplora = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } # bdk_file_store = { version = "0.2.0" } -# bdk_file_store = { path = "../bdk/crates/file_store" } +# bdk_file_store = { path = "../../lib/bdk/crates/file_store" } bdk_file_store = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } -# bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } -bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } +bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } +# bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } clap = { version = "4.4.0", features = ["derive"] } cln-plugin = { git = "https://github.com/elementsproject/lightning", version = "0.1.4" } # cln-plugin = { path = "../../lightning/plugins" } diff --git a/src/main.rs b/src/main.rs index 824b6c9..d8f39fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -287,7 +287,10 @@ async fn add( let brpc_port = state.brpc_port.clone(); let brpc_user = state.brpc_user.clone(); let brpc_pass = state.brpc_pass.clone(); - match dw.scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass) { + match dw + .scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass) + .await + { Ok(_) => { log::info!("scan succeeded"); } @@ -410,8 +413,10 @@ async fn delete( async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Result<(), Error> { log::trace!("Got a block_added notification: {}", v); - log::trace!("Smaug state!!! {:?}", plugin.state().lock().await.wallets.clone()); - + log::trace!( + "Smaug state!!! {:?}", + plugin.state().lock().await.wallets.clone() + ); log::trace!("waiting for db_dir lock in block_handler"); let db_dir = { @@ -426,19 +431,17 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res log::trace!("db_dir in block_handler: {:?}", &db_dir); log::trace!("acquired wallet lock in block_handler"); for (_dw_desc, dw) in descriptor_wallets.iter_mut() { - log::trace!("fetching wallet in block_handler: {:?}", dw); - - let wallet = dw - .fetch_wallet(db_dir.clone()) - .await?; - log::trace!("...fetched wallet in block_handler"); - let bdk_transactions_iter = wallet.transactions(); + // let wallet = dw + // .fetch_wallet(plugin.state().lock().await.db_dir.clone()) + // .await?; + // let bdk_transactions_iter = wallet.transactions(); let mut transactions = Vec::::new(); - for bdk_transaction in bdk_transactions_iter { - log::trace!("BDK transaction = {:?}", bdk_transaction.node.tx); - transactions.push(wallet.get_tx(bdk_transaction.node.txid, true).unwrap()); - } + // for bdk_transaction in bdk_transactions_iter { + // log::trace!("BDK transaction = {:?}", bdk_transaction.node.tx); + // transactions.push(wallet.get_tx(bdk_transaction.node.txid, true).unwrap()); + // } + // let transactions = vec![]; if transactions.len() > 0 { log::trace!( "found some new transactions in new block! : {:?}", diff --git a/src/wallet.rs b/src/wallet.rs index 4e3cb9c..dfe1218 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2,7 +2,7 @@ use anyhow::anyhow; use bdk::{ bitcoin::{ secp256k1::{All, Secp256k1}, - Network, Txid, + Network, Transaction, Txid, }, chain::{keychain::LocalChangeSet, ConfirmationTime, ConfirmationTimeAnchor}, wallet::wallet_name_from_descriptor, @@ -15,9 +15,11 @@ use bitcoincore_rpc::{ bitcoincore_rpc_json::{ ScanBlocksOptions, ScanBlocksRequest, ScanBlocksRequestDescriptor, ScanBlocksResult, }, + Auth, Client, RpcApi, }; use clap::{command, Parser}; use cln_plugin::{Error, Plugin}; +use cln_rpc::ClnRpc; use serde::{Deserialize, Serialize}; use serde_json::json; use std::{collections::BTreeMap, fmt, io::Write, path::PathBuf, time::Duration}; @@ -41,6 +43,14 @@ pub enum WatchError { InvalidFormat(String), } +#[derive(Debug, Deserialize, Serialize, Clone)] +struct RpcConnection { + pub host: String, + pub port: u16, + pub user: String, + pub pass: String, +} + impl std::error::Error for WatchError {} impl std::fmt::Display for WatchError { @@ -248,93 +258,100 @@ impl DescriptorWallet { )?) } - pub async fn fetch_wallet<'a>( - &self, - db_dir: PathBuf, - ) -> Result>>, Error> - { - log::trace!("creating path"); - let db_filename = self.get_name()?; - let db_path = db_dir - // .join(DATADIR) - .join(format!("{}.db", db_filename,)); - log::trace!("searching for path: {:?}", db_path); - let db = Store::::new_from_path(SMAUG_DATADIR.as_bytes(), db_path)?; - log::trace!("db created!"); - // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; - // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" - let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; - let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; - let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; - let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - // let external_descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; - // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)"; - - // let external_descriptor = mutinynet_descriptor_ext; - // let internal_descriptor = mutinynet_descriptor_int; - let external_descriptor = self.descriptor.clone(); - let internal_descriptor = self.change_descriptor.clone(); - let mut wallet = Wallet::new( - &external_descriptor, - internal_descriptor.as_ref(), - db, - self.get_network()?, - )?; - log::trace!("wallet created!"); - - let balance = wallet.get_balance(); - log::trace!("Wallet balance before syncing: {} sats", balance.total()); - - log::trace!("Syncing..."); - log::debug!("using network: {}", json!(self.network).as_str().unwrap()); - log::debug!( - "using esplora url: {}", - get_network_url(json!(self.network).as_str().unwrap()).as_str() - ); - let client = - // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; - esplora_client::Builder::new( - get_network_url( - json!(self.network).as_str().unwrap() - ).as_str() - ).build_async()?; - - let local_chain = wallet.checkpoints(); - let keychain_spks = wallet - .spks_of_all_keychains() - .into_iter() - .map(|(k, k_spks)| { - let mut once = Some(()); - let mut stdout = std::io::stdout(); - let k_spks = k_spks - .inspect(move |(spk_i, _)| match once.take() { - Some(_) => log::debug!("\nScanning keychain [{:?}]", k), - None => log::trace!(" {:<3}", spk_i), - }) - .inspect(move |_| stdout.flush().expect("must flush")); - (k, k_spks) - }) - .collect(); - log::trace!("Finished scanning"); - let update = client - .scan( - local_chain, - keychain_spks, - [], - [], - STOP_GAP, - PARALLEL_REQUESTS, - ) - .await?; - wallet.apply_update(update)?; - wallet.commit()?; - - let balance = wallet.get_balance(); - log::trace!("Wallet balance after syncing: {} sats", balance.total()); - return Ok(wallet); - } + // pub async fn fetch_wallet<'a>( + // &self, + // db_dir: PathBuf, + // ) -> Result>>, Error> + // { + // log::trace!("creating path"); + // let db_filename = self.get_name()?; + // let db_path = db_dir + // // .join(DATADIR) + // .join(format!("{}.db", db_filename,)); + // log::trace!("searching for path: {:?}", db_path); + // let db = Store::::new_from_path(SMAUG_DATADIR.as_bytes(), db_path)?; + // log::trace!("db created!"); + // // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; + // // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" + // let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; + // let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; + // let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; + // let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; + // // let external_descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; + // // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)"; + + // // let external_descriptor = mutinynet_descriptor_ext; + // // let internal_descriptor = mutinynet_descriptor_int; + // let external_descriptor = self.descriptor.clone(); + // let internal_descriptor = self.change_descriptor.clone(); + // let mut wallet = Wallet::new( + // &external_descriptor, + // internal_descriptor.as_ref(), + // db, + // self.get_network()?, + // )?; + // log::trace!("wallet created!"); + + // let balance = wallet.get_balance(); + // log::trace!("Wallet balance before syncing: {} sats", balance.total()); + + // log::trace!("Syncing..."); + // log::debug!("using network: {}", json!(self.network).as_str().unwrap()); + // log::debug!( + // "using esplora url: {}", + // get_network_url(json!(self.network).as_str().unwrap()).as_str() + // ); + // // let client = + // // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; + // // esplora_client::Builder::new( + // // get_network_url( + // // json!(self.network).as_str().unwrap() + // // ).as_str() + // // ).build_async()?; + // let client = + // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; + // esplora_client::Builder::new( + // get_network_url( + // json!(self.network).as_str().unwrap() + // ).as_str() + // ).build_async()?; + + // let local_chain = wallet.checkpoints(); + // let keychain_spks = wallet + // .spks_of_all_keychains() + // .into_iter() + // .map(|(k, k_spks)| { + // let mut once = Some(()); + // let mut stdout = std::io::stdout(); + // let k_spks = k_spks + // .inspect(move |(spk_i, _)| match once.take() { + // Some(_) => log::debug!("\nScanning keychain [{:?}]", k), + // None => log::trace!(" {:<3}", spk_i), + // }) + // .inspect(move |_| stdout.flush().expect("must flush")); + // (k, k_spks) + // }) + // .collect(); + // log::trace!("Finished scanning"); + // let update = client + // .scan( + // local_chain, + // keychain_spks, + // [], + // [], + // STOP_GAP, + // PARALLEL_REQUESTS, + // ) + // .await?; + // wallet.apply_update(update)?; + // wallet.commit()?; + + // let balance = wallet.get_balance(); + // log::trace!("Wallet balance after syncing: {} sats", balance.total()); + // return Ok(wallet); + // } - pub fn scanblocks<'a>( + pub async fn scanblocks<'a>( &self, brpc_host: String, brpc_port: u16, @@ -348,11 +365,9 @@ impl DescriptorWallet { let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - use bitcoincore_rpc::{Auth, Client, RpcApi}; - let rpc = Client::new_with_timeout( &format!("http://{}:{}", brpc_host, brpc_port), - Auth::UserPass(brpc_user, brpc_pass), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) + Auth::UserPass(brpc_user.clone(), brpc_pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) Duration::from_secs(3600), ) .unwrap(); @@ -370,12 +385,42 @@ impl DescriptorWallet { filter_false_positives: Some(true), }), }; - let res = rpc.scan_blocks_blocking(request); - log::info!("scanblocks result: {:?}", res.unwrap()); + let res = rpc.scan_blocks_blocking(request)?; + log::info!("scanblocks result: {:?}", res); + + let conn = RpcConnection { + host: brpc_host, + port: brpc_port, + user: brpc_user, + pass: brpc_pass, + }; + + for bh in res.relevant_blocks { + self.get_relevant_txs(bh, &conn).await?; + } return Ok(()); } + async fn get_relevant_txs( + &self, + bh: BlockHash, + conn: &RpcConnection, + ) -> Result, Error> { + let relevant_txs: Vec = vec![]; + let rpc = Client::new( + &format!("http://{}:{}", conn.host, conn.port), + Auth::UserPass(conn.user.clone(), conn.pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) + // Duration::from_secs(3600), + )?; + let block = rpc.get_block(&bh)?; + for tx in block.txdata { + // let tx_bdk = tx.into(); + relevant_txs.push(tx); + } + Ok(relevant_txs) + } + // assume we own all inputs, ie sent from our wallet. all inputs and outputs should generate coin movement bookkeeper events async fn spend_tx_notify<'a>( &self, From 17d23e2651059ffbc205517abe6f22d9bd22e25a Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Tue, 5 Dec 2023 16:05:24 -0600 Subject: [PATCH 07/17] use bdk wallet_rpc custom example, builds now --- Cargo.lock | 730 +----------------------------------------- Cargo.toml | 9 +- src/main.rs | 132 ++++---- src/wallet.rs | 862 ++++++++++++++++++++++++++++---------------------- 4 files changed, 569 insertions(+), 1164 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc835f8..263ce8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,17 +80,6 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" -[[package]] -name = "async-trait" -version = "0.1.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -126,11 +115,10 @@ checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bdk" -version = "1.0.0-alpha.1" -source = "git+https://github.com/bitcoindevkit/bdk?rev=8f38e96e4542db2378e2e64cd9289638ee86ba1a#8f38e96e4542db2378e2e64cd9289638ee86ba1a" +version = "1.0.0-alpha.2" dependencies = [ "bdk_chain", - "bitcoin 0.29.2", + "bitcoin 0.30.2", "getrandom", "js-sys", "log", @@ -142,29 +130,16 @@ dependencies = [ [[package]] name = "bdk_chain" -version = "0.5.0" -source = "git+https://github.com/bitcoindevkit/bdk?rev=8f38e96e4542db2378e2e64cd9289638ee86ba1a#8f38e96e4542db2378e2e64cd9289638ee86ba1a" +version = "0.6.0" dependencies = [ - "bitcoin 0.29.2", + "bitcoin 0.30.2", "miniscript", "serde", ] -[[package]] -name = "bdk_esplora" -version = "0.3.0" -source = "git+https://github.com/bitcoindevkit/bdk?rev=8f38e96e4542db2378e2e64cd9289638ee86ba1a#8f38e96e4542db2378e2e64cd9289638ee86ba1a" -dependencies = [ - "async-trait", - "bdk_chain", - "esplora-client", - "futures", -] - [[package]] name = "bdk_file_store" version = "0.2.0" -source = "git+https://github.com/bitcoindevkit/bdk?rev=8f38e96e4542db2378e2e64cd9289638ee86ba1a#8f38e96e4542db2378e2e64cd9289638ee86ba1a" dependencies = [ "bdk_chain", "bincode", @@ -192,7 +167,6 @@ version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ - "base64 0.13.1", "bech32", "bitcoin_hashes 0.11.0", "secp256k1 0.24.3", @@ -205,6 +179,7 @@ version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" dependencies = [ + "base64 0.13.1", "bech32", "bitcoin-private", "bitcoin_hashes 0.12.0", @@ -260,12 +235,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" @@ -278,12 +247,6 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - [[package]] name = "bytes" version = "1.4.0" @@ -387,46 +350,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -461,65 +384,6 @@ dependencies = [ "libc", ] -[[package]] -name = "esplora-client" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e11244e7fd8b0beee0a3c62137c4bd9f756fe2c492ccf93171f81467b59200" -dependencies = [ - "bitcoin 0.29.2", - "log", - "reqwest", - "serde", - "ureq", -] - -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - [[package]] name = "futures" version = "0.3.28" @@ -626,31 +490,6 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" -[[package]] -name = "h2" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "heck" version = "0.4.1" @@ -684,109 +523,12 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.9", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - [[package]] name = "is-terminal" version = "0.4.9" @@ -823,12 +565,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.147" @@ -853,19 +589,14 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniscript" -version = "9.0.2" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b106477a0709e2da253e5559ba4ab20a272f8577f1eefff72f3a905b5d35f5" +checksum = "1eb102b66b2127a872dbcc73095b7b47aeb9d92f7b03c2b2298253ffc82c7594" dependencies = [ - "bitcoin 0.29.2", + "bitcoin 0.30.2", + "bitcoin-private", "serde", ] @@ -889,24 +620,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "num_cpus" version = "1.16.0" @@ -932,56 +645,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" -[[package]] -name = "openssl" -version = "0.10.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - [[package]] name = "pin-project-lite" version = "0.2.12" @@ -994,12 +657,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1054,15 +711,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" version = "1.9.3" @@ -1092,59 +740,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" -[[package]] -name = "reqwest" -version = "0.11.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" -dependencies = [ - "base64 0.21.2", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tokio-socks", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1157,70 +752,19 @@ version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags 2.4.0", + "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] -[[package]] -name = "rustls" -version = "0.21.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.3", - "sct", -] - -[[package]] -name = "rustls-webpki" -version = "0.100.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "secp256k1" version = "0.24.3" @@ -1228,7 +772,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "bitcoin_hashes 0.11.0", - "rand", "secp256k1-sys 0.6.1", "serde", ] @@ -1263,29 +806,6 @@ dependencies = [ "cc", ] -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.183" @@ -1317,18 +837,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "slab" version = "0.4.8" @@ -1345,7 +853,6 @@ dependencies = [ "anyhow", "base64 0.21.2", "bdk", - "bdk_esplora", "bdk_file_store", "bitcoincore-rpc", "clap", @@ -1358,16 +865,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.3" @@ -1378,23 +875,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "socks" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" -dependencies = [ - "byteorder", - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "strsim" version = "0.10.0" @@ -1412,19 +892,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys", -] - [[package]] name = "termcolor" version = "1.2.0" @@ -1434,41 +901,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "thiserror" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.31.0" @@ -1481,7 +913,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.3", + "socket2", "tokio-macros", "windows-sys", ] @@ -1497,28 +929,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-socks" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" -dependencies = [ - "either", - "futures-util", - "thiserror", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.14" @@ -1544,12 +954,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" version = "0.1.37" @@ -1570,90 +974,18 @@ dependencies = [ "once_cell", ] -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "ureq" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" -dependencies = [ - "base64 0.21.2", - "flate2", - "log", - "once_cell", - "rustls", - "rustls-webpki 0.100.1", - "serde", - "serde_json", - "socks", - "url", - "webpki-roots", -] - -[[package]] -name = "url" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1685,18 +1017,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -1726,25 +1046,6 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.1", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1841,12 +1142,3 @@ name = "windows_x86_64_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] diff --git a/Cargo.toml b/Cargo.toml index d2e41f6..5ec8c46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,17 @@ anyhow = "1.0" base64 = "0.21.2" # bdk = "0.27.1" # bdk = { version = "0.29", default-features=false, features = ["std", "key-value-db","async-interface", "use-esplora-async", "rpc"] } -bdk = { git = "https://github.com/bitcoindevkit/bdk", version = "1.0.0-alpha.1", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } +# bdk = { git = "https://github.com/bitcoindevkit/bdk", version = "1.0.0-alpha.1", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } +# bdk = "1.0.0-alpha.2" # bdk = { path = "../../lib/bdk/crates/bdk" } +bdk = { path = "../bdk/crates/bdk" } # bdk_esplora = { version = "0.3.0", features = ["async-https"] } # bdk_esplora = { path = "../../lib/bdk/crates/esplora", features = ["async-https"] } -bdk_esplora = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } +# bdk_esplora = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } # bdk_file_store = { version = "0.2.0" } # bdk_file_store = { path = "../../lib/bdk/crates/file_store" } -bdk_file_store = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } +bdk_file_store = { path = "../bdk/crates/file_store" } +# bdk_file_store = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } # bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } clap = { version = "4.4.0", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index d8f39fd..c93521b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ #[macro_use] extern crate serde_json; +use bdk::chain::tx_graph::CanonicalTx; +use bdk::chain::ConfirmationTimeAnchor; use clap::error::ErrorKind; use clap::{CommandFactory, Parser, Subcommand}; use cln_rpc::model::DatastoreMode; @@ -26,7 +28,7 @@ use smaug::wallet::{AddArgs, DescriptorWallet, SMAUG_DATADIR, UTXO_DEPOSIT_TAG, use cln_plugin::{anyhow, messages, options, Builder, Error, Plugin}; use tokio; -use bdk::TransactionDetails; +use bdk::bitcoin::Transaction; use smaug::state::{Smaug, State}; #[tokio::main] @@ -277,50 +279,52 @@ async fn add( // v: serde_json::Value, args: AddArgs, ) -> Result { - let dw = DescriptorWallet::from_args(args, plugin.state().lock().await.network.clone()) + let mut dw = DescriptorWallet::from_args(args, plugin.state().lock().await.network.clone()) .map_err(|e| anyhow!("error parsing args: {}", e))?; // dw.network = ); log::trace!("params = {:?}", dw); - { - let state = &plugin.state().lock().await; - let brpc_host = state.brpc_host.clone(); - let brpc_port = state.brpc_port.clone(); - let brpc_user = state.brpc_user.clone(); - let brpc_pass = state.brpc_pass.clone(); - match dw - .scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass) - .await - { - Ok(_) => { - log::info!("scan succeeded"); - } - Err(e) => { - log::info!("scan failed: {}", e); - } - }; - } - // let wallet = dw - // .fetch_wallet(plugin.state().lock().await.db_dir.clone()) - // .await?; - // let bdk_transactions_iter = wallet.transactions(); - // let mut transactions = Vec::::new(); - // for bdk_transaction in bdk_transactions_iter { - // log::trace!("BDK transaction = {:?}", bdk_transaction.node.tx); - // transactions.push(wallet.get_tx(bdk_transaction.node.txid, true).unwrap()); - // } - - // if transactions.len() > 0 { - // log::trace!("found some transactions: {:?}", transactions); - // let new_txs = dw.update_transactions(transactions); - // if new_txs.len() > 0 { - // for tx in new_txs { - // log::trace!("new tx found!: {:?}", tx); - // dw.send_notifications_for_tx(&plugin, &wallet, tx).await?; - // } - // } else { - // log::debug!("no new txs this time"); + // { + let state = &plugin.state().lock().await; + let brpc_host = state.brpc_host.clone(); + let brpc_port = state.brpc_port.clone(); + let brpc_user = state.brpc_user.clone(); + let brpc_pass = state.brpc_pass.clone(); + let db_dir = state.db_dir.clone(); + // match dw + // .scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass) + // .await + // { + // Ok(_) => { + // log::info!("scan succeeded"); + // } + // Err(e) => { + // log::info!("scan failed: {}", e); // } + // }; // } + let dw_clone = dw.clone(); + let wallet = dw_clone + .fetch_wallet(db_dir, brpc_host, brpc_port, brpc_user, brpc_pass) + .await?; + let bdk_transactions_iter = wallet.transactions(); + let mut transactions = Vec::>::new(); + for bdk_transaction in bdk_transactions_iter { + log::trace!("BDK transaction = {:?}", bdk_transaction.tx_node.tx); + transactions.push(bdk_transaction); + } + + if transactions.len() > 0 { + log::trace!("found some transactions: {:?}", transactions); + let new_txs = dw.update_transactions(transactions); + if new_txs.len() > 0 { + for tx in new_txs { + log::trace!("new tx found!: {:?}", tx); + dw.send_notifications_for_tx(&plugin, &wallet, tx).await?; + } + } else { + log::debug!("no new txs this time"); + } + } log::info!("waiting for wallet lock"); plugin.state().lock().await.add_descriptor_wallet(&dw)?; @@ -417,12 +421,17 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res "Smaug state!!! {:?}", plugin.state().lock().await.wallets.clone() ); - - log::trace!("waiting for db_dir lock in block_handler"); - let db_dir = { - let state = plugin.state().lock().await; - state.db_dir.clone() - }; + let state = &plugin.state().lock().await; + let brpc_host = state.brpc_host.clone(); + let brpc_port = state.brpc_port.clone(); + let brpc_user = state.brpc_user.clone(); + let brpc_pass = state.brpc_pass.clone(); + let db_dir = state.db_dir.clone(); + // log::trace!("waiting for db_dir lock in block_handler"); + // let db_dir = { + // let state = plugin.state().lock().await; + // state.db_dir.clone() + // }; log::trace!("waiting for wallet lock in block_handler"); let state = &mut plugin.state().lock().await; @@ -431,17 +440,28 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res log::trace!("db_dir in block_handler: {:?}", &db_dir); log::trace!("acquired wallet lock in block_handler"); for (_dw_desc, dw) in descriptor_wallets.iter_mut() { - // let wallet = dw - // .fetch_wallet(plugin.state().lock().await.db_dir.clone()) - // .await?; - // let bdk_transactions_iter = wallet.transactions(); - let mut transactions = Vec::::new(); - // for bdk_transaction in bdk_transactions_iter { - // log::trace!("BDK transaction = {:?}", bdk_transaction.node.tx); - // transactions.push(wallet.get_tx(bdk_transaction.node.txid, true).unwrap()); - // } + log::trace!("fetching wallet in block_handler: {:?}", dw); + + let dw_clone = dw.clone(); + let wallet = dw_clone + .fetch_wallet( + db_dir.clone(), + brpc_host.clone(), + brpc_port.clone(), + brpc_user.clone(), + brpc_pass.clone(), + ) + .await?; + + // let wallet = dw.fetch_wallet(db_dir.clone()).await?; + log::trace!("...fetched wallet in block_handler"); + let bdk_transactions_iter = wallet.transactions(); + let mut transactions = Vec::>::new(); + for bdk_transaction in bdk_transactions_iter { + log::trace!("BDK transaction = {:?}", bdk_transaction.tx_node.tx); + transactions.push(bdk_transaction); + } - // let transactions = vec![]; if transactions.len() > 0 { log::trace!( "found some new transactions in new block! : {:?}", diff --git a/src/wallet.rs b/src/wallet.rs index dfe1218..0cf14ce 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,17 +1,18 @@ -use anyhow::anyhow; +use anyhow::{anyhow, Chain}; use bdk::{ bitcoin::{ secp256k1::{All, Secp256k1}, Network, Transaction, Txid, }, - chain::{keychain::LocalChangeSet, ConfirmationTime, ConfirmationTimeAnchor}, + chain::{ + tx_graph::CanonicalTx, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, + }, wallet::wallet_name_from_descriptor, - KeychainKind, TransactionDetails, Wallet, + Wallet, }; -use bdk_esplora::{esplora_client, EsploraAsyncExt}; +// use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_file_store::Store; use bitcoincore_rpc::{ - bitcoin::BlockHash, bitcoincore_rpc_json::{ ScanBlocksOptions, ScanBlocksRequest, ScanBlocksRequestDescriptor, ScanBlocksResult, }, @@ -19,10 +20,9 @@ use bitcoincore_rpc::{ }; use clap::{command, Parser}; use cln_plugin::{Error, Plugin}; -use cln_rpc::ClnRpc; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{collections::BTreeMap, fmt, io::Write, path::PathBuf, time::Duration}; +use std::{collections::BTreeMap, fmt, path::PathBuf, time::Duration}; use crate::state::State; @@ -123,7 +123,7 @@ pub struct DescriptorWallet { pub gap: Option, // pub last_synced: Option, // #[serde(skip_serializing, skip_deserializing)] - pub transactions: BTreeMap, + pub transactions: BTreeMap, pub network: Option, } impl DescriptorWallet { @@ -227,15 +227,16 @@ impl DescriptorWallet { // self.last_synced = Some(last_synced); // } - pub fn update_transactions( + pub fn update_transactions<'a>( &mut self, - transactions: Vec, - ) -> Vec { + transactions: Vec>, + ) -> Vec> { let mut new_txs = vec![]; for tx in transactions { - if !self.transactions.contains_key(&tx.txid) { + if !self.transactions.contains_key(&tx.tx_node.txid) { new_txs.push(tx.clone()); - self.transactions.insert(tx.txid, tx); + self.transactions + .insert(tx.tx_node.txid.clone(), tx.tx_node.tx.clone()); } } new_txs @@ -258,124 +259,125 @@ impl DescriptorWallet { )?) } - // pub async fn fetch_wallet<'a>( - // &self, - // db_dir: PathBuf, - // ) -> Result>>, Error> - // { - // log::trace!("creating path"); - // let db_filename = self.get_name()?; - // let db_path = db_dir - // // .join(DATADIR) - // .join(format!("{}.db", db_filename,)); - // log::trace!("searching for path: {:?}", db_path); - // let db = Store::::new_from_path(SMAUG_DATADIR.as_bytes(), db_path)?; - // log::trace!("db created!"); - // // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; - // // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" - // let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; - // let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; - // let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; - // let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - // // let external_descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; - // // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)"; - - // // let external_descriptor = mutinynet_descriptor_ext; - // // let internal_descriptor = mutinynet_descriptor_int; - // let external_descriptor = self.descriptor.clone(); - // let internal_descriptor = self.change_descriptor.clone(); - // let mut wallet = Wallet::new( - // &external_descriptor, - // internal_descriptor.as_ref(), - // db, - // self.get_network()?, - // )?; - // log::trace!("wallet created!"); - - // let balance = wallet.get_balance(); - // log::trace!("Wallet balance before syncing: {} sats", balance.total()); - - // log::trace!("Syncing..."); - // log::debug!("using network: {}", json!(self.network).as_str().unwrap()); - // log::debug!( - // "using esplora url: {}", - // get_network_url(json!(self.network).as_str().unwrap()).as_str() - // ); - // // let client = - // // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; - // // esplora_client::Builder::new( - // // get_network_url( - // // json!(self.network).as_str().unwrap() - // // ).as_str() - // // ).build_async()?; - // let client = - // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; - // esplora_client::Builder::new( - // get_network_url( - // json!(self.network).as_str().unwrap() - // ).as_str() - // ).build_async()?; - - // let local_chain = wallet.checkpoints(); - // let keychain_spks = wallet - // .spks_of_all_keychains() - // .into_iter() - // .map(|(k, k_spks)| { - // let mut once = Some(()); - // let mut stdout = std::io::stdout(); - // let k_spks = k_spks - // .inspect(move |(spk_i, _)| match once.take() { - // Some(_) => log::debug!("\nScanning keychain [{:?}]", k), - // None => log::trace!(" {:<3}", spk_i), - // }) - // .inspect(move |_| stdout.flush().expect("must flush")); - // (k, k_spks) - // }) - // .collect(); - // log::trace!("Finished scanning"); - // let update = client - // .scan( - // local_chain, - // keychain_spks, - // [], - // [], - // STOP_GAP, - // PARALLEL_REQUESTS, - // ) - // .await?; - // wallet.apply_update(update)?; - // wallet.commit()?; - - // let balance = wallet.get_balance(); - // log::trace!("Wallet balance after syncing: {} sats", balance.total()); - // return Ok(wallet); - // } - - pub async fn scanblocks<'a>( + pub async fn fetch_wallet<'a>( &self, + db_dir: PathBuf, brpc_host: String, brpc_port: u16, brpc_user: String, brpc_pass: String, - ) -> Result<(), Error> { + // ) -> Result>>, Error> + ) -> Result>, Error> { + log::trace!("creating path"); + let db_filename = self.get_name()?; + let db_path = db_dir + // .join(DATADIR) + .join(format!("{}.db", db_filename,)); + log::trace!("searching for path: {:?}", db_path); + // let db = Store::::new_from_path(SMAUG_DATADIR.as_bytes(), db_path)?; + let db = Store::::new_from_path(SMAUG_DATADIR.as_bytes(), db_path)?; + log::trace!("db created!"); // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - - let rpc = Client::new_with_timeout( - &format!("http://{}:{}", brpc_host, brpc_port), + // let external_descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; + // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)"; + + // let external_descriptor = mutinynet_descriptor_ext; + // let internal_descriptor = mutinynet_descriptor_int; + let external_descriptor = self.descriptor.clone(); + let internal_descriptor = self.change_descriptor.clone(); + let mut wallet = Wallet::new( + &external_descriptor, + internal_descriptor.as_ref(), + db, + self.get_network()?, + )?; + log::trace!("wallet created!"); + + let balance = wallet.get_balance(); + log::trace!("Wallet balance before syncing: {} sats", balance.total()); + + log::trace!("Syncing..."); + log::debug!("using network: {}", json!(self.network).as_str().unwrap()); + // log::debug!( + // "using esplora url: {}", + // get_network_url(json!(self.network).as_str().unwrap()).as_str() + // ); + // let client = + // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; + // esplora_client::Builder::new( + // get_network_url( + // json!(self.network).as_str().unwrap() + // ).as_str() + // ).build_async()?; + // let client = + // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; + // esplora_client::Builder::new( + // get_network_url( + // json!(self.network).as_str().unwrap() + // ).as_str() + // ).build_async()?; + + // let local_chain = wallet.checkpoints(); + // let keychain_spks = wallet + // .spks_of_all_keychains() + // .into_iter() + // .map(|(k, k_spks)| { + // let mut once = Some(()); + // let mut stdout = std::io::stdout(); + // let k_spks = k_spks + // .inspect(move |(spk_i, _)| match once.take() { + // Some(_) => log::debug!("\nScanning keychain [{:?}]", k), + // None => log::trace!(" {:<3}", spk_i), + // }) + // .inspect(move |_| stdout.flush().expect("must flush")); + // (k, k_spks) + // }) + // .collect(); + // log::trace!("Finished scanning"); + // let update = client + // .scan( + // local_chain, + // keychain_spks, + // [], + // [], + // STOP_GAP, + // PARALLEL_REQUESTS, + // ) + // .await?; + // wallet.apply_update(update)?; + // wallet.commit()?; + + let rpc_client = Client::new_with_timeout( + &format!("http://{}:{}", brpc_host.clone(), brpc_port.clone()), Auth::UserPass(brpc_user.clone(), brpc_pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) Duration::from_secs(3600), - ) - .unwrap(); - let descriptor = ScanBlocksRequestDescriptor::Extended { - desc: self.descriptor.clone().to_string(), + )?; + + println!( + "Connected to Bitcoin Core RPC at {:?}", + rpc_client.get_blockchain_info().unwrap() + ); + + let external_descriptor = ScanBlocksRequestDescriptor::Extended { + desc: external_descriptor.to_string(), range: None, }; - let descriptors = &[descriptor]; + let mut descriptors_vec = vec![external_descriptor]; + + if let Some(id) = internal_descriptor { + descriptors_vec.push(ScanBlocksRequestDescriptor::Extended { + desc: id.to_string(), + range: None, + }); + } + + // let descriptors = &[external_descriptor, internal_descriptor]; + let descriptors = &descriptors_vec[..]; let request = ScanBlocksRequest { scanobjects: descriptors, start_height: None, @@ -385,146 +387,244 @@ impl DescriptorWallet { filter_false_positives: Some(true), }), }; - let res = rpc.scan_blocks_blocking(request)?; - log::info!("scanblocks result: {:?}", res); - - let conn = RpcConnection { - host: brpc_host, - port: brpc_port, - user: brpc_user, - pass: brpc_pass, - }; + let res: ScanBlocksResult = rpc_client.scan_blocks_blocking(request)?; + // let res: ScanBlocksResult = ScanBlocksResult { + // from_height: 0, + // to_height: 819333, + // relevant_blocks: vec![ + // BlockHash::from_str( + // "000000000000000000047a0baacb20399819c82d6983a545d849625c040380e5", + // )?, + // BlockHash::from_str( + // "0000000000000000000114f60040b10b192bc37d3f1f5777686509898106105e", + // )?, + // BlockHash::from_str( + // "000000000000000000031359d3aff6ecfb95995bc9b84b079302836db45174ed", + // )?, + // ], + // }; + println!("scanblocks result: {:?}", res); + println!("wallet = {:?}", wallet); + + wallet.set_lookahead_for_all(20)?; + + // let chain_tip = wallet.latest_checkpoint(); + // let mut emitter = match chain_tip { + // Some(cp) => Emitter::from_checkpoint(&rpc_client, cp), + // None => Emitter::from_height(&rpc_client, args[5].parse::()?), + // }; + + let mut prev_block_id = None; for bh in res.relevant_blocks { - self.get_relevant_txs(bh, &conn).await?; + // self.get_relevant_txs(bh, &conn); + let block = rpc_client.get_block(&bh)?; + let height: u32 = block.bip34_block_height()?.try_into().unwrap(); + println!("adding block height {} to wallet", height); + wallet.apply_block_relevant(block.clone(), prev_block_id, height)?; + wallet.commit()?; + prev_block_id = Some(BlockId { height, hash: bh }); } - return Ok(()); - } + // while let Some((height, block)) = emitter.next_block()? { + // println!("Applying block {} at height {}", block.block_hash(), height); + // wallet.apply_block_relevant(block, height)?; + // wallet.commit()?; + // } - async fn get_relevant_txs( - &self, - bh: BlockHash, - conn: &RpcConnection, - ) -> Result, Error> { - let relevant_txs: Vec = vec![]; - let rpc = Client::new( - &format!("http://{}:{}", conn.host, conn.port), - Auth::UserPass(conn.user.clone(), conn.pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) - // Duration::from_secs(3600), - )?; - let block = rpc.get_block(&bh)?; - for tx in block.txdata { - // let tx_bdk = tx.into(); - relevant_txs.push(tx); - } - Ok(relevant_txs) + // println!("About to apply unconfirmed transactions: ..."); + // let unconfirmed_txs = emitter.mempool()?; + // println!("Applying unconfirmed transactions: ..."); + // wallet.batch_insert_relevant_unconfirmed(unconfirmed_txs.iter().map(|(tx, time)| (tx, *time))); + // wallet.commit()?; + + let balance = wallet.get_balance(); + println!("Wallet balance after syncing: {} sats", balance.total()); + + let balance = wallet.get_balance(); + log::trace!("Wallet balance after syncing: {} sats", balance.total()); + return Ok(wallet); } + // pub async fn scanblocks<'a>( + // &self, + // brpc_host: String, + // brpc_port: u16, + // brpc_user: String, + // brpc_pass: String, + // ) -> Result<(), Error> { + // // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; + // // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" + // let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; + // let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; + // let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; + // let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; + + // let rpc = Client::new_with_timeout( + // &format!("http://{}:{}", brpc_host, brpc_port), + // Auth::UserPass(brpc_user.clone(), brpc_pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) + // Duration::from_secs(3600), + // )?; + // let descriptor = ScanBlocksRequestDescriptor::Extended { + // desc: self.descriptor.clone().to_string(), + // range: None, + // }; + // let descriptors = &[descriptor]; + // let request = ScanBlocksRequest { + // scanobjects: descriptors, + // start_height: None, + // stop_height: None, + // filtertype: None, + // options: Some(ScanBlocksOptions { + // filter_false_positives: Some(true), + // }), + // }; + // let res = rpc.scan_blocks_blocking(request)?; + // log::info!("scanblocks result: {:?}", res); + + // let conn = RpcConnection { + // host: brpc_host, + // port: brpc_port, + // user: brpc_user, + // pass: brpc_pass, + // }; + + // for bh in res.relevant_blocks { + // self.get_relevant_txs(bh, &conn).await?; + // } + + // return Ok(()); + // } + + // async fn get_relevant_txs( + // &self, + // bh: BlockHash, + // conn: &RpcConnection, + // ) -> Result, Error> { + // let mut relevant_txs: Vec = vec![]; + // let rpc = Client::new( + // &format!("http://{}:{}", conn.host, conn.port), + // Auth::UserPass(conn.user.clone(), conn.pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) + // // Duration::from_secs(3600), + // )?; + // let block = rpc.get_block(&bh)?; + // for tx in block.txdata { + // // let tx_bdk = tx.into(); + // let chain_update = + // CheckPoint::from_header(&block.header, height).into_update(false); + // let chain_changeset = chain + // .apply_update(chain_update) + // .expect("must always apply as we receive blocks in order from emitter"); + // let graph_changeset = graph.apply_block_relevant(block, height); + // (chain_changeset, graph_changeset) + // relevant_txs.push(tx); + // } + // Ok(relevant_txs) + // } + // assume we own all inputs, ie sent from our wallet. all inputs and outputs should generate coin movement bookkeeper events async fn spend_tx_notify<'a>( &self, plugin: &Plugin, - wallet: &Wallet>>, - tx: &TransactionDetails, + wallet: &Wallet>, + tx: &CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>, ) -> Result<(), Error> { - match tx.transaction.clone() { - Some(t) => { - // send spent notification for each input - for input in t.input.iter() { - if let Some(po) = wallet.tx_graph().get_txout(input.previous_output) { - match tx.confirmation_time { - ConfirmationTime::Unconfirmed { .. } => { - continue; - } - ConfirmationTime::Confirmed { height, time } => { - let acct = format!("smaug:{}", self.get_name()?); - let amount = po.value; - let outpoint = format!("{}", input.previous_output.to_string()); - log::trace!("outpoint = {}", format!("{}", outpoint)); - let onchain_spend = json!({UTXO_SPENT_TAG: { - "account": acct, - "outpoint": outpoint, - "spending_txid": tx.txid.to_string(), - "amount_msat": Self::sats_to_msats(amount), - "coin_type": "bcrt", - "timestamp": format!("{}", time), - "blockheight": format!("{}", height), - }}); - log::trace!("INSIDE SEND SPEND NOTIFICATION ON SMAUG SIDE"); - let cloned_plugin = plugin.clone(); - tokio::spawn(async move { - if let Err(e) = cloned_plugin - .send_custom_notification( - UTXO_SPENT_TAG.to_string(), - onchain_spend, - ) - .await - { - log::error!("Error sending custom notification: {:?}", e); - } - }); + // match tx { + // Some(t) => { + // send spent notification for each input + for input in tx.tx_node.tx.input.iter() { + if let Some(po) = wallet.tx_graph().get_txout(input.previous_output) { + match tx.chain_position { + // ChainPosition::Confirmed(a) => Self::Confirmed { + // height: a.confirmation_height, + // time: a.confirmation_time, + // }, + // ChainPosition::Unconfirmed(_) => Self::Unconfirmed { last_seen: 0 }, + // } + // match ConfirmationTime::from(&) { + // ConfirmationTime::Unconfirmed { .. } => { + ChainPosition::Unconfirmed(_) => { + continue; + } + ChainPosition::Confirmed(a) => { + let acct = format!("smaug:{}", self.get_name()?); + let amount = po.value; + let outpoint = format!("{}", input.previous_output.to_string()); + log::trace!("outpoint = {}", format!("{}", outpoint)); + let onchain_spend = json!({UTXO_SPENT_TAG: { + "account": acct, + "outpoint": outpoint, + "spending_txid": tx.tx_node.txid, + "amount_msat": Self::sats_to_msats(amount), + "coin_type": "bcrt", + "timestamp": format!("{}", a.confirmation_time), + "blockheight": format!("{}", a.confirmation_height), + }}); + log::trace!("INSIDE SEND SPEND NOTIFICATION ON SMAUG SIDE"); + let cloned_plugin = plugin.clone(); + tokio::spawn(async move { + if let Err(e) = cloned_plugin + .send_custom_notification(UTXO_SPENT_TAG.to_string(), onchain_spend) + .await + { + log::error!("Error sending custom notification: {:?}", e); } - } - } else { - log::trace!("Transaction prevout not found"); + }); } } + } else { + log::trace!("Transaction prevout not found"); + } + } - // send deposit notification for every output, since all of them are spends from our wallet - for (vout, output) in t.output.iter().enumerate() { - match tx.confirmation_time { - ConfirmationTime::Unconfirmed { .. } => { - continue; - } - ConfirmationTime::Confirmed { height, time } => { - let acct: String; - let transfer_from: String; - if wallet.is_mine(&output.script_pubkey) { - acct = format!("smaug:{}", self.get_name()?); - transfer_from = "external".to_owned(); - } else { - transfer_from = format!("smaug:{}", self.get_name()?); - acct = "external".to_owned(); - } - let amount = output.value; - let outpoint = format!("{}:{}", tx.txid.to_string(), vout.to_string()); - log::trace!( - "outpoint = {}", - format!("{}:{}", tx.txid.to_string(), vout.to_string()) - ); - let onchain_deposit = json!({UTXO_DEPOSIT_TAG:{ - "account": acct, - "transfer_from": transfer_from, - "outpoint": outpoint, - "spending_txid": tx.txid.to_string(), - "amount_msat": Self::sats_to_msats(amount), - "coin_type": "bcrt", - "timestamp": format!("{}", time), - "blockheight": format!("{}", height), - }}); - log::trace!("INSIDE SEND DEPOSIT NOTIFICATION ON SMAUG SIDE"); - let cloned_plugin = plugin.clone(); - tokio::spawn(async move { - if let Err(e) = cloned_plugin - .send_custom_notification( - UTXO_DEPOSIT_TAG.to_string(), - onchain_deposit, - ) - .await - { - log::error!("Error sending custom notification: {:?}", e); - } - }); - } + // send deposit notification for every output, since all of them are spends from our wallet + for (vout, output) in tx.tx_node.tx.output.iter().enumerate() { + match tx.chain_position { + ChainPosition::Unconfirmed(_) => { + continue; + } + ChainPosition::Confirmed(a) => { + let acct: String; + let transfer_from: String; + if wallet.is_mine(&output.script_pubkey) { + acct = format!("smaug:{}", self.get_name()?); + transfer_from = "external".to_owned(); + } else { + transfer_from = format!("smaug:{}", self.get_name()?); + acct = "external".to_owned(); } + let amount = output.value; + let outpoint = format!("{}:{}", tx.tx_node.txid.to_string(), vout.to_string()); + log::trace!("outpoint = {}", format!("{}:{}", tx.tx_node.txid, vout)); + let onchain_deposit = json!({UTXO_DEPOSIT_TAG:{ + "account": acct, + "transfer_from": transfer_from, + "outpoint": outpoint, + "spending_txid": tx.tx_node.txid, + "amount_msat": Self::sats_to_msats(amount), + "coin_type": "bcrt", + "timestamp": format!("{}", a.confirmation_time), + "blockheight": format!("{}", a.confirmation_height), + }}); + log::trace!("INSIDE SEND DEPOSIT NOTIFICATION ON SMAUG SIDE"); + let cloned_plugin = plugin.clone(); + tokio::spawn(async move { + if let Err(e) = cloned_plugin + .send_custom_notification(UTXO_DEPOSIT_TAG.to_string(), onchain_deposit) + .await + { + log::error!("Error sending custom notification: {:?}", e); + } + }); } } - None => { - log::debug!("TransactionDetails is missing a Transaction"); - } } Ok(()) + // } + // None => { + // log::debug!("Transaction is missing a Transaction"); + // } + // } } // assume we own no inputs. sent to us from someone else's wallet. @@ -533,70 +633,69 @@ impl DescriptorWallet { async fn receive_tx_notify<'a>( &self, plugin: &Plugin, - wallet: &Wallet>>, - tx: &TransactionDetails, + wallet: &Wallet>, + tx: &CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>, ) -> Result<(), Error> { - match tx.transaction.clone() { - Some(t) => { - for (vout, output) in t.output.iter().enumerate() { - if wallet.is_mine(&output.script_pubkey) { - match tx.confirmation_time { - ConfirmationTime::Unconfirmed { .. } => { - continue; - } - ConfirmationTime::Confirmed { height, time } => { - let acct: String; - let transfer_from: String; - if wallet.is_mine(&output.script_pubkey) { - acct = format!("smaug:{}", self.get_name()?); - transfer_from = "external".to_owned(); - } else { - // transfer_from = format!( - // "smaug:{}", - // self.get_name? - // ); - // acct = "external".to_owned(); - continue; - } - let amount = output.value; - let outpoint = - format!("{}:{}", tx.txid.to_string(), vout.to_string()); - log::trace!( - "outpoint = {}", - format!("{}:{}", tx.txid.to_string(), vout.to_string()) - ); - let onchain_deposit = json!({UTXO_DEPOSIT_TAG: { - "account": acct, - "transfer_from": transfer_from, - "outpoint": outpoint, - "spending_txid": tx.txid.to_string(), - "amount_msat": Self::sats_to_msats(amount), - "coin_type": "bcrt", - "timestamp": format!("{}", time), - "blockheight": format!("{}", height), - }}); - log::trace!("INSIDE SEND DEPOSIT NOTIFICATION ON SMAUG SIDE"); - let cloned_plugin = plugin.clone(); - tokio::spawn(async move { - if let Err(e) = cloned_plugin - .send_custom_notification( - UTXO_DEPOSIT_TAG.to_string(), - onchain_deposit, - ) - .await - { - log::error!("Error sending custom notification: {:?}", e); - } - }); - } + // match tx.transaction.clone() { + // Some(t) => { + for (vout, output) in tx.tx_node.tx.output.iter().enumerate() { + if wallet.is_mine(&output.script_pubkey) { + match tx.chain_position { + ChainPosition::Unconfirmed(_) => { + continue; + } + ChainPosition::Confirmed(a) => { + let acct: String; + let transfer_from: String; + if wallet.is_mine(&output.script_pubkey) { + acct = format!("smaug:{}", self.get_name()?); + transfer_from = "external".to_owned(); + } else { + // transfer_from = format!( + // "smaug:{}", + // self.get_name? + // ); + // acct = "external".to_owned(); + continue; } + let amount = output.value; + let outpoint = format!("{}:{}", tx.tx_node.txid, vout); + log::trace!( + "outpoint = {}", + format!("{}:{}", tx.tx_node.txid.to_string(), vout.to_string()) + ); + let onchain_deposit = json!({UTXO_DEPOSIT_TAG: { + "account": acct, + "transfer_from": transfer_from, + "outpoint": outpoint, + "spending_txid": tx.tx_node.txid.to_string(), + "amount_msat": Self::sats_to_msats(amount), + "coin_type": "bcrt", + "timestamp": format!("{}", a.confirmation_time), + "blockheight": format!("{}", a.confirmation_height), + }}); + log::trace!("INSIDE SEND DEPOSIT NOTIFICATION ON SMAUG SIDE"); + let cloned_plugin = plugin.clone(); + tokio::spawn(async move { + if let Err(e) = cloned_plugin + .send_custom_notification( + UTXO_DEPOSIT_TAG.to_string(), + onchain_deposit, + ) + .await + { + log::error!("Error sending custom notification: {:?}", e); + } + }); } } } - None => { - log::debug!("TransactionDetails is missing a Transaction"); - } } + // } + // None => { + // log::debug!("Transaction is missing a Transaction"); + // } + // } Ok(()) } @@ -607,99 +706,40 @@ impl DescriptorWallet { async fn shared_tx_notify<'a>( &self, plugin: &Plugin, - wallet: &Wallet>>, - tx: &TransactionDetails, + wallet: &Wallet>, + tx: &CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>, ) -> Result<(), Error> { - match tx.transaction.clone() { - Some(t) => { - // send spent notification for each input that spends one of our outputs - for input in t.input.iter() { - if let Some(po) = wallet.tx_graph().get_txout(input.previous_output) { - match tx.confirmation_time { - ConfirmationTime::Unconfirmed { .. } => { - continue; - } - ConfirmationTime::Confirmed { height, time } => { - if wallet.is_mine(&po.script_pubkey) { - let acct = format!("smaug:{}", self.get_name()?); - let amount = po.value; - let outpoint = format!("{}", input.previous_output.to_string()); - log::trace!("outpoint = {}", format!("{}", outpoint)); - let onchain_spend = json!({UTXO_SPENT_TAG: { - "account": acct, - "outpoint": outpoint, - "spending_txid": tx.txid.to_string(), - "amount_msat": Self::sats_to_msats(amount), - "coin_type": "bcrt", - "timestamp": format!("{}", time), - "blockheight": format!("{}", height), - }}); - log::trace!("INSIDE SEND SPEND NOTIFICATION ON SMAUG SIDE"); - let cloned_plugin = plugin.clone(); - tokio::spawn(async move { - if let Err(e) = cloned_plugin - .send_custom_notification( - UTXO_SPENT_TAG.to_string(), - onchain_spend, - ) - .await - { - log::error!( - "Error sending custom notification: {:?}", - e - ); - } - }); - } - } - } - } else { - log::debug!("Transaction prevout not found"); + // match tx.transaction.clone() { + // Some(t) => { + // send spent notification for each input that spends one of our outputs + for input in tx.tx_node.input.iter() { + if let Some(po) = wallet.tx_graph().get_txout(input.previous_output) { + match tx.chain_position { + ChainPosition::Unconfirmed(_) => { + continue; } - } - - // send deposit notification for every output, since all of them *might be* spends from our wallet. - // store them in a temp account and let the user update later as needed. - for (vout, output) in t.output.iter().enumerate() { - match tx.confirmation_time { - ConfirmationTime::Unconfirmed { .. } => { - continue; - } - ConfirmationTime::Confirmed { height, time } => { - let acct: String; - let transfer_from: String; - let our_acct = format!("smaug:{}:shared_outputs", self.get_name()?); - let ext_acct = "external".to_owned(); - if wallet.is_mine(&output.script_pubkey) { - acct = our_acct; - transfer_from = ext_acct; - } else { - acct = ext_acct; - transfer_from = our_acct; - } - let amount = output.value; - let outpoint = format!("{}:{}", tx.txid.to_string(), vout.to_string()); - log::trace!( - "outpoint = {}", - format!("{}:{}", tx.txid.to_string(), vout.to_string()) - ); - let onchain_deposit = json!({UTXO_DEPOSIT_TAG: { - "account": acct, - "transfer_from": transfer_from, - "outpoint": outpoint, - "spending_txid": tx.txid.to_string(), - "amount_msat": Self::sats_to_msats(amount), - "coin_type": "bcrt", - "timestamp": format!("{}", time), - "blockheight": format!("{}", height), + ChainPosition::Confirmed(a) => { + if wallet.is_mine(&po.script_pubkey) { + let acct = format!("smaug:{}", self.get_name()?); + let amount = po.value; + let outpoint = format!("{}", input.previous_output.to_string()); + log::trace!("outpoint = {}", format!("{}", outpoint)); + let onchain_spend = json!({UTXO_SPENT_TAG: { + "account": acct, + "outpoint": outpoint, + "spending_txid": tx.tx_node.txid.to_string(), + "amount_msat": Self::sats_to_msats(amount), + "coin_type": "bcrt", + "timestamp": format!("{}", a.confirmation_time), + "blockheight": format!("{}", a.confirmation_height), }}); - log::trace!("INSIDE SEND DEPOSIT NOTIFICATION ON SMAUG SIDE"); + log::trace!("INSIDE SEND SPEND NOTIFICATION ON SMAUG SIDE"); let cloned_plugin = plugin.clone(); tokio::spawn(async move { if let Err(e) = cloned_plugin .send_custom_notification( - UTXO_DEPOSIT_TAG.to_string(), - onchain_deposit, + UTXO_SPENT_TAG.to_string(), + onchain_spend, ) .await { @@ -709,23 +749,73 @@ impl DescriptorWallet { } } } + } else { + log::debug!("Transaction prevout not found"); } - None => { - log::debug!("TransactionDetails is missing a Transaction"); + } + + // send deposit notification for every output, since all of them *might be* spends from our wallet. + // store them in a temp account and let the user update later as needed. + for (vout, output) in tx.tx_node.tx.output.iter().enumerate() { + match tx.chain_position { + ChainPosition::Unconfirmed(_) => { + continue; + } + ChainPosition::Confirmed(a) => { + let acct: String; + let transfer_from: String; + let our_acct = format!("smaug:{}:shared_outputs", self.get_name()?); + let ext_acct = "external".to_owned(); + if wallet.is_mine(&output.script_pubkey) { + acct = our_acct; + transfer_from = ext_acct; + } else { + acct = ext_acct; + transfer_from = our_acct; + } + let amount = output.value; + let outpoint = format!("{}:{}", tx.tx_node.txid, vout); + log::trace!("outpoint = {}", format!("{}:{}", tx.tx_node.txid, vout)); + let onchain_deposit = json!({UTXO_DEPOSIT_TAG: { + "account": acct, + "transfer_from": transfer_from, + "outpoint": outpoint, + "spending_txid": tx.tx_node.txid, + "amount_msat": Self::sats_to_msats(amount), + "coin_type": "bcrt", + "timestamp": format!("{}", a.confirmation_time), + "blockheight": format!("{}", a.confirmation_height), + }}); + log::trace!("INSIDE SEND DEPOSIT NOTIFICATION ON SMAUG SIDE"); + let cloned_plugin = plugin.clone(); + tokio::spawn(async move { + if let Err(e) = cloned_plugin + .send_custom_notification(UTXO_DEPOSIT_TAG.to_string(), onchain_deposit) + .await + { + log::error!("Error sending custom notification: {:?}", e); + } + }); + } } } + // } + // None => { + // log::debug!("Transaction is missing a Transaction"); + // } + // } Ok(()) } pub async fn send_notifications_for_tx<'a>( &self, plugin: &Plugin, - wallet: &Wallet>>, - tx: TransactionDetails, + wallet: &Wallet>, + tx: CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>, ) -> Result<(), Error> { - log::debug!("sending notifs for txid/tx: {:?} {:?}", tx.txid, tx); + log::debug!("sending notifs for txid/tx: {:?} {:?}", tx.tx_node.txid, tx); // we own all inputs - if tx.clone().transaction.unwrap().input.iter().all(|x| { + if tx.clone().tx_node.tx.input.iter().all(|x| { match wallet.tx_graph().get_txout(x.previous_output) { Some(o) => { log::trace!( @@ -745,7 +835,7 @@ impl DescriptorWallet { self.spend_tx_notify(plugin, wallet, &tx).await?; } else // we own no inputs - if !tx.clone().transaction.unwrap().input.iter().any(|x| { + if !tx.clone().tx_node.tx.input.iter().any(|x| { match wallet.tx_graph().get_txout(x.previous_output) { Some(o) => { log::trace!( From d40ffdc45cb2533fcf173ff0589a403dc0df37ce Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Tue, 5 Dec 2023 17:01:39 -0600 Subject: [PATCH 08/17] remove print statements --- src/wallet.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 0cf14ce..039bfe8 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -358,10 +358,10 @@ impl DescriptorWallet { Duration::from_secs(3600), )?; - println!( - "Connected to Bitcoin Core RPC at {:?}", - rpc_client.get_blockchain_info().unwrap() - ); + // println!( + // "Connected to Bitcoin Core RPC at {:?}", + // rpc_client.get_blockchain_info().unwrap() + // ); let external_descriptor = ScanBlocksRequestDescriptor::Extended { desc: external_descriptor.to_string(), @@ -403,8 +403,8 @@ impl DescriptorWallet { // )?, // ], // }; - println!("scanblocks result: {:?}", res); - println!("wallet = {:?}", wallet); + // println!("scanblocks result: {:?}", res); + // println!("wallet = {:?}", wallet); wallet.set_lookahead_for_all(20)?; @@ -420,7 +420,7 @@ impl DescriptorWallet { // self.get_relevant_txs(bh, &conn); let block = rpc_client.get_block(&bh)?; let height: u32 = block.bip34_block_height()?.try_into().unwrap(); - println!("adding block height {} to wallet", height); + // println!("adding block height {} to wallet", height); wallet.apply_block_relevant(block.clone(), prev_block_id, height)?; wallet.commit()?; prev_block_id = Some(BlockId { height, hash: bh }); @@ -438,8 +438,8 @@ impl DescriptorWallet { // wallet.batch_insert_relevant_unconfirmed(unconfirmed_txs.iter().map(|(tx, time)| (tx, *time))); // wallet.commit()?; - let balance = wallet.get_balance(); - println!("Wallet balance after syncing: {} sats", balance.total()); + // let balance = wallet.get_balance(); + // println!("Wallet balance after syncing: {} sats", balance.total()); let balance = wallet.get_balance(); log::trace!("Wallet balance after syncing: {} sats", balance.total()); From 79585b6a58f8021a0d92bc262cf8f609b30c61ac Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Tue, 5 Dec 2023 21:26:42 -0600 Subject: [PATCH 09/17] fix agressive locking and don't rescan every block. we still rescan if the same wallet is added again --- src/main.rs | 62 +++++++++++++++++++++++---------------------------- src/wallet.rs | 48 ++++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/main.rs b/src/main.rs index c93521b..7e4d09c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -284,25 +284,17 @@ async fn add( // dw.network = ); log::trace!("params = {:?}", dw); // { - let state = &plugin.state().lock().await; - let brpc_host = state.brpc_host.clone(); - let brpc_port = state.brpc_port.clone(); - let brpc_user = state.brpc_user.clone(); - let brpc_pass = state.brpc_pass.clone(); - let db_dir = state.db_dir.clone(); - // match dw - // .scanblocks(brpc_host, brpc_port, brpc_user, brpc_pass) - // .await - // { - // Ok(_) => { - // log::info!("scan succeeded"); - // } - // Err(e) => { - // log::info!("scan failed: {}", e); - // } - // }; - // } - let dw_clone = dw.clone(); + let (db_dir, brpc_host, brpc_port, brpc_user, brpc_pass) = { + let state = plugin.state().lock().await; + ( + state.db_dir.clone(), + state.brpc_host.clone(), + state.brpc_port.clone(), + state.brpc_user.clone(), + state.brpc_pass.clone(), + ) + }; + let mut dw_clone = dw.clone(); let wallet = dw_clone .fetch_wallet(db_dir, brpc_host, brpc_port, brpc_user, brpc_pass) .await?; @@ -325,10 +317,11 @@ async fn add( log::debug!("no new txs this time"); } } - log::info!("waiting for wallet lock"); + dw.update_last_synced(dw_clone.last_synced.unwrap()); + log::trace!("waiting for wallet lock"); plugin.state().lock().await.add_descriptor_wallet(&dw)?; - log::info!("add_descriptor_wallet"); + log::trace!("add_descriptor_wallet"); let wallets_str = json!(plugin.state().lock().await.wallets).to_string(); let rpc_file = plugin.configuration().rpc_file; let p = Path::new(&rpc_file); @@ -349,7 +342,7 @@ async fn add( "Wallet with deterministic name {} successfully added", &dw.get_name()? ); - log::info!("returning"); + log::trace!("returning"); Ok(json!(message)) } @@ -421,17 +414,16 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res "Smaug state!!! {:?}", plugin.state().lock().await.wallets.clone() ); - let state = &plugin.state().lock().await; - let brpc_host = state.brpc_host.clone(); - let brpc_port = state.brpc_port.clone(); - let brpc_user = state.brpc_user.clone(); - let brpc_pass = state.brpc_pass.clone(); - let db_dir = state.db_dir.clone(); - // log::trace!("waiting for db_dir lock in block_handler"); - // let db_dir = { - // let state = plugin.state().lock().await; - // state.db_dir.clone() - // }; + let (db_dir, brpc_host, brpc_port, brpc_user, brpc_pass) = { + let state = plugin.state().lock().await; + ( + state.db_dir.clone(), + state.brpc_host.clone(), + state.brpc_port.clone(), + state.brpc_user.clone(), + state.brpc_pass.clone(), + ) + }; log::trace!("waiting for wallet lock in block_handler"); let state = &mut plugin.state().lock().await; @@ -442,7 +434,7 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res for (_dw_desc, dw) in descriptor_wallets.iter_mut() { log::trace!("fetching wallet in block_handler: {:?}", dw); - let dw_clone = dw.clone(); + let mut dw_clone = dw.clone(); let wallet = dw_clone .fetch_wallet( db_dir.clone(), @@ -478,6 +470,8 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res } else { log::debug!("found no transactions"); } + log::info!("scanned up to height {}", dw_clone.last_synced.unwrap()); + dw.update_last_synced(dw_clone.last_synced.unwrap()); } log::trace!("returning from block_added_handler"); Ok(()) diff --git a/src/wallet.rs b/src/wallet.rs index 039bfe8..975517f 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Chain}; use bdk::{ bitcoin::{ secp256k1::{All, Secp256k1}, - Network, Transaction, Txid, + BlockHash, Network, Transaction, Txid, }, chain::{ tx_graph::CanonicalTx, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, @@ -22,7 +22,7 @@ use clap::{command, Parser}; use cln_plugin::{Error, Plugin}; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{collections::BTreeMap, fmt, path::PathBuf, time::Duration}; +use std::{collections::BTreeMap, fmt, path::PathBuf, str::FromStr, time::Duration}; use crate::state::State; @@ -121,7 +121,7 @@ pub struct DescriptorWallet { pub change_descriptor: Option, pub birthday: Option, pub gap: Option, - // pub last_synced: Option, + pub last_synced: Option, // #[serde(skip_serializing, skip_deserializing)] pub transactions: BTreeMap, pub network: Option, @@ -158,6 +158,7 @@ impl DescriptorWallet { gap: args.gap, transactions: BTreeMap::new(), network: Some(network), + last_synced: None, }) } @@ -170,6 +171,7 @@ impl DescriptorWallet { // last_synced: None, transactions: BTreeMap::new(), network: None, + last_synced: None, }) } @@ -243,14 +245,18 @@ impl DescriptorWallet { // self.transactions = transactions; } + pub fn update_last_synced(&mut self, height: u32) { + self.last_synced = Some(height); + } + pub fn get_network(&self) -> Result { get_network(self.network.clone()) } pub fn get_name(&self) -> Result { - log::info!("get_name called"); + log::trace!("get_name called"); let network = get_network(self.network.clone()); - log::info!("get_network succeeded"); + log::trace!("get_network succeeded"); Ok(wallet_name_from_descriptor( &self.descriptor, self.change_descriptor.as_ref(), @@ -260,7 +266,7 @@ impl DescriptorWallet { } pub async fn fetch_wallet<'a>( - &self, + &mut self, db_dir: PathBuf, brpc_host: String, brpc_port: u16, @@ -376,11 +382,23 @@ impl DescriptorWallet { }); } - // let descriptors = &[external_descriptor, internal_descriptor]; + wallet.set_lookahead_for_all(20)?; + + // let chain_tip = wallet.latest_checkpoint(); + log::info!("last_synced = {:?}", self.last_synced); + let start_height: Option = match self.last_synced { + Some(ct) => Some(ct.into()), + None => None, + }; + // let mut emitter = match chain_tip { + // Some(cp) => Emitter::from_checkpoint(&rpc_client, cp), + // None => Emitter::from_height(&rpc_client, args[5].parse::()?), + // }; + let descriptors = &descriptors_vec[..]; let request = ScanBlocksRequest { scanobjects: descriptors, - start_height: None, + start_height, stop_height: None, filtertype: None, options: Some(ScanBlocksOptions { @@ -403,16 +421,8 @@ impl DescriptorWallet { // )?, // ], // }; - // println!("scanblocks result: {:?}", res); - // println!("wallet = {:?}", wallet); - - wallet.set_lookahead_for_all(20)?; - - // let chain_tip = wallet.latest_checkpoint(); - // let mut emitter = match chain_tip { - // Some(cp) => Emitter::from_checkpoint(&rpc_client, cp), - // None => Emitter::from_height(&rpc_client, args[5].parse::()?), - // }; + log::trace!("scanblocks result: {:?}", res); + log::trace!("wallet = {:?}", wallet); let mut prev_block_id = None; @@ -426,6 +436,8 @@ impl DescriptorWallet { prev_block_id = Some(BlockId { height, hash: bh }); } + self.update_last_synced(res.to_height.try_into().unwrap()); + // while let Some((height, block)) = emitter.next_block()? { // println!("Applying block {} at height {}", block.block_hash(), height); // wallet.apply_block_relevant(block, height)?; From 08723d7af8fe883f407b788c02ea6ce53d9fb471 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Tue, 5 Dec 2023 21:43:45 -0600 Subject: [PATCH 10/17] don't use local deps --- Cargo.lock | 5 +++++ Cargo.toml | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 263ce8f..48fb625 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,6 +116,7 @@ checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bdk" version = "1.0.0-alpha.2" +source = "git+https://github.com/chrisguida/bdk?rev=cc32e69a12b8c30e571e6261a8b99dec2cef6ff5#cc32e69a12b8c30e571e6261a8b99dec2cef6ff5" dependencies = [ "bdk_chain", "bitcoin 0.30.2", @@ -131,6 +132,7 @@ dependencies = [ [[package]] name = "bdk_chain" version = "0.6.0" +source = "git+https://github.com/chrisguida/bdk?rev=cc32e69a12b8c30e571e6261a8b99dec2cef6ff5#cc32e69a12b8c30e571e6261a8b99dec2cef6ff5" dependencies = [ "bitcoin 0.30.2", "miniscript", @@ -140,6 +142,7 @@ dependencies = [ [[package]] name = "bdk_file_store" version = "0.2.0" +source = "git+https://github.com/chrisguida/bdk?rev=cc32e69a12b8c30e571e6261a8b99dec2cef6ff5#cc32e69a12b8c30e571e6261a8b99dec2cef6ff5" dependencies = [ "bdk_chain", "bincode", @@ -216,6 +219,7 @@ dependencies = [ [[package]] name = "bitcoincore-rpc" version = "0.17.0" +source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23927f3e8b3bd413e0bf8b7c42b9531345bdd1a3" dependencies = [ "bitcoin-private", "bitcoincore-rpc-json", @@ -228,6 +232,7 @@ dependencies = [ [[package]] name = "bitcoincore-rpc-json" version = "0.17.0" +source = "git+https://github.com/chrisguida/rust-bitcoincore-rpc?branch=feat/scanblocks#23927f3e8b3bd413e0bf8b7c42b9531345bdd1a3" dependencies = [ "bitcoin 0.30.2", "bitcoin-private", diff --git a/Cargo.toml b/Cargo.toml index 5ec8c46..0afd5c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,20 @@ base64 = "0.21.2" # bdk = "0.27.1" # bdk = { version = "0.29", default-features=false, features = ["std", "key-value-db","async-interface", "use-esplora-async", "rpc"] } # bdk = { git = "https://github.com/bitcoindevkit/bdk", version = "1.0.0-alpha.1", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } +bdk = { git = "https://github.com/chrisguida/bdk", rev = "cc32e69a12b8c30e571e6261a8b99dec2cef6ff5" } # bdk = "1.0.0-alpha.2" # bdk = { path = "../../lib/bdk/crates/bdk" } -bdk = { path = "../bdk/crates/bdk" } +# bdk = { path = "../bdk/crates/bdk" } # bdk_esplora = { version = "0.3.0", features = ["async-https"] } # bdk_esplora = { path = "../../lib/bdk/crates/esplora", features = ["async-https"] } # bdk_esplora = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } # bdk_file_store = { version = "0.2.0" } # bdk_file_store = { path = "../../lib/bdk/crates/file_store" } -bdk_file_store = { path = "../bdk/crates/file_store" } +# bdk_file_store = { path = "../bdk/crates/file_store" } # bdk_file_store = { git = "https://github.com/bitcoindevkit/bdk", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } -bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } -# bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } +bdk_file_store = { git = "https://github.com/chrisguida/bdk", rev = "cc32e69a12b8c30e571e6261a8b99dec2cef6ff5" } +# bitcoincore-rpc = { path = "../rust-bitcoincore-rpc/client" } +bitcoincore-rpc = { git = "https://github.com/chrisguida/rust-bitcoincore-rpc", branch = "feat/scanblocks" } clap = { version = "4.4.0", features = ["derive"] } cln-plugin = { git = "https://github.com/elementsproject/lightning", version = "0.1.4" } # cln-plugin = { path = "../../lightning/plugins" } From 904f16b5a29d614e2592a462a6c28e1912800ae4 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Wed, 6 Dec 2023 10:41:00 -0600 Subject: [PATCH 11/17] return more helpful error when wallet in datastore is invalid --- Cargo.toml | 2 +- src/main.rs | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0afd5c0..8eca6d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ base64 = "0.21.2" # bdk = "0.27.1" # bdk = { version = "0.29", default-features=false, features = ["std", "key-value-db","async-interface", "use-esplora-async", "rpc"] } # bdk = { git = "https://github.com/bitcoindevkit/bdk", version = "1.0.0-alpha.1", rev = "8f38e96e4542db2378e2e64cd9289638ee86ba1a" } -bdk = { git = "https://github.com/chrisguida/bdk", rev = "cc32e69a12b8c30e571e6261a8b99dec2cef6ff5" } +bdk = { git = "https://github.com/chrisguida/bdk", version = "1.0.0-alpha.2", rev = "cc32e69a12b8c30e571e6261a8b99dec2cef6ff5" } # bdk = "1.0.0-alpha.2" # bdk = { path = "../../lib/bdk/crates/bdk" } # bdk = { path = "../bdk/crates/bdk" } diff --git a/src/main.rs b/src/main.rs index 7e4d09c..d5c090e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,11 +74,11 @@ async fn main() -> Result<(), anyhow::Error> { } else { return Ok(()); }; - log::debug!( + log::info!( "Configuration from CLN main daemon: {:?}", configured_plugin.configuration() ); - log::debug!( + log::info!( "smaug_network = {:?}, cln_network = {}", configured_plugin.option("smaug_network"), configured_plugin.configuration().network @@ -140,17 +140,20 @@ async fn main() -> Result<(), anyhow::Error> { log::error!("Cannot create data dir: {e:?}"); std::process::exit(1); }); - log::debug!("network = {}", network); + log::info!("network = {}", network); let rpc_file = configured_plugin.configuration().rpc_file; let p = Path::new(&rpc_file); let mut rpc = ClnRpc::new(p).await?; + log::info!("calling listdatastore"); + let lds_response = rpc .call(Request::ListDatastore(ListdatastoreRequest { key: Some(vec!["smaug".to_owned()]), })) .await .map_err(|e| anyhow!("Error calling listdatastore: {:?}", e))?; + log::info!("fetching wallets from listdatastore response"); let wallets: BTreeMap = match lds_response { Response::ListDatastore(r) => match r.datastore.is_empty() { true => BTreeMap::new(), @@ -158,6 +161,7 @@ async fn main() -> Result<(), anyhow::Error> { Some(deserialized) => match serde_json::from_str(&deserialized) { core::result::Result::Ok(dws) => dws, core::result::Result::Err(e) => { + log::error!("Error parsing wallet from datastore: {:?}", &r.datastore[0].string); log::error!("{}", e); return Err(e.into()); } @@ -165,8 +169,9 @@ async fn main() -> Result<(), anyhow::Error> { None => BTreeMap::new(), }, }, - _ => panic!(), + _ => panic!("Unrecognized type returned from listdatastore call, exiting"), }; + log::info!("creating plugin state"); let watch_descriptor = Smaug { wallets, network: network.clone(), @@ -177,7 +182,11 @@ async fn main() -> Result<(), anyhow::Error> { db_dir: ln_dir.join(SMAUG_DATADIR), }; let plugin_state = Arc::new(Mutex::new(watch_descriptor.clone())); + log::info!("getting lock on state"); + plugin_state.lock().await.network = network; + log::info!("starting Smaug"); + let plugin = configured_plugin.start(plugin_state).await?; log::info!("Smaug started"); plugin.join().await From b9ff607ebe9933685d08bb8bcfe8dd84a825a048 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Wed, 6 Dec 2023 12:02:34 -0600 Subject: [PATCH 12/17] even better error message --- src/main.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index d5c090e..ae1095f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -161,8 +161,11 @@ async fn main() -> Result<(), anyhow::Error> { Some(deserialized) => match serde_json::from_str(&deserialized) { core::result::Result::Ok(dws) => dws, core::result::Result::Err(e) => { - log::error!("Error parsing wallet from datastore: {:?}", &r.datastore[0].string); - log::error!("{}", e); + // sometimes log::error! doesn't execute before plugin is killed + eprintln!("Error parsing wallet from datastore: {:?}", &r.datastore[0].string); + eprintln!("{}", e); + eprintln!("This is probably due to an outdated wallet format."); + eprintln!("Please delete the wallet with `lightning-cli deldatastore smaug` and restart Smaug."); return Err(e.into()); } }, From 35a92f66053f1637504a898d6d0aba3b23463325 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Wed, 6 Dec 2023 14:55:29 -0600 Subject: [PATCH 13/17] fix chain initialization connect error --- src/wallet.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 975517f..b1a851d 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -384,7 +384,6 @@ impl DescriptorWallet { wallet.set_lookahead_for_all(20)?; - // let chain_tip = wallet.latest_checkpoint(); log::info!("last_synced = {:?}", self.last_synced); let start_height: Option = match self.last_synced { Some(ct) => Some(ct.into()), @@ -424,7 +423,11 @@ impl DescriptorWallet { log::trace!("scanblocks result: {:?}", res); log::trace!("wallet = {:?}", wallet); - let mut prev_block_id = None; + let chain_tip = wallet.latest_checkpoint(); + let mut prev_block_id = match chain_tip { + Some(ct) => Some(ct.block_id()), + None => None, + }; for bh in res.relevant_blocks { // self.get_relevant_txs(bh, &conn); @@ -438,6 +441,8 @@ impl DescriptorWallet { self.update_last_synced(res.to_height.try_into().unwrap()); + log::info!("last_synced after scan = {:?}", self.last_synced); + // while let Some((height, block)) = emitter.next_block()? { // println!("Applying block {} at height {}", block.block_hash(), height); // wallet.apply_block_relevant(block, height)?; From 9673bab2b4203694e9b6eb3f8b0e49c2ef9a3962 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Wed, 6 Dec 2023 15:50:23 -0600 Subject: [PATCH 14/17] cleanup --- src/main.rs | 26 ++---- src/wallet.rs | 236 +------------------------------------------------- 2 files changed, 11 insertions(+), 251 deletions(-) diff --git a/src/main.rs b/src/main.rs index ae1095f..a7fbc4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,6 @@ use bdk::bitcoin::Transaction; use smaug::state::{Smaug, State}; #[tokio::main] -// #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), anyhow::Error> { let builder = Builder::new(tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( @@ -161,8 +160,11 @@ async fn main() -> Result<(), anyhow::Error> { Some(deserialized) => match serde_json::from_str(&deserialized) { core::result::Result::Ok(dws) => dws, core::result::Result::Err(e) => { - // sometimes log::error! doesn't execute before plugin is killed - eprintln!("Error parsing wallet from datastore: {:?}", &r.datastore[0].string); + // sometimes log::error! doesn't execute before plugin is killed, so we use eprintln! here instead + eprintln!( + "Error parsing wallet from datastore: {:?}", + &r.datastore[0].string + ); eprintln!("{}", e); eprintln!("This is probably due to an outdated wallet format."); eprintln!("Please delete the wallet with `lightning-cli deldatastore smaug` and restart Smaug."); @@ -219,7 +221,6 @@ enum Commands { #[command(alias = "del", alias = "delete", alias = "remove")] Rm { /// Deterministic name (concatenated checksums) of wallet to delete - // #[arg(short, long)] descriptor_name: String, }, /// List descriptor wallets currently being watched @@ -286,16 +287,10 @@ async fn parse_command( } } -async fn add( - plugin: Plugin, - // v: serde_json::Value, - args: AddArgs, -) -> Result { +async fn add(plugin: Plugin, args: AddArgs) -> Result { let mut dw = DescriptorWallet::from_args(args, plugin.state().lock().await.network.clone()) .map_err(|e| anyhow!("error parsing args: {}", e))?; - // dw.network = ); log::trace!("params = {:?}", dw); - // { let (db_dir, brpc_host, brpc_port, brpc_user, brpc_pass) = { let state = plugin.state().lock().await; ( @@ -367,10 +362,7 @@ struct ListResponseItem { pub network: Option, } -async fn list( - plugin: Plugin, - // _v: serde_json::Value, -) -> Result { +async fn list(plugin: Plugin) -> Result { let state = &plugin.state().lock().await; let wallets = state.wallets.clone(); @@ -392,7 +384,6 @@ async fn list( async fn delete( plugin: Plugin, - // v: serde_json::Value, descriptor_name: String, ) -> Result { let wallets = &mut plugin.state().lock().await.wallets; @@ -457,7 +448,6 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res ) .await?; - // let wallet = dw.fetch_wallet(db_dir.clone()).await?; log::trace!("...fetched wallet in block_handler"); let bdk_transactions_iter = wallet.transactions(); let mut transactions = Vec::>::new(); @@ -483,6 +473,8 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res log::debug!("found no transactions"); } log::info!("scanned up to height {}", dw_clone.last_synced.unwrap()); + + // FIXME: this is horrible, please find a better way to do this dw.update_last_synced(dw_clone.last_synced.unwrap()); } log::trace!("returning from block_added_handler"); diff --git a/src/wallet.rs b/src/wallet.rs index b1a851d..184f50f 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -10,7 +10,6 @@ use bdk::{ wallet::wallet_name_from_descriptor, Wallet, }; -// use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_file_store::Store; use bitcoincore_rpc::{ bitcoincore_rpc_json::{ @@ -168,7 +167,6 @@ impl DescriptorWallet { change_descriptor: None, birthday: None, gap: None, - // last_synced: None, transactions: BTreeMap::new(), network: None, last_synced: None, @@ -225,10 +223,6 @@ impl DescriptorWallet { amount * 1000 } - // pub fn update_last_synced(&mut self, last_synced: BlockTime) { - // self.last_synced = Some(last_synced); - // } - pub fn update_transactions<'a>( &mut self, transactions: Vec>, @@ -242,7 +236,6 @@ impl DescriptorWallet { } } new_txs - // self.transactions = transactions; } pub fn update_last_synced(&mut self, height: u32) { @@ -272,28 +265,13 @@ impl DescriptorWallet { brpc_port: u16, brpc_user: String, brpc_pass: String, - // ) -> Result>>, Error> ) -> Result>, Error> { log::trace!("creating path"); let db_filename = self.get_name()?; - let db_path = db_dir - // .join(DATADIR) - .join(format!("{}.db", db_filename,)); + let db_path = db_dir.join(format!("{}.db", db_filename,)); log::trace!("searching for path: {:?}", db_path); - // let db = Store::::new_from_path(SMAUG_DATADIR.as_bytes(), db_path)?; let db = Store::::new_from_path(SMAUG_DATADIR.as_bytes(), db_path)?; log::trace!("db created!"); - // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; - // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" - let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; - let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; - let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; - let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - // let external_descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; - // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)"; - - // let external_descriptor = mutinynet_descriptor_ext; - // let internal_descriptor = mutinynet_descriptor_int; let external_descriptor = self.descriptor.clone(); let internal_descriptor = self.change_descriptor.clone(); let mut wallet = Wallet::new( @@ -309,54 +287,6 @@ impl DescriptorWallet { log::trace!("Syncing..."); log::debug!("using network: {}", json!(self.network).as_str().unwrap()); - // log::debug!( - // "using esplora url: {}", - // get_network_url(json!(self.network).as_str().unwrap()).as_str() - // ); - // let client = - // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; - // esplora_client::Builder::new( - // get_network_url( - // json!(self.network).as_str().unwrap() - // ).as_str() - // ).build_async()?; - // let client = - // // esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?; - // esplora_client::Builder::new( - // get_network_url( - // json!(self.network).as_str().unwrap() - // ).as_str() - // ).build_async()?; - - // let local_chain = wallet.checkpoints(); - // let keychain_spks = wallet - // .spks_of_all_keychains() - // .into_iter() - // .map(|(k, k_spks)| { - // let mut once = Some(()); - // let mut stdout = std::io::stdout(); - // let k_spks = k_spks - // .inspect(move |(spk_i, _)| match once.take() { - // Some(_) => log::debug!("\nScanning keychain [{:?}]", k), - // None => log::trace!(" {:<3}", spk_i), - // }) - // .inspect(move |_| stdout.flush().expect("must flush")); - // (k, k_spks) - // }) - // .collect(); - // log::trace!("Finished scanning"); - // let update = client - // .scan( - // local_chain, - // keychain_spks, - // [], - // [], - // STOP_GAP, - // PARALLEL_REQUESTS, - // ) - // .await?; - // wallet.apply_update(update)?; - // wallet.commit()?; let rpc_client = Client::new_with_timeout( &format!("http://{}:{}", brpc_host.clone(), brpc_port.clone()), @@ -364,11 +294,6 @@ impl DescriptorWallet { Duration::from_secs(3600), )?; - // println!( - // "Connected to Bitcoin Core RPC at {:?}", - // rpc_client.get_blockchain_info().unwrap() - // ); - let external_descriptor = ScanBlocksRequestDescriptor::Extended { desc: external_descriptor.to_string(), range: None, @@ -389,10 +314,6 @@ impl DescriptorWallet { Some(ct) => Some(ct.into()), None => None, }; - // let mut emitter = match chain_tip { - // Some(cp) => Emitter::from_checkpoint(&rpc_client, cp), - // None => Emitter::from_height(&rpc_client, args[5].parse::()?), - // }; let descriptors = &descriptors_vec[..]; let request = ScanBlocksRequest { @@ -405,21 +326,6 @@ impl DescriptorWallet { }), }; let res: ScanBlocksResult = rpc_client.scan_blocks_blocking(request)?; - // let res: ScanBlocksResult = ScanBlocksResult { - // from_height: 0, - // to_height: 819333, - // relevant_blocks: vec![ - // BlockHash::from_str( - // "000000000000000000047a0baacb20399819c82d6983a545d849625c040380e5", - // )?, - // BlockHash::from_str( - // "0000000000000000000114f60040b10b192bc37d3f1f5777686509898106105e", - // )?, - // BlockHash::from_str( - // "000000000000000000031359d3aff6ecfb95995bc9b84b079302836db45174ed", - // )?, - // ], - // }; log::trace!("scanblocks result: {:?}", res); log::trace!("wallet = {:?}", wallet); @@ -430,10 +336,8 @@ impl DescriptorWallet { }; for bh in res.relevant_blocks { - // self.get_relevant_txs(bh, &conn); let block = rpc_client.get_block(&bh)?; let height: u32 = block.bip34_block_height()?.try_into().unwrap(); - // println!("adding block height {} to wallet", height); wallet.apply_block_relevant(block.clone(), prev_block_id, height)?; wallet.commit()?; prev_block_id = Some(BlockId { height, hash: bh }); @@ -441,104 +345,13 @@ impl DescriptorWallet { self.update_last_synced(res.to_height.try_into().unwrap()); - log::info!("last_synced after scan = {:?}", self.last_synced); - - // while let Some((height, block)) = emitter.next_block()? { - // println!("Applying block {} at height {}", block.block_hash(), height); - // wallet.apply_block_relevant(block, height)?; - // wallet.commit()?; - // } - - // println!("About to apply unconfirmed transactions: ..."); - // let unconfirmed_txs = emitter.mempool()?; - // println!("Applying unconfirmed transactions: ..."); - // wallet.batch_insert_relevant_unconfirmed(unconfirmed_txs.iter().map(|(tx, time)| (tx, *time))); - // wallet.commit()?; - - // let balance = wallet.get_balance(); - // println!("Wallet balance after syncing: {} sats", balance.total()); + log::debug!("last_synced after scan = {:?}", self.last_synced); let balance = wallet.get_balance(); log::trace!("Wallet balance after syncing: {} sats", balance.total()); return Ok(wallet); } - // pub async fn scanblocks<'a>( - // &self, - // brpc_host: String, - // brpc_port: u16, - // brpc_user: String, - // brpc_pass: String, - // ) -> Result<(), Error> { - // // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)"; - // // mutinynet_descriptor = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/*)" - // let _mutinynet_descriptor_ext = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/0/*)"; - // let _mutinynet_descriptor_int = "wpkh(tprv8ZgxMBicQKsPdSAgthqLZ5ZWQkm5As4V3qNA5G8KKxGuqdaVVtBhytrUqRGPm4RxTktSdvch8JyUdfWR8g3ddrC49WfZnj4iGZN8y5L8NPZ/84'/0'/0'/1/*)"; - // let _mutinynet_descriptor_ext_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/0/*)"; - // let _mutinynet_descriptor_int_2 = "wpkh(tprv8ZgxMBicQKsPeRye8MhHA8hLxMuomycmGYXyRs7zViNck2VJsCJMTPt81Que8qp3PyPgQRnN7Gb1JyBVBKgj8AKEoEmmYxYDwzZJ63q1yjA/84'/0'/0'/1/*)"; - - // let rpc = Client::new_with_timeout( - // &format!("http://{}:{}", brpc_host, brpc_port), - // Auth::UserPass(brpc_user.clone(), brpc_pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) - // Duration::from_secs(3600), - // )?; - // let descriptor = ScanBlocksRequestDescriptor::Extended { - // desc: self.descriptor.clone().to_string(), - // range: None, - // }; - // let descriptors = &[descriptor]; - // let request = ScanBlocksRequest { - // scanobjects: descriptors, - // start_height: None, - // stop_height: None, - // filtertype: None, - // options: Some(ScanBlocksOptions { - // filter_false_positives: Some(true), - // }), - // }; - // let res = rpc.scan_blocks_blocking(request)?; - // log::info!("scanblocks result: {:?}", res); - - // let conn = RpcConnection { - // host: brpc_host, - // port: brpc_port, - // user: brpc_user, - // pass: brpc_pass, - // }; - - // for bh in res.relevant_blocks { - // self.get_relevant_txs(bh, &conn).await?; - // } - - // return Ok(()); - // } - - // async fn get_relevant_txs( - // &self, - // bh: BlockHash, - // conn: &RpcConnection, - // ) -> Result, Error> { - // let mut relevant_txs: Vec = vec![]; - // let rpc = Client::new( - // &format!("http://{}:{}", conn.host, conn.port), - // Auth::UserPass(conn.user.clone(), conn.pass.clone()), // Auth::CookieFile(PathBuf::from("/home/cguida/.bitcoin/regtest/.cookie")) - // // Duration::from_secs(3600), - // )?; - // let block = rpc.get_block(&bh)?; - // for tx in block.txdata { - // // let tx_bdk = tx.into(); - // let chain_update = - // CheckPoint::from_header(&block.header, height).into_update(false); - // let chain_changeset = chain - // .apply_update(chain_update) - // .expect("must always apply as we receive blocks in order from emitter"); - // let graph_changeset = graph.apply_block_relevant(block, height); - // (chain_changeset, graph_changeset) - // relevant_txs.push(tx); - // } - // Ok(relevant_txs) - // } - // assume we own all inputs, ie sent from our wallet. all inputs and outputs should generate coin movement bookkeeper events async fn spend_tx_notify<'a>( &self, @@ -546,20 +359,10 @@ impl DescriptorWallet { wallet: &Wallet>, tx: &CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>, ) -> Result<(), Error> { - // match tx { - // Some(t) => { // send spent notification for each input for input in tx.tx_node.tx.input.iter() { if let Some(po) = wallet.tx_graph().get_txout(input.previous_output) { match tx.chain_position { - // ChainPosition::Confirmed(a) => Self::Confirmed { - // height: a.confirmation_height, - // time: a.confirmation_time, - // }, - // ChainPosition::Unconfirmed(_) => Self::Unconfirmed { last_seen: 0 }, - // } - // match ConfirmationTime::from(&) { - // ConfirmationTime::Unconfirmed { .. } => { ChainPosition::Unconfirmed(_) => { continue; } @@ -637,11 +440,6 @@ impl DescriptorWallet { } } Ok(()) - // } - // None => { - // log::debug!("Transaction is missing a Transaction"); - // } - // } } // assume we own no inputs. sent to us from someone else's wallet. @@ -653,8 +451,6 @@ impl DescriptorWallet { wallet: &Wallet>, tx: &CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>, ) -> Result<(), Error> { - // match tx.transaction.clone() { - // Some(t) => { for (vout, output) in tx.tx_node.tx.output.iter().enumerate() { if wallet.is_mine(&output.script_pubkey) { match tx.chain_position { @@ -668,11 +464,6 @@ impl DescriptorWallet { acct = format!("smaug:{}", self.get_name()?); transfer_from = "external".to_owned(); } else { - // transfer_from = format!( - // "smaug:{}", - // self.get_name? - // ); - // acct = "external".to_owned(); continue; } let amount = output.value; @@ -708,11 +499,6 @@ impl DescriptorWallet { } } } - // } - // None => { - // log::debug!("Transaction is missing a Transaction"); - // } - // } Ok(()) } @@ -726,9 +512,6 @@ impl DescriptorWallet { wallet: &Wallet>, tx: &CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>, ) -> Result<(), Error> { - // match tx.transaction.clone() { - // Some(t) => { - // send spent notification for each input that spends one of our outputs for input in tx.tx_node.input.iter() { if let Some(po) = wallet.tx_graph().get_txout(input.previous_output) { match tx.chain_position { @@ -816,11 +599,6 @@ impl DescriptorWallet { } } } - // } - // None => { - // log::debug!("Transaction is missing a Transaction"); - // } - // } Ok(()) } @@ -876,14 +654,6 @@ impl DescriptorWallet { log::debug!("sending shared notif"); self.shared_tx_notify(plugin, wallet, &tx).await?; } - - // if tx.sent > 0 { - - // } - - // if tx.received > 0 { - - // } Ok(()) } } @@ -899,10 +669,8 @@ impl TryFrom for DescriptorWallet { let param_count = a.len(); match param_count { - // 1 => DescriptorWallet::try_from(a.pop().unwrap()), 1..=4 => { let descriptor = a.get(0).unwrap().as_str().ok_or_else(|| WatchError::InvalidDescriptor("descriptor must be a string".to_string()))?; - // let change_descriptor = Some(a.get(1).unwrap().as_str().ok_or_else(|| WatchError::InvalidChangeDescriptor("change_descriptor must be a string".to_string()))?); log::trace!("try_from array: change_descriptor = {:?}", a.get(1)); let change_descriptor = if let Some(cd) = a.get(1) { Some(cd.as_str().ok_or_else(|| WatchError::InvalidChangeDescriptor(format!("change_descriptor must be a string. Received: {cd}")))?) From 9bf8c147e874bd543e1050477c5bf0617591b0da Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Wed, 6 Dec 2023 15:55:08 -0600 Subject: [PATCH 15/17] fix brpc_user error msg and cleanup logging --- src/main.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index a7fbc4d..4e0a9b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,11 +73,11 @@ async fn main() -> Result<(), anyhow::Error> { } else { return Ok(()); }; - log::info!( + log::debug!( "Configuration from CLN main daemon: {:?}", configured_plugin.configuration() ); - log::info!( + log::debug!( "smaug_network = {:?}, cln_network = {}", configured_plugin.option("smaug_network"), configured_plugin.configuration().network @@ -131,7 +131,7 @@ async fn main() -> Result<(), anyhow::Error> { )) } }, - None => return Err(anyhow!("must specify smaug_brpc_user")), + None => return Err(anyhow!("must specify smaug_brpc_pass")), }; let ln_dir: PathBuf = configured_plugin.configuration().lightning_dir.into(); // Create data dir if it does not exist @@ -139,12 +139,12 @@ async fn main() -> Result<(), anyhow::Error> { log::error!("Cannot create data dir: {e:?}"); std::process::exit(1); }); - log::info!("network = {}", network); + log::trace!("network = {}", network); let rpc_file = configured_plugin.configuration().rpc_file; let p = Path::new(&rpc_file); let mut rpc = ClnRpc::new(p).await?; - log::info!("calling listdatastore"); + log::trace!("calling listdatastore"); let lds_response = rpc .call(Request::ListDatastore(ListdatastoreRequest { @@ -152,7 +152,7 @@ async fn main() -> Result<(), anyhow::Error> { })) .await .map_err(|e| anyhow!("Error calling listdatastore: {:?}", e))?; - log::info!("fetching wallets from listdatastore response"); + log::trace!("fetching wallets from listdatastore response"); let wallets: BTreeMap = match lds_response { Response::ListDatastore(r) => match r.datastore.is_empty() { true => BTreeMap::new(), @@ -176,7 +176,7 @@ async fn main() -> Result<(), anyhow::Error> { }, _ => panic!("Unrecognized type returned from listdatastore call, exiting"), }; - log::info!("creating plugin state"); + log::trace!("creating plugin state"); let watch_descriptor = Smaug { wallets, network: network.clone(), @@ -187,10 +187,10 @@ async fn main() -> Result<(), anyhow::Error> { db_dir: ln_dir.join(SMAUG_DATADIR), }; let plugin_state = Arc::new(Mutex::new(watch_descriptor.clone())); - log::info!("getting lock on state"); + log::trace!("getting lock on state"); plugin_state.lock().await.network = network; - log::info!("starting Smaug"); + log::trace!("starting Smaug"); let plugin = configured_plugin.start(plugin_state).await?; log::info!("Smaug started"); @@ -472,7 +472,7 @@ async fn block_added_handler(plugin: Plugin, v: serde_json::Value) -> Res } else { log::debug!("found no transactions"); } - log::info!("scanned up to height {}", dw_clone.last_synced.unwrap()); + log::debug!("scanned up to height {}", dw_clone.last_synced.unwrap()); // FIXME: this is horrible, please find a better way to do this dw.update_last_synced(dw_clone.last_synced.unwrap()); From 60844e0d3e815851e58de7a9216355b850c03357 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Wed, 6 Dec 2023 16:03:01 -0600 Subject: [PATCH 16/17] more cleanup --- src/main.rs | 2 ++ src/state.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 4e0a9b8..8bed6aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -295,6 +295,7 @@ async fn add(plugin: Plugin, args: AddArgs) -> Result, args: AddArgs) -> Result Result<(), anyhow::Error> { - log::info!("add_descriptor_wallet called"); + log::trace!("add_descriptor_wallet called"); self.wallets.insert(wallet.get_name()?, wallet.clone()); Ok(()) } From 81e7e648f90e34292a972e7dec4044cff7133269 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Wed, 6 Dec 2023 16:09:31 -0600 Subject: [PATCH 17/17] more cleanup --- src/wallet.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 184f50f..8e8fab7 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,12 +1,10 @@ -use anyhow::{anyhow, Chain}; +use anyhow::anyhow; use bdk::{ bitcoin::{ secp256k1::{All, Secp256k1}, - BlockHash, Network, Transaction, Txid, - }, - chain::{ - tx_graph::CanonicalTx, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, + Network, Transaction, Txid, }, + chain::{tx_graph::CanonicalTx, BlockId, ChainPosition, ConfirmationTimeAnchor}, wallet::wallet_name_from_descriptor, Wallet, }; @@ -21,13 +19,11 @@ use clap::{command, Parser}; use cln_plugin::{Error, Plugin}; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{collections::BTreeMap, fmt, path::PathBuf, str::FromStr, time::Duration}; +use std::{collections::BTreeMap, fmt, path::PathBuf, time::Duration}; use crate::state::State; pub const SMAUG_DATADIR: &str = ".smaug"; -const STOP_GAP: usize = 50; -const PARALLEL_REQUESTS: usize = 5; pub const UTXO_DEPOSIT_TAG: &str = "utxo_deposit"; pub const UTXO_SPENT_TAG: &str = "utxo_spent";