From a244207f77b0005e51e544a3e90b21433297fbd9 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 15:38:42 +0100 Subject: [PATCH 01/55] ibid --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cc91624..0dc1f7c 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,11 @@ Next dev tasks: ### Side Watcher -The Side Watcher is a simple relayer node that sits between the Side Chain (Cosmos) and the decentralized Side Nodes. At the moment, it simply relays transactions between nodes via a websocket. +The Side Watcher is a simple relayer node that sits between the Side Chain (Cosmos) and the decentralized Side Nodes. At the moment, it simply relays transactions between nodes via a websocket. We aim to eliminate this component from the architecture, but for the moment it simplifies networking and consensus agreement while we experiment with higher-value concepts. -Next, the Side Watcher needs to: +To fulfill the promises in the Lite Paper, the Side Watcher needs to: -[ ] make a block for the P2P when the Side Chain creates a block (see litepaper) +[ ] make a block for the P2P when the Side Chain creates a block [ ] submit P2P chain data to the Side Chain Later, we will aim to remove the Side Watcher from the architecture, by (a) moving to pure P2P transactions between Side Nodes, and (b) doing leader election of a Side Node to reach agreement on the submitted block. From 4cf65139594de3ef699477dc07725adbdc9d491c Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 16:00:02 +0100 Subject: [PATCH 02/55] Getting ready to create Bitcoin keys --- Cargo.lock | 113 +++++++++++++++++++++++++++++++++++-- side-node/Cargo.toml | 1 + side-node/src/init/mod.rs | 22 +++++++- side-node/src/stdin.rs | 1 - side-node/src/utils.rs | 4 +- side-node/src/websocket.rs | 4 +- 6 files changed, 133 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9967c7c..3968272 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,6 +225,12 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-channel" version = "1.9.0" @@ -302,6 +308,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals", + "bitcoin_hashes 0.14.0", +] + [[package]] name = "base64" version = "0.13.1" @@ -332,6 +348,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bft-crdt-derive" version = "0.1.0" @@ -370,12 +392,50 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" +dependencies = [ + "base58ck", + "bech32 0.11.0", + "bitcoin-internals", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative", + "hex_lit", + "secp256k1 0.29.0", +] + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" + +[[package]] +name = "bitcoin-io" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" + [[package]] name = "bitcoin-private" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +[[package]] +name = "bitcoin-units" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb54da0b28892f3c52203a7191534033e051b6f4b52bc15480681b57b7e036f5" +dependencies = [ + "bitcoin-internals", +] + [[package]] name = "bitcoin_hashes" version = "0.12.0" @@ -385,6 +445,16 @@ dependencies = [ "bitcoin-private", ] +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1016,7 +1086,7 @@ dependencies = [ "ark-serialize", "auto_ops", "base64ct", - "bech32", + "bech32 0.9.1", "bincode", "blake2", "blst", @@ -1042,7 +1112,7 @@ dependencies = [ "rfc6979", "rsa", "schemars", - "secp256k1", + "secp256k1 0.27.0", "serde", "serde_json", "serde_with 2.3.3", @@ -1299,12 +1369,27 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + [[package]] name = "hkdf" version = "0.12.4" @@ -2114,9 +2199,19 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.12.0", "rand 0.8.5", - "secp256k1-sys", + "secp256k1-sys 0.8.1", +] + +[[package]] +name = "secp256k1" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +dependencies = [ + "bitcoin_hashes 0.12.0", + "secp256k1-sys 0.10.0", ] [[package]] @@ -2128,6 +2223,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" +dependencies = [ + "cc", +] + [[package]] name = "semver" version = "1.0.23" @@ -2318,6 +2422,7 @@ dependencies = [ "async-trait", "bft-crdt-derive", "bft-json-crdt", + "bitcoin", "clap 4.5.4", "dirs", "ezsockets", diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index d202bb5..2c443a9 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" async-trait = "0.1.52" bft-json-crdt = { path = "../crates/bft-json-crdt" } bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" } +bitcoin = "0.32.2" clap = { version = "4.5.4", features = ["derive"] } dirs = "5.0.1" ezsockets = { version = "*", features = ["client"] } diff --git a/side-node/src/init/mod.rs b/side-node/src/init/mod.rs index cca68ee..453c720 100644 --- a/side-node/src/init/mod.rs +++ b/side-node/src/init/mod.rs @@ -53,6 +53,24 @@ mod tests { (SideNodeConfig { name: name.clone() }, name) } + #[test] + fn creates_bitcoin_keys() { + let (config, name) = side_node_config(); + let side_dir = format!("/tmp/side/{name}"); + + let mut key_file_path = PathBuf::new(); + key_file_path.push(side_dir.clone()); + key_file_path.push(utils::BFT_CRDT_KEY_FILE); + + let _ = init(PathBuf::from_str(&side_dir).unwrap(), config); + assert!(key_file_path.exists()); + + // check that the pem is readable + let data = fs::read_to_string(key_file_path).expect("couldn't read key file"); + let keys = Ed25519KeyPair::decode_base64(&data).expect("couldn't load keypair from file"); + assert_eq!(keys.public().as_bytes().len(), 32); + } + #[test] fn creates_side_node_directory() { let (config, name) = side_node_config(); @@ -67,13 +85,13 @@ mod tests { } #[test] - fn creates_key_file() { + fn creates_bft_crdt_key_file() { let (config, name) = side_node_config(); let side_dir = format!("/tmp/side/{name}"); let mut key_file_path = PathBuf::new(); key_file_path.push(side_dir.clone()); - key_file_path.push(utils::KEY_FILE); + key_file_path.push(utils::BFT_CRDT_KEY_FILE); let _ = init(PathBuf::from_str(&side_dir).unwrap(), config); assert!(key_file_path.exists()); diff --git a/side-node/src/stdin.rs b/side-node/src/stdin.rs index 9d74705..b71083f 100644 --- a/side-node/src/stdin.rs +++ b/side-node/src/stdin.rs @@ -5,7 +5,6 @@ pub(crate) fn input(stdin_sender: std::sync::mpsc::Sender) { let stdin = std::io::stdin(); let lines = stdin.lock().lines(); for line in lines { - println!("We're in stdin_input"); let line = line.unwrap(); stdin_sender.send(line).unwrap(); } diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index 17fb4cb..f70d865 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -3,13 +3,13 @@ use std::path::PathBuf; use bft_json_crdt::json_crdt::SignedOp; use serde_json::{json, Value}; -pub(crate) const KEY_FILE: &str = "keys.pem"; +pub(crate) const BFT_CRDT_KEY_FILE: &str = "keys.pem"; pub(crate) const CONFIG_FILE: &str = "config.toml"; /// Returns the path to the key file and config for this host OS. pub(crate) fn side_paths(prefix: PathBuf) -> (PathBuf, PathBuf) { let mut key_path = prefix.clone(); - key_path.push(KEY_FILE); + key_path.push(BFT_CRDT_KEY_FILE); let mut config_path = prefix.clone(); config_path.push(CONFIG_FILE); diff --git a/side-node/src/websocket.rs b/side-node/src/websocket.rs index de6ee5d..b3a1e05 100644 --- a/side-node/src/websocket.rs +++ b/side-node/src/websocket.rs @@ -47,9 +47,7 @@ impl ezsockets::ClientExt for WebSocketClient { let object_sha = utils::shappy(incoming.clone()); println!("deserialized: {}", object_sha); if string_sha != object_sha { - println!("SHA mismatch: {string_sha} != {object_sha}"); - println!("text: {text}"); - println!("incoming: {incoming:?}"); + panic!("sha mismatch: {string_sha} != {object_sha}, bft-crdt has failed"); } self.incoming_sender.send(incoming).await?; Ok(()) From f5da5af0b9d551e86b18920024f447c1b785e5b0 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 16:32:32 +0100 Subject: [PATCH 03/55] Bitcoin keys now being produced per-node --- Cargo.lock | 1 + side-node/Cargo.toml | 2 +- side-node/src/bitcoin_keys.rs | 35 +++++++++++++++++++++++++++++++++++ side-node/src/init/mod.rs | 27 +++++++++++++++------------ side-node/src/keys.rs | 4 ++-- side-node/src/lib.rs | 1 + side-node/src/node.rs | 6 +++--- side-node/src/utils.rs | 12 ++++++++---- 8 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 side-node/src/bitcoin_keys.rs diff --git a/Cargo.lock b/Cargo.lock index 3968272..5fd95be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2211,6 +2211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" dependencies = [ "bitcoin_hashes 0.12.0", + "rand 0.8.5", "secp256k1-sys 0.10.0", ] diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index 2c443a9..dfee8df 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" async-trait = "0.1.52" bft-json-crdt = { path = "../crates/bft-json-crdt" } bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" } -bitcoin = "0.32.2" +bitcoin = { version = "0.32.2", features = ["rand"] } clap = { version = "4.5.4", features = ["derive"] } dirs = "5.0.1" ezsockets = { version = "*", features = ["client"] } diff --git a/side-node/src/bitcoin_keys.rs b/side-node/src/bitcoin_keys.rs new file mode 100644 index 0000000..f4e6bcb --- /dev/null +++ b/side-node/src/bitcoin_keys.rs @@ -0,0 +1,35 @@ +use bitcoin::secp256k1::{rand, Keypair, Secp256k1}; +use std::{ + fs::{self, File}, + io::Write, + path::PathBuf, +}; + +pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { + let secp = Secp256k1::new(); + let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng()); + + let mut file = File::create(key_path)?; + + let pub_str = public_key.to_string(); + let priv_str = secret_key.display_secret().to_string(); + let out = format!("{pub_str}/{priv_str}"); + println!("out: {out}"); + file.write(out.as_bytes())?; + + Ok(()) +} + +pub(crate) fn load_from_file(side_dir: PathBuf) -> bitcoin::secp256k1::Keypair { + let key_path = crate::utils::side_paths(side_dir.clone()).0; + + let data = fs::read_to_string(key_path).expect("couldn't read bitcoin key file"); + println!("data: {:?}", data); + + let secret_key_data = data.split("/").collect::>()[1]; + + let secp = Secp256k1::new(); + let secret_key = bitcoin::secp256k1::SecretKey::from_slice(secret_key_data.as_bytes()).unwrap(); + + Keypair::from_secret_key(&secp, &secret_key) +} diff --git a/side-node/src/init/mod.rs b/side-node/src/init/mod.rs index 453c720..bdbd3b5 100644 --- a/side-node/src/init/mod.rs +++ b/side-node/src/init/mod.rs @@ -2,16 +2,19 @@ use std::path::PathBuf; use config::SideNodeConfig; -use crate::{keys, utils}; +use crate::{bitcoin_keys, keys, utils}; pub(crate) mod config; pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io::Error> { ensure_side_directory_exists(&home)?; - let (key_path, config_path) = utils::side_paths(home.clone()); + let (bft_crdt_key_path, bitcoin_key_path, config_path) = utils::side_paths(home.clone()); - println!("Writing key to: {:?}", key_path); - keys::write(key_path)?; + println!("Writing bft crdt key to: {:?}", bft_crdt_key_path); + keys::write(&bft_crdt_key_path)?; + + println!("Writing bitcoin key to: {:?}", bitcoin_key_path); + bitcoin_keys::write(&bitcoin_key_path)?; println!("Writing config to: {:?}", config_path); config::write_toml(&config, &config_path).expect("unable to write config file"); @@ -20,7 +23,7 @@ pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io: } /// Ensures that the directory at side_dir exists, so we have a place -/// to store our key file and config file. +/// to store our key files and config file. fn ensure_side_directory_exists(side_dir: &PathBuf) -> Result<(), std::io::Error> { if side_dir.exists() { return Ok(()); @@ -58,17 +61,17 @@ mod tests { let (config, name) = side_node_config(); let side_dir = format!("/tmp/side/{name}"); - let mut key_file_path = PathBuf::new(); - key_file_path.push(side_dir.clone()); - key_file_path.push(utils::BFT_CRDT_KEY_FILE); + let mut bitcoin_keys_path = PathBuf::new(); + bitcoin_keys_path.push(side_dir.clone()); + bitcoin_keys_path.push(utils::BITCOIN_KEY_FILE); let _ = init(PathBuf::from_str(&side_dir).unwrap(), config); - assert!(key_file_path.exists()); + assert!(bitcoin_keys_path.exists()); // check that the pem is readable - let data = fs::read_to_string(key_file_path).expect("couldn't read key file"); - let keys = Ed25519KeyPair::decode_base64(&data).expect("couldn't load keypair from file"); - assert_eq!(keys.public().as_bytes().len(), 32); + // let data = fs::read_to_string(bitcoin_keys_path).expect("couldn't read key file"); + // let keys = Ed25519KeyPair::decode_base64(&data).expect("couldn't load keypair from file"); + // assert_eq!(keys.public().as_bytes().len(), 32); } #[test] diff --git a/side-node/src/keys.rs b/side-node/src/keys.rs index 4f0ef34..00ec03e 100644 --- a/side-node/src/keys.rs +++ b/side-node/src/keys.rs @@ -8,7 +8,7 @@ use bft_json_crdt::keypair::{make_keypair, Ed25519KeyPair}; use fastcrypto::traits::EncodeDecodeBase64; /// Writes a new Ed25519 keypair to the file at key_path. -pub(crate) fn write(key_path: PathBuf) -> Result<(), std::io::Error> { +pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { let keys = make_keypair(); let mut file = File::create(key_path)?; @@ -20,7 +20,7 @@ pub(crate) fn write(key_path: PathBuf) -> Result<(), std::io::Error> { pub(crate) fn load_from_file(side_dir: PathBuf) -> Ed25519KeyPair { let key_path = crate::utils::side_paths(side_dir.clone()).0; - let data = fs::read_to_string(key_path).expect("couldn't read key file"); + let data = fs::read_to_string(key_path).expect("couldn't read bft-crdt key file"); println!("data: {:?}", data); Ed25519KeyPair::decode_base64(&data).expect("couldn't load keypair from file") diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index cb199a4..5dbd829 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -5,6 +5,7 @@ use node::SideNode; use tokio::{sync::mpsc, task}; use websocket::WebSocketClient; +pub(crate) mod bitcoin_keys; pub(crate) mod cli; pub mod crdt; pub(crate) mod init; diff --git a/side-node/src/node.rs b/side-node/src/node.rs index 35eee2d..aa8a970 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -6,7 +6,7 @@ use crate::{crdt::TransactionList, utils, websocket::WebSocketClient}; pub struct SideNode { crdt: BaseCrdt, - keys: fastcrypto::ed25519::Ed25519KeyPair, + bft_crdt_keys: fastcrypto::ed25519::Ed25519KeyPair, incoming_receiver: mpsc::Receiver, stdin_receiver: std::sync::mpsc::Receiver, handle: ezsockets::Client, @@ -22,7 +22,7 @@ impl SideNode { ) -> Self { let node = Self { crdt, - keys, + bft_crdt_keys: keys, incoming_receiver, stdin_receiver, handle, @@ -80,7 +80,7 @@ impl SideNode { .doc .list .insert(last.id, transaction) - .sign(&self.keys); + .sign(&self.bft_crdt_keys); // self.trace_crdt(); signed_op } diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index f70d865..aa55a83 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -3,18 +3,22 @@ use std::path::PathBuf; use bft_json_crdt::json_crdt::SignedOp; use serde_json::{json, Value}; +pub(crate) const BITCOIN_KEY_FILE: &str = "bitcoin_keys.pem"; pub(crate) const BFT_CRDT_KEY_FILE: &str = "keys.pem"; pub(crate) const CONFIG_FILE: &str = "config.toml"; /// Returns the path to the key file and config for this host OS. -pub(crate) fn side_paths(prefix: PathBuf) -> (PathBuf, PathBuf) { - let mut key_path = prefix.clone(); - key_path.push(BFT_CRDT_KEY_FILE); +pub(crate) fn side_paths(prefix: PathBuf) -> (PathBuf, PathBuf, PathBuf) { + let mut bft_crdt_key_path = prefix.clone(); + bft_crdt_key_path.push(BFT_CRDT_KEY_FILE); + + let mut bitcoin_key_path = prefix.clone(); + bitcoin_key_path.push(BITCOIN_KEY_FILE); let mut config_path = prefix.clone(); config_path.push(CONFIG_FILE); - (key_path, config_path) + (bft_crdt_key_path, bitcoin_key_path, config_path) } pub(crate) fn home(name: &String) -> std::path::PathBuf { From ae8a70e2493febd6f9ad3985e84770deefb97240 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 16:34:03 +0100 Subject: [PATCH 04/55] Renaming keys to bft_crdt_keys --- side-node/src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 5dbd829..349d71a 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -39,8 +39,8 @@ pub async fn run() { async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); - let keys = keys::load_from_file(side_dir); - let crdt = BaseCrdt::::new(&keys); + let bft_crdt_keys = keys::load_from_file(side_dir); + let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input let (incoming_sender, incoming_receiver) = mpsc::channel::(32); @@ -51,7 +51,13 @@ async fn setup(name: &String) -> SideNode { // Finally, create the node and return it let handle = WebSocketClient::new(incoming_sender).await; - let node = SideNode::new(crdt, keys, incoming_receiver, stdin_receiver, handle); + let node = SideNode::new( + crdt, + bft_crdt_keys, + incoming_receiver, + stdin_receiver, + handle, + ); println!("Node setup complete."); node } From ecec883f9b79145d62551d93dca65a232be0b81a Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 16:35:56 +0100 Subject: [PATCH 05/55] Renamed keys module --- side-node/src/{keys.rs => bft_crdt_keys.rs} | 0 side-node/src/init/mod.rs | 4 ++-- side-node/src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename side-node/src/{keys.rs => bft_crdt_keys.rs} (100%) diff --git a/side-node/src/keys.rs b/side-node/src/bft_crdt_keys.rs similarity index 100% rename from side-node/src/keys.rs rename to side-node/src/bft_crdt_keys.rs diff --git a/side-node/src/init/mod.rs b/side-node/src/init/mod.rs index bdbd3b5..ab0457a 100644 --- a/side-node/src/init/mod.rs +++ b/side-node/src/init/mod.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use config::SideNodeConfig; -use crate::{bitcoin_keys, keys, utils}; +use crate::{bft_crdt_keys, bitcoin_keys, utils}; pub(crate) mod config; @@ -11,7 +11,7 @@ pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io: let (bft_crdt_key_path, bitcoin_key_path, config_path) = utils::side_paths(home.clone()); println!("Writing bft crdt key to: {:?}", bft_crdt_key_path); - keys::write(&bft_crdt_key_path)?; + bft_crdt_keys::write(&bft_crdt_key_path)?; println!("Writing bitcoin key to: {:?}", bitcoin_key_path); bitcoin_keys::write(&bitcoin_key_path)?; diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 349d71a..60e4335 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -5,11 +5,11 @@ use node::SideNode; use tokio::{sync::mpsc, task}; use websocket::WebSocketClient; +pub mod bft_crdt_keys; pub(crate) mod bitcoin_keys; pub(crate) mod cli; pub mod crdt; pub(crate) mod init; -pub mod keys; pub mod node; pub(crate) mod stdin; pub mod utils; @@ -39,7 +39,7 @@ pub async fn run() { async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); - let bft_crdt_keys = keys::load_from_file(side_dir); + let bft_crdt_keys = bft_crdt_keys::load_from_file(side_dir); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input From 706a6719023ce93c899bf03e97fd3b0653456418 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 16:56:24 +0100 Subject: [PATCH 06/55] wip adding bitcoin keys to side nodes --- side-node/src/bft_crdt_keys.rs | 2 +- side-node/src/bitcoin_keys.rs | 17 +++++++++-------- side-node/src/lib.rs | 4 +++- side-node/src/node.rs | 7 +++++-- side-node/tests/side_node.rs | 1 + 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/side-node/src/bft_crdt_keys.rs b/side-node/src/bft_crdt_keys.rs index 00ec03e..4057f0f 100644 --- a/side-node/src/bft_crdt_keys.rs +++ b/side-node/src/bft_crdt_keys.rs @@ -17,7 +17,7 @@ pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { Ok(()) } -pub(crate) fn load_from_file(side_dir: PathBuf) -> Ed25519KeyPair { +pub(crate) fn load_from_file(side_dir: &PathBuf) -> Ed25519KeyPair { let key_path = crate::utils::side_paths(side_dir.clone()).0; let data = fs::read_to_string(key_path).expect("couldn't read bft-crdt key file"); diff --git a/side-node/src/bitcoin_keys.rs b/side-node/src/bitcoin_keys.rs index f4e6bcb..e17bdf1 100644 --- a/side-node/src/bitcoin_keys.rs +++ b/side-node/src/bitcoin_keys.rs @@ -1,8 +1,9 @@ -use bitcoin::secp256k1::{rand, Keypair, Secp256k1}; +use bitcoin::secp256k1::{rand, Keypair, Secp256k1, SecretKey}; use std::{ fs::{self, File}, io::Write, path::PathBuf, + str::FromStr, }; pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { @@ -13,6 +14,7 @@ pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { let pub_str = public_key.to_string(); let priv_str = secret_key.display_secret().to_string(); + println!("private key: {priv_str}"); let out = format!("{pub_str}/{priv_str}"); println!("out: {out}"); file.write(out.as_bytes())?; @@ -20,16 +22,15 @@ pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { Ok(()) } -pub(crate) fn load_from_file(side_dir: PathBuf) -> bitcoin::secp256k1::Keypair { - let key_path = crate::utils::side_paths(side_dir.clone()).0; +pub(crate) fn load_from_file(side_dir: &PathBuf) -> bitcoin::secp256k1::Keypair { + let bitcoin_key_path = crate::utils::side_paths(side_dir.clone()).1; // TODO: this tuple stinks - let data = fs::read_to_string(key_path).expect("couldn't read bitcoin key file"); - println!("data: {:?}", data); + let data = fs::read_to_string(bitcoin_key_path).expect("couldn't read bitcoin key file"); + println!("bitcoin keys: {:?}", data); let secret_key_data = data.split("/").collect::>()[1]; - let secp = Secp256k1::new(); - let secret_key = bitcoin::secp256k1::SecretKey::from_slice(secret_key_data.as_bytes()).unwrap(); - + let secret_key = + SecretKey::from_str(secret_key_data).expect("couldn't load secret key from file"); Keypair::from_secret_key(&secp, &secret_key) } diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 60e4335..8944442 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -39,7 +39,8 @@ pub async fn run() { async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); - let bft_crdt_keys = bft_crdt_keys::load_from_file(side_dir); + let bft_crdt_keys = bft_crdt_keys::load_from_file(&side_dir); + let bitcoin_keys = bitcoin_keys::load_from_file(&side_dir); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input @@ -54,6 +55,7 @@ async fn setup(name: &String) -> SideNode { let node = SideNode::new( crdt, bft_crdt_keys, + bitcoin_keys, incoming_receiver, stdin_receiver, handle, diff --git a/side-node/src/node.rs b/side-node/src/node.rs index aa8a970..8d1aac6 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -7,6 +7,7 @@ use crate::{crdt::TransactionList, utils, websocket::WebSocketClient}; pub struct SideNode { crdt: BaseCrdt, bft_crdt_keys: fastcrypto::ed25519::Ed25519KeyPair, + bitcoin_keys: bitcoin::secp256k1::Keypair, incoming_receiver: mpsc::Receiver, stdin_receiver: std::sync::mpsc::Receiver, handle: ezsockets::Client, @@ -15,14 +16,16 @@ pub struct SideNode { impl SideNode { pub fn new( crdt: BaseCrdt, - keys: Ed25519KeyPair, + bft_crdt_keys: Ed25519KeyPair, + bitcoin_keys: bitcoin::secp256k1::Keypair, incoming_receiver: mpsc::Receiver, stdin_receiver: std::sync::mpsc::Receiver, handle: ezsockets::Client, ) -> Self { let node = Self { crdt, - bft_crdt_keys: keys, + bft_crdt_keys, + bitcoin_keys, incoming_receiver, stdin_receiver, handle, diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index a4ad31f..be38a1b 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -35,6 +35,7 @@ async fn test_distribute_via_websockets() { async fn setup(_: &str) -> SideNode { // First, load up the keys and create a bft-crdt let keys = make_keypair(); + let bitcoin_keys = bitcoin::secp256k1::Keypair::new(); let crdt = BaseCrdt::::new(&keys); // Channels for internal communication, and a tokio task for stdin input From 8e7d24ec7bc854a773b870db986cef0341df87b2 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 17:03:31 +0100 Subject: [PATCH 07/55] Bitcoin keys now load into SideNode --- side-node/src/bitcoin_keys.rs | 14 +++++++++----- side-node/src/lib.rs | 2 +- side-node/tests/side_node.rs | 19 ++++++++++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/side-node/src/bitcoin_keys.rs b/side-node/src/bitcoin_keys.rs index e17bdf1..53baedd 100644 --- a/side-node/src/bitcoin_keys.rs +++ b/side-node/src/bitcoin_keys.rs @@ -1,4 +1,4 @@ -use bitcoin::secp256k1::{rand, Keypair, Secp256k1, SecretKey}; +use bitcoin::secp256k1::{rand, Keypair, PublicKey, Secp256k1, SecretKey}; use std::{ fs::{self, File}, io::Write, @@ -6,14 +6,18 @@ use std::{ str::FromStr, }; -pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { +pub fn make_keypair() -> bitcoin::secp256k1::Keypair { let secp = Secp256k1::new(); - let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng()); + let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng()); + Keypair::from_secret_key(&secp, &secret_key) +} +pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { + let key_pair = make_keypair(); let mut file = File::create(key_path)?; - let pub_str = public_key.to_string(); - let priv_str = secret_key.display_secret().to_string(); + let pub_str = key_pair.public_key().to_string(); + let priv_str = key_pair.display_secret().to_string(); println!("private key: {priv_str}"); let out = format!("{pub_str}/{priv_str}"); println!("out: {out}"); diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 8944442..068d00e 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -6,7 +6,7 @@ use tokio::{sync::mpsc, task}; use websocket::WebSocketClient; pub mod bft_crdt_keys; -pub(crate) mod bitcoin_keys; +pub mod bitcoin_keys; pub(crate) mod cli; pub mod crdt; pub(crate) mod init; diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index be38a1b..524b11a 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -2,7 +2,9 @@ use bft_json_crdt::{ json_crdt::{BaseCrdt, SignedOp}, keypair::make_keypair, }; -use side_node::{crdt::TransactionList, node::SideNode, utils, websocket::WebSocketClient}; +use side_node::{ + bitcoin_keys, crdt::TransactionList, node::SideNode, utils, websocket::WebSocketClient, +}; use tokio::sync::mpsc; #[tokio::test] @@ -34,9 +36,9 @@ async fn test_distribute_via_websockets() { /// Wire everything up, ignoring things we are not using in the test async fn setup(_: &str) -> SideNode { // First, load up the keys and create a bft-crdt - let keys = make_keypair(); - let bitcoin_keys = bitcoin::secp256k1::Keypair::new(); - let crdt = BaseCrdt::::new(&keys); + let bft_crdt_keys = make_keypair(); + let bitcoin_keys = bitcoin_keys::make_keypair(); + let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input let (incoming_sender, incoming_receiver) = mpsc::channel::(32); @@ -44,6 +46,13 @@ async fn setup(_: &str) -> SideNode { // Finally, create the node and return it let handle = WebSocketClient::new(incoming_sender).await; - let node = SideNode::new(crdt, keys, incoming_receiver, stdin_receiver, handle); + let node = SideNode::new( + crdt, + bft_crdt_keys, + bitcoin_keys, + incoming_receiver, + stdin_receiver, + handle, + ); node } From 089201b7bef5f0550c9d4b2052def6196b52220d Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 17:04:29 +0100 Subject: [PATCH 08/55] Removed unused import --- side-node/src/bitcoin_keys.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/side-node/src/bitcoin_keys.rs b/side-node/src/bitcoin_keys.rs index 53baedd..968752f 100644 --- a/side-node/src/bitcoin_keys.rs +++ b/side-node/src/bitcoin_keys.rs @@ -1,4 +1,4 @@ -use bitcoin::secp256k1::{rand, Keypair, PublicKey, Secp256k1, SecretKey}; +use bitcoin::secp256k1::{rand, Keypair, Secp256k1, SecretKey}; use std::{ fs::{self, File}, io::Write, From 60e87383b02155dca2b127e679462f1317fe50e4 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 17:12:05 +0100 Subject: [PATCH 09/55] Getting ready to format a Bitcoin transaction --- side-node/src/node.rs | 2 +- side-node/src/utils.rs | 2 +- side-node/tests/crdt.rs | 6 +++--- side-node/tests/side_node.rs | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/side-node/src/node.rs b/side-node/src/node.rs index 8d1aac6..19156a8 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -39,7 +39,7 @@ impl SideNode { loop { match self.stdin_receiver.try_recv() { Ok(stdin) => { - let transaction = utils::fake_transaction_json(stdin); + let transaction = utils::fake_generic_transaction_json(stdin); let json = serde_json::to_value(transaction).unwrap(); let signed_op = self.add_transaction_local(json); println!("STDIN: {}", utils::shappy(signed_op.clone())); diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index aa55a83..dda9264 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -29,7 +29,7 @@ pub(crate) fn home(name: &String) -> std::path::PathBuf { } /// Generate a fake transaction with customizable from_pubkey String -pub fn fake_transaction_json(from: String) -> Value { +pub fn fake_generic_transaction_json(from: String) -> Value { json!({ "from": from, "to": "Bob", diff --git a/side-node/tests/crdt.rs b/side-node/tests/crdt.rs index d9f0a1c..57c2180 100644 --- a/side-node/tests/crdt.rs +++ b/side-node/tests/crdt.rs @@ -11,9 +11,9 @@ fn test_valid_updates() { let keypair1 = make_keypair(); let mut crdt1 = BaseCrdt::::new(&keypair1); - let val_a = side_node::utils::fake_transaction_json(String::from("a")); - let val_b = side_node::utils::fake_transaction_json(String::from("b")); - let val_c = side_node::utils::fake_transaction_json(String::from("c")); + let val_a = side_node::utils::fake_generic_transaction_json(String::from("a")); + let val_b = side_node::utils::fake_generic_transaction_json(String::from("b")); + let val_c = side_node::utils::fake_generic_transaction_json(String::from("c")); let _a = crdt1 .doc diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index 524b11a..4263972 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -14,19 +14,19 @@ async fn test_distribute_via_websockets() { assert_eq!(node1.current_sha(), node2.current_sha()); - let transaction = utils::fake_transaction_json("from_alice".to_string()); + let transaction = utils::fake_generic_transaction_json("from_alice".to_string()); let signed_op = node1.add_transaction_local(transaction); node2.handle_incoming(signed_op); assert_eq!(node1.current_sha(), node2.current_sha()); - let transaction = utils::fake_transaction_json("from_alice2".to_string()); + let transaction = utils::fake_generic_transaction_json("from_alice2".to_string()); let signed_op = node1.add_transaction_local(transaction); node2.handle_incoming(signed_op); assert_eq!(node1.current_sha(), node2.current_sha()); - let transaction = utils::fake_transaction_json("from_alice3".to_string()); + let transaction = utils::fake_generic_transaction_json("from_alice3".to_string()); let signed_op = node1.add_transaction_local(transaction); node2.handle_incoming(signed_op); From 1ad7c99283b08fd64f826217663092ecc8705c43 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 18 Jun 2024 17:43:32 +0100 Subject: [PATCH 10/55] wip btc --- side-node/src/utils.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index dda9264..14599e3 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use bft_json_crdt::json_crdt::SignedOp; +use bitcoin::{absolute, key::Keypair, transaction::Version, TxIn, TxOut}; use serde_json::{json, Value}; pub(crate) const BITCOIN_KEY_FILE: &str = "bitcoin_keys.pem"; @@ -37,6 +38,31 @@ pub fn fake_generic_transaction_json(from: String) -> Value { }) } +/// Generate a Bitcoin transaction from this node's bitcoin_keys +pub fn fake_bitcoin_transaction(key_pair: Keypair) -> bitcoin::Transaction { + let from = key_pair.public_key().to_string(); + let to = "Bob"; + let amount = 1; + let lock_time = absolute::LockTime::from_height(0).expect("couldn't format btc lock time"); + let input = TxIn { + previous_output: todo!(), + script_sig: todo!(), + sequence: todo!(), + witness: todo!(), + }; + let output = TxOut { + value: todo!(), + script_pubkey: todo!(), + }; + let tx = bitcoin::Transaction { + version: Version(1), + lock_time, + input, + output, + }; + tx +} + pub fn shappy(op: SignedOp) -> String { let b = serde_json::to_string(&op).unwrap().into_bytes(); sha256::digest(b).to_string() From a29a0fca0426c62aa920f253caf03e965aa07016 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 20 Jun 2024 17:13:34 +0100 Subject: [PATCH 11/55] Moved keys submodules --- side-node/src/init/mod.rs | 6 +++--- side-node/src/{bft_crdt_keys.rs => keys/bft_crdt.rs} | 0 side-node/src/{bitcoin_keys.rs => keys/bitcoin.rs} | 0 side-node/src/keys/mod.rs | 2 ++ side-node/src/lib.rs | 7 +++---- side-node/tests/side_node.rs | 6 ++---- 6 files changed, 10 insertions(+), 11 deletions(-) rename side-node/src/{bft_crdt_keys.rs => keys/bft_crdt.rs} (100%) rename side-node/src/{bitcoin_keys.rs => keys/bitcoin.rs} (100%) create mode 100644 side-node/src/keys/mod.rs diff --git a/side-node/src/init/mod.rs b/side-node/src/init/mod.rs index ab0457a..2f07328 100644 --- a/side-node/src/init/mod.rs +++ b/side-node/src/init/mod.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use config::SideNodeConfig; -use crate::{bft_crdt_keys, bitcoin_keys, utils}; +use crate::{keys, utils}; pub(crate) mod config; @@ -11,10 +11,10 @@ pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io: let (bft_crdt_key_path, bitcoin_key_path, config_path) = utils::side_paths(home.clone()); println!("Writing bft crdt key to: {:?}", bft_crdt_key_path); - bft_crdt_keys::write(&bft_crdt_key_path)?; + keys::bft_crdt::write(&bft_crdt_key_path)?; println!("Writing bitcoin key to: {:?}", bitcoin_key_path); - bitcoin_keys::write(&bitcoin_key_path)?; + keys::bitcoin::write(&bitcoin_key_path)?; println!("Writing config to: {:?}", config_path); config::write_toml(&config, &config_path).expect("unable to write config file"); diff --git a/side-node/src/bft_crdt_keys.rs b/side-node/src/keys/bft_crdt.rs similarity index 100% rename from side-node/src/bft_crdt_keys.rs rename to side-node/src/keys/bft_crdt.rs diff --git a/side-node/src/bitcoin_keys.rs b/side-node/src/keys/bitcoin.rs similarity index 100% rename from side-node/src/bitcoin_keys.rs rename to side-node/src/keys/bitcoin.rs diff --git a/side-node/src/keys/mod.rs b/side-node/src/keys/mod.rs new file mode 100644 index 0000000..c5a0529 --- /dev/null +++ b/side-node/src/keys/mod.rs @@ -0,0 +1,2 @@ +pub mod bft_crdt; +pub mod bitcoin; diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 068d00e..9400626 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -5,11 +5,10 @@ use node::SideNode; use tokio::{sync::mpsc, task}; use websocket::WebSocketClient; -pub mod bft_crdt_keys; -pub mod bitcoin_keys; pub(crate) mod cli; pub mod crdt; pub(crate) mod init; +pub mod keys; pub mod node; pub(crate) mod stdin; pub mod utils; @@ -39,8 +38,8 @@ pub async fn run() { async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); - let bft_crdt_keys = bft_crdt_keys::load_from_file(&side_dir); - let bitcoin_keys = bitcoin_keys::load_from_file(&side_dir); + let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); + let bitcoin_keys = keys::bitcoin::load_from_file(&side_dir); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index 4263972..355d7d6 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -2,9 +2,7 @@ use bft_json_crdt::{ json_crdt::{BaseCrdt, SignedOp}, keypair::make_keypair, }; -use side_node::{ - bitcoin_keys, crdt::TransactionList, node::SideNode, utils, websocket::WebSocketClient, -}; +use side_node::{crdt::TransactionList, keys, node::SideNode, utils, websocket::WebSocketClient}; use tokio::sync::mpsc; #[tokio::test] @@ -37,7 +35,7 @@ async fn test_distribute_via_websockets() { async fn setup(_: &str) -> SideNode { // First, load up the keys and create a bft-crdt let bft_crdt_keys = make_keypair(); - let bitcoin_keys = bitcoin_keys::make_keypair(); + let bitcoin_keys = keys::bitcoin::make_keypair(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input From 53b17591b8d6c164ca7f3c1956f5d86f5632c572 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 20 Jun 2024 17:13:47 +0100 Subject: [PATCH 12/55] Fixed bitcoin tx compilation (currently unused) --- side-node/src/utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index 14599e3..40bac26 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -57,8 +57,8 @@ pub fn fake_bitcoin_transaction(key_pair: Keypair) -> bitcoin::Transaction { let tx = bitcoin::Transaction { version: Version(1), lock_time, - input, - output, + input: vec![input], + output: vec![output], }; tx } From c0c5a12e8429ad825ff3fd5c0fbacb93c14bfba5 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 20 Jun 2024 17:13:56 +0100 Subject: [PATCH 13/55] Added a bitcoin client --- Cargo.lock | 58 +++++++++++++++++++++++++++++++++++++++++++- side-node/Cargo.toml | 2 ++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5fd95be..a48d3d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,7 @@ dependencies = [ "hex-conservative", "hex_lit", "secp256k1 0.29.0", + "serde", ] [[package]] @@ -414,6 +415,9 @@ name = "bitcoin-internals" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] [[package]] name = "bitcoin-io" @@ -434,6 +438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb54da0b28892f3c52203a7191534033e051b6f4b52bc15480681b57b7e036f5" dependencies = [ "bitcoin-internals", + "serde", ] [[package]] @@ -453,6 +458,31 @@ checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", "hex-conservative", + "serde", +] + +[[package]] +name = "bitcoincore-rpc" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" +dependencies = [ + "bitcoincore-rpc-json", + "jsonrpc", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "bitcoincore-rpc-json" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" +dependencies = [ + "bitcoin", + "serde", + "serde_json", ] [[package]] @@ -1522,6 +1552,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpc" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" +dependencies = [ + "base64 0.13.1", + "minreq", + "serde", + "serde_json", +] + [[package]] name = "keccak" version = "0.1.5" @@ -1593,6 +1635,17 @@ dependencies = [ "adler", ] +[[package]] +name = "minreq" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fdef521c74c2884a4f3570bcdb6d2a77b3c533feb6b27ac2ae72673cc221c64" +dependencies = [ + "log", + "serde", + "serde_json", +] + [[package]] name = "mio" version = "0.8.11" @@ -2210,9 +2263,10 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" dependencies = [ - "bitcoin_hashes 0.12.0", + "bitcoin_hashes 0.14.0", "rand 0.8.5", "secp256k1-sys 0.10.0", + "serde", ] [[package]] @@ -2424,6 +2478,8 @@ dependencies = [ "bft-crdt-derive", "bft-json-crdt", "bitcoin", + "bitcoincore-rpc", + "bitcoincore-rpc-json", "clap 4.5.4", "dirs", "ezsockets", diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index dfee8df..bb9b395 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -23,6 +23,8 @@ tracing = "0.1.32" # tracing-subscriber = "0.3.9" toml = "0.8.14" indexmap = { version = "2.2.6", features = ["serde"] } +bitcoincore-rpc = "0.19.0" +bitcoincore-rpc-json = "0.19.0" [dev-dependencies] uuid = { version = "1.8.0", features = ["v4"] } From 13e144f19e22b5ca1e97da7f7a17643b66eec278 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 20 Jun 2024 17:21:41 +0100 Subject: [PATCH 14/55] Implemented a blank Btc command --- side-node/src/cli/mod.rs | 3 +++ side-node/src/clients/btc.rs | 14 ++++++++++++++ side-node/src/clients/mod.rs | 2 ++ side-node/src/{ => clients}/websocket.rs | 14 ++++++-------- side-node/src/lib.rs | 9 ++++++--- side-node/src/node.rs | 6 +++--- side-node/tests/side_node.rs | 4 ++-- 7 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 side-node/src/clients/btc.rs create mode 100644 side-node/src/clients/mod.rs rename side-node/src/{ => clients}/websocket.rs (86%) diff --git a/side-node/src/cli/mod.rs b/side-node/src/cli/mod.rs index cf50b6f..d20431d 100644 --- a/side-node/src/cli/mod.rs +++ b/side-node/src/cli/mod.rs @@ -17,6 +17,9 @@ pub(crate) struct Args { #[derive(Subcommand)] pub(crate) enum Commands { + /// Placeholder for future BTC commands + Btc {}, + /// runs the Side Node Run { name: String }, diff --git a/side-node/src/clients/btc.rs b/side-node/src/clients/btc.rs new file mode 100644 index 0000000..dc5359c --- /dev/null +++ b/side-node/src/clients/btc.rs @@ -0,0 +1,14 @@ +use bitcoincore_rpc::{Auth, Client, RpcApi}; + +pub fn client() { + let rpc = Client::new( + "http://localhost:8332", + Auth::UserPass( + "".to_string(), + "".to_string(), + ), + ) + .unwrap(); + let best_block_hash = rpc.get_best_block_hash().unwrap(); + println!("best block hash: {}", best_block_hash); +} diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs new file mode 100644 index 0000000..e0c6d71 --- /dev/null +++ b/side-node/src/clients/mod.rs @@ -0,0 +1,2 @@ +pub mod btc; +pub mod websocket; diff --git a/side-node/src/websocket.rs b/side-node/src/clients/websocket.rs similarity index 86% rename from side-node/src/websocket.rs rename to side-node/src/clients/websocket.rs index b3a1e05..f7bfde0 100644 --- a/side-node/src/websocket.rs +++ b/side-node/src/clients/websocket.rs @@ -5,19 +5,17 @@ use tokio::sync::mpsc; use crate::utils; -pub struct WebSocketClient { +pub struct Client { incoming_sender: mpsc::Sender, - handle: ezsockets::Client, + handle: ezsockets::Client, } -impl WebSocketClient { +impl Client { /// Start the websocket client - pub async fn new( - incoming_sender: mpsc::Sender, - ) -> ezsockets::Client { + pub async fn new(incoming_sender: mpsc::Sender) -> ezsockets::Client { let config = ClientConfig::new("ws://localhost:8080/websocket"); let (handle, future) = ezsockets::connect( - |client| WebSocketClient { + |client| Client { incoming_sender, handle: client, }, @@ -32,7 +30,7 @@ impl WebSocketClient { } #[async_trait] -impl ezsockets::ClientExt for WebSocketClient { +impl ezsockets::ClientExt for Client { // Right now we're only using the Call type for sending signed ops // change this to an enum if we need to send other types of calls, and // match on it. diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 9400626..149b3de 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -1,18 +1,18 @@ use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use cli::{parse_args, Commands}; +use clients::websocket; use crdt::TransactionList; use node::SideNode; use tokio::{sync::mpsc, task}; -use websocket::WebSocketClient; pub(crate) mod cli; +pub mod clients; pub mod crdt; pub(crate) mod init; pub mod keys; pub mod node; pub(crate) mod stdin; pub mod utils; -pub mod websocket; #[tokio::main] pub async fn run() { @@ -30,6 +30,9 @@ pub async fn run() { let mut node = setup(name).await; node.start().await; } + Some(Commands::Btc {}) => { + println!("BTC command not yet implemented."); + } None => println!("No command provided. Exiting. See --help for more information."), } } @@ -50,7 +53,7 @@ async fn setup(name: &String) -> SideNode { }); // Finally, create the node and return it - let handle = WebSocketClient::new(incoming_sender).await; + let handle = websocket::Client::new(incoming_sender).await; let node = SideNode::new( crdt, bft_crdt_keys, diff --git a/side-node/src/node.rs b/side-node/src/node.rs index 19156a8..882b4a0 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -2,7 +2,7 @@ use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use fastcrypto::ed25519::Ed25519KeyPair; use tokio::sync::mpsc; -use crate::{crdt::TransactionList, utils, websocket::WebSocketClient}; +use crate::{clients::websocket::Client, crdt::TransactionList, utils}; pub struct SideNode { crdt: BaseCrdt, @@ -10,7 +10,7 @@ pub struct SideNode { bitcoin_keys: bitcoin::secp256k1::Keypair, incoming_receiver: mpsc::Receiver, stdin_receiver: std::sync::mpsc::Receiver, - handle: ezsockets::Client, + handle: ezsockets::Client, } impl SideNode { @@ -20,7 +20,7 @@ impl SideNode { bitcoin_keys: bitcoin::secp256k1::Keypair, incoming_receiver: mpsc::Receiver, stdin_receiver: std::sync::mpsc::Receiver, - handle: ezsockets::Client, + handle: ezsockets::Client, ) -> Self { let node = Self { crdt, diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index 355d7d6..951a1a9 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -2,7 +2,7 @@ use bft_json_crdt::{ json_crdt::{BaseCrdt, SignedOp}, keypair::make_keypair, }; -use side_node::{crdt::TransactionList, keys, node::SideNode, utils, websocket::WebSocketClient}; +use side_node::{clients::websocket::Client, crdt::TransactionList, keys, node::SideNode, utils}; use tokio::sync::mpsc; #[tokio::test] @@ -43,7 +43,7 @@ async fn setup(_: &str) -> SideNode { let (_, stdin_receiver) = std::sync::mpsc::channel(); // Finally, create the node and return it - let handle = WebSocketClient::new(incoming_sender).await; + let handle = Client::new(incoming_sender).await; let node = SideNode::new( crdt, bft_crdt_keys, From c5a6aeb067a49d5d2cc0f3340574c7a0f8ca0ba5 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 20 Jun 2024 19:46:56 +0100 Subject: [PATCH 15/55] Added a working btc-rpc client, works with a running local signet node --- side-node/src/clients/btc.rs | 14 -------------- side-node/src/clients/btc_rpc.rs | 27 +++++++++++++++++++++++++++ side-node/src/clients/mod.rs | 2 +- side-node/src/lib.rs | 2 +- 4 files changed, 29 insertions(+), 16 deletions(-) delete mode 100644 side-node/src/clients/btc.rs create mode 100644 side-node/src/clients/btc_rpc.rs diff --git a/side-node/src/clients/btc.rs b/side-node/src/clients/btc.rs deleted file mode 100644 index dc5359c..0000000 --- a/side-node/src/clients/btc.rs +++ /dev/null @@ -1,14 +0,0 @@ -use bitcoincore_rpc::{Auth, Client, RpcApi}; - -pub fn client() { - let rpc = Client::new( - "http://localhost:8332", - Auth::UserPass( - "".to_string(), - "".to_string(), - ), - ) - .unwrap(); - let best_block_hash = rpc.get_best_block_hash().unwrap(); - println!("best block hash: {}", best_block_hash); -} diff --git a/side-node/src/clients/btc_rpc.rs b/side-node/src/clients/btc_rpc.rs new file mode 100644 index 0000000..b7a9166 --- /dev/null +++ b/side-node/src/clients/btc_rpc.rs @@ -0,0 +1,27 @@ +use std::str::FromStr; + +use bitcoincore_rpc::{Auth, Client, RpcApi}; + +fn parse_and_validate_address( + address: &str, + network: bitcoin::Network, +) -> Result { + let address = address + .parse::>()? + .require_network(network)?; + Ok(address) +} + +pub fn client() { + let rpc = Client::new( + "http://127.0.0.1:38332", + Auth::UserPass("dave".to_string(), "password".to_string()), + ) + .unwrap(); + + let a = "tb1p4dvr6dzszagw34scnr6kde0dr2yrmu6gew9faza788rmqy3d645sdm9e50"; + let address = parse_and_validate_address(a, bitcoin::Network::Signet).unwrap(); + let info = rpc.get_address_info(&address).unwrap(); + + println!("info {info:?} "); +} diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs index e0c6d71..3190f48 100644 --- a/side-node/src/clients/mod.rs +++ b/side-node/src/clients/mod.rs @@ -1,2 +1,2 @@ -pub mod btc; +pub mod btc_rpc; pub mod websocket; diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 149b3de..266018f 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -31,7 +31,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - println!("BTC command not yet implemented."); + clients::btc_rpc::client(); } None => println!("No command provided. Exiting. See --help for more information."), } From 14f24c6d349b2a462106472df600b2e491d47e14 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Fri, 21 Jun 2024 16:26:43 +0100 Subject: [PATCH 16/55] WIP commit with rustbitcoin-rpc, which is deeply unpleasant and unfinished --- Cargo.lock | 40 +++++++++++++++++++ side-node/Cargo.toml | 5 ++- side-node/src/clients/btc_rpc.rs | 67 +++++++++++++++++++++++++++----- side-node/src/lib.rs | 2 +- side-node/src/utils.rs | 1 + 5 files changed, 102 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a48d3d8..5861e63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,6 +342,33 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bdk_chain" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "163b064557cee078e8ee5dd2c88944204506f7b2b1524f78e8fcba38c346da7b" +dependencies = [ + "bitcoin", + "miniscript", + "serde", +] + +[[package]] +name = "bdk_wallet" +version = "1.0.0-alpha.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2926afdbfc54ebf7df2caa51af5be4435b90c01c6fbe5578b51b7c2c0a264bd9" +dependencies = [ + "bdk_chain", + "bitcoin", + "getrandom 0.2.15", + "js-sys", + "miniscript", + "rand 0.8.5", + "serde", + "serde_json", +] + [[package]] name = "bech32" version = "0.9.1" @@ -399,6 +426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" dependencies = [ "base58ck", + "base64 0.21.7", "bech32 0.11.0", "bitcoin-internals", "bitcoin-io", @@ -1626,6 +1654,17 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "miniscript" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b59c67956fd276ceec0cf194fbf80754ef4d88a496d5cf5e4fdf33561466183d" +dependencies = [ + "bech32 0.11.0", + "bitcoin", + "serde", +] + [[package]] name = "miniz_oxide" version = "0.7.3" @@ -2475,6 +2514,7 @@ name = "side-node" version = "0.1.0" dependencies = [ "async-trait", + "bdk_wallet", "bft-crdt-derive", "bft-json-crdt", "bitcoin", diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index bb9b395..9f4768d 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -10,6 +10,8 @@ async-trait = "0.1.52" bft-json-crdt = { path = "../crates/bft-json-crdt" } bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" } bitcoin = { version = "0.32.2", features = ["rand"] } +bitcoincore-rpc = "0.19.0" +bitcoincore-rpc-json = "0.19.0" clap = { version = "4.5.4", features = ["derive"] } dirs = "5.0.1" ezsockets = { version = "*", features = ["client"] } @@ -23,8 +25,7 @@ tracing = "0.1.32" # tracing-subscriber = "0.3.9" toml = "0.8.14" indexmap = { version = "2.2.6", features = ["serde"] } -bitcoincore-rpc = "0.19.0" -bitcoincore-rpc-json = "0.19.0" +bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys"] } [dev-dependencies] uuid = { version = "1.8.0", features = ["v4"] } diff --git a/side-node/src/clients/btc_rpc.rs b/side-node/src/clients/btc_rpc.rs index b7a9166..0c73ece 100644 --- a/side-node/src/clients/btc_rpc.rs +++ b/side-node/src/clients/btc_rpc.rs @@ -1,27 +1,74 @@ use std::str::FromStr; +use bitcoin::{address::NetworkUnchecked, key::Secp256k1, Address, Amount}; use bitcoincore_rpc::{Auth, Client, RpcApi}; +use crate::{keys, utils}; + +pub fn run(wallet_name: &str) { + let rpc = client_for(wallet_name); + let name = rpc.get_wallet_info().unwrap().wallet_name; + println!("Wallet name: {name}"); + let balance = rpc.get_balance(None, None).unwrap(); + let address = get_default_address(rpc); + let account = rpc.generate_to_address(block_num, address); + println!("account {account:?} has balance {balance:?}"); + + // let sammy = Address::from_str("tb1qtd5e44gf6eqqdknymaydqyvw6869fda32w3l06").unwrap(); + // let amount = Amount::from_sat(1000); + // let sammy = parse_and_validate_address(sammy).unwrap(); + // transfer_funds(rpc, amount, sammy); +} + +// fn login(rpc: Client, wallet_name: &str) { +// let passphrase = serde_json::Value::from_str("password").unwrap(); +// rpc.call("walletpassphrase", &[passphrase]).unwrap(); +// } + +fn get_default_address(rpc: Client) -> Address { + let address = rpc.generate_to_address(None, None).unwrap(); + println!("Default address: {address:?}"); + parse_and_validate_address(address).unwrap() +} + +fn get_info(rpc: Client) { + let info = rpc.get_wallet_info().unwrap(); + println!("Wallet info: {info:?}"); +} + fn parse_and_validate_address( - address: &str, - network: bitcoin::Network, + address: bitcoin::Address, ) -> Result { - let address = address - .parse::>()? - .require_network(network)?; + let require_network = address.require_network(bitcoin::Network::Signet); + let address = require_network?; Ok(address) } -pub fn client() { +fn transfer_funds(rpc: Client, amount: Amount, to: Address) { + rpc.send_to_address(&to, amount, None, None, None, None, None, None) + .unwrap(); +} + +fn client_for(wallet_name: &str) -> Client { let rpc = Client::new( "http://127.0.0.1:38332", Auth::UserPass("dave".to_string(), "password".to_string()), ) .unwrap(); - let a = "tb1p4dvr6dzszagw34scnr6kde0dr2yrmu6gew9faza788rmqy3d645sdm9e50"; - let address = parse_and_validate_address(a, bitcoin::Network::Signet).unwrap(); - let info = rpc.get_address_info(&address).unwrap(); + if !rpc + .list_wallets() + .unwrap() + .contains(&wallet_name.to_string()) + { + rpc.load_wallet(wallet_name).unwrap(); + } - println!("info {info:?} "); + rpc.list_wallets().unwrap().iter().for_each(|wallet| { + if wallet != wallet_name { + rpc.unload_wallet(Some(wallet)).unwrap(); + } + }); + + rpc } diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 266018f..88a60a8 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -31,7 +31,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - clients::btc_rpc::client(); + clients::btc_rpc::run("sammy_wallet"); } None => println!("No command provided. Exiting. See --help for more information."), } diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index 40bac26..8ecad01 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -22,6 +22,7 @@ pub(crate) fn side_paths(prefix: PathBuf) -> (PathBuf, PathBuf, PathBuf) { (bft_crdt_key_path, bitcoin_key_path, config_path) } +/// Returns the path to the home directory for this host OS and the given node name pub(crate) fn home(name: &String) -> std::path::PathBuf { let mut path = dirs::home_dir().unwrap(); path.push(".side"); From 933fea76df09b53cda08742b27b3b238c1c764c9 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Fri, 21 Jun 2024 16:34:53 +0100 Subject: [PATCH 17/55] Going to try out the bdk --- Cargo.lock | 203 +++++++++++++------------------ side-node/Cargo.lock | 7 -- side-node/Cargo.toml | 4 +- side-node/src/clients/btc_rpc.rs | 75 +----------- 4 files changed, 89 insertions(+), 200 deletions(-) delete mode 100644 side-node/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index 5861e63..c46e125 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,9 +91,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -250,7 +250,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -289,9 +289,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -360,6 +360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2926afdbfc54ebf7df2caa51af5be4435b90c01c6fbe5578b51b7c2c0a264bd9" dependencies = [ "bdk_chain", + "bip39", "bitcoin", "getrandom 0.2.15", "js-sys", @@ -419,6 +420,17 @@ dependencies = [ "serde", ] +[[package]] +name = "bip39" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +dependencies = [ + "bitcoin_hashes 0.11.0", + "serde", + "unicode-normalization", +] + [[package]] name = "bitcoin" version = "0.32.2" @@ -469,6 +481,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" + [[package]] name = "bitcoin_hashes" version = "0.12.0" @@ -489,30 +507,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitcoincore-rpc" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" -dependencies = [ - "bitcoincore-rpc-json", - "jsonrpc", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "bitcoincore-rpc-json" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" -dependencies = [ - "bitcoin", - "serde", - "serde_json", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -554,9 +548,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" dependencies = [ "cc", "glob", @@ -596,9 +590,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -660,9 +654,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -670,26 +664,26 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.0", + "clap_lex 0.7.1", "strsim", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -703,9 +697,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -891,7 +885,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -902,7 +896,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -956,15 +950,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.67", ] [[package]] @@ -1277,7 +1271,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1479,9 +1473,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "iana-time-zone" @@ -1580,18 +1574,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonrpc" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" -dependencies = [ - "base64 0.13.1", - "minreq", - "serde", - "serde_json", -] - [[package]] name = "keccak" version = "0.1.5" @@ -1650,9 +1632,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniscript" @@ -1667,24 +1649,13 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] -[[package]] -name = "minreq" -version = "2.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fdef521c74c2884a4f3570bcdb6d2a77b3c533feb6b27ac2ae72673cc221c64" -dependencies = [ - "log", - "serde", - "serde_json", -] - [[package]] name = "mio" version = "0.8.11" @@ -1781,9 +1752,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1994,9 +1965,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -2128,14 +2099,14 @@ checksum = "a25d631e41bfb5fdcde1d4e2215f62f7f0afa3ff11e26563765bd6ea1d229aeb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -2262,7 +2233,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2349,7 +2320,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2360,7 +2331,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2427,7 +2398,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2439,7 +2410,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2518,9 +2489,7 @@ dependencies = [ "bft-crdt-derive", "bft-json-crdt", "bitcoin", - "bitcoincore-rpc", - "bitcoincore-rpc-json", - "clap 4.5.4", + "clap 4.5.7", "dirs", "ezsockets", "fastcrypto", @@ -2630,9 +2599,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5" [[package]] name = "subtle-ng" @@ -2653,9 +2622,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" dependencies = [ "proc-macro2", "quote", @@ -2685,7 +2654,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2776,9 +2745,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -2795,13 +2764,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2876,7 +2845,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.12", + "winnow 0.6.13", ] [[package]] @@ -2898,7 +2867,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2975,9 +2944,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] @@ -2990,9 +2959,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -3007,9 +2976,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -3081,7 +3050,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", "wasm-bindgen-shared", ] @@ -3115,7 +3084,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3340,9 +3309,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ff33f391015ecab21cd092389215eb265ef9496a9a07b6bee7d3529831deda" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -3364,7 +3333,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -3384,5 +3353,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] diff --git a/side-node/Cargo.lock b/side-node/Cargo.lock deleted file mode 100644 index 37680ea..0000000 --- a/side-node/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "side-node" -version = "0.1.0" diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index 9f4768d..abf102c 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -7,11 +7,10 @@ edition = "2021" [dependencies] async-trait = "0.1.52" +bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys"] } bft-json-crdt = { path = "../crates/bft-json-crdt" } bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" } bitcoin = { version = "0.32.2", features = ["rand"] } -bitcoincore-rpc = "0.19.0" -bitcoincore-rpc-json = "0.19.0" clap = { version = "4.5.4", features = ["derive"] } dirs = "5.0.1" ezsockets = { version = "*", features = ["client"] } @@ -25,7 +24,6 @@ tracing = "0.1.32" # tracing-subscriber = "0.3.9" toml = "0.8.14" indexmap = { version = "2.2.6", features = ["serde"] } -bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys"] } [dev-dependencies] uuid = { version = "1.8.0", features = ["v4"] } diff --git a/side-node/src/clients/btc_rpc.rs b/side-node/src/clients/btc_rpc.rs index 0c73ece..93f8a97 100644 --- a/side-node/src/clients/btc_rpc.rs +++ b/side-node/src/clients/btc_rpc.rs @@ -1,74 +1,3 @@ -use std::str::FromStr; - -use bitcoin::{address::NetworkUnchecked, key::Secp256k1, Address, Amount}; -use bitcoincore_rpc::{Auth, Client, RpcApi}; - -use crate::{keys, utils}; - -pub fn run(wallet_name: &str) { - let rpc = client_for(wallet_name); - let name = rpc.get_wallet_info().unwrap().wallet_name; - println!("Wallet name: {name}"); - let balance = rpc.get_balance(None, None).unwrap(); - let address = get_default_address(rpc); - let account = rpc.generate_to_address(block_num, address); - println!("account {account:?} has balance {balance:?}"); - - // let sammy = Address::from_str("tb1qtd5e44gf6eqqdknymaydqyvw6869fda32w3l06").unwrap(); - // let amount = Amount::from_sat(1000); - // let sammy = parse_and_validate_address(sammy).unwrap(); - // transfer_funds(rpc, amount, sammy); -} - -// fn login(rpc: Client, wallet_name: &str) { -// let passphrase = serde_json::Value::from_str("password").unwrap(); -// rpc.call("walletpassphrase", &[passphrase]).unwrap(); -// } - -fn get_default_address(rpc: Client) -> Address { - let address = rpc.generate_to_address(None, None).unwrap(); - println!("Default address: {address:?}"); - parse_and_validate_address(address).unwrap() -} - -fn get_info(rpc: Client) { - let info = rpc.get_wallet_info().unwrap(); - println!("Wallet info: {info:?}"); -} - -fn parse_and_validate_address( - address: bitcoin::Address, -) -> Result { - let require_network = address.require_network(bitcoin::Network::Signet); - let address = require_network?; - Ok(address) -} - -fn transfer_funds(rpc: Client, amount: Amount, to: Address) { - rpc.send_to_address(&to, amount, None, None, None, None, None, None) - .unwrap(); -} - -fn client_for(wallet_name: &str) -> Client { - let rpc = Client::new( - "http://127.0.0.1:38332", - Auth::UserPass("dave".to_string(), "password".to_string()), - ) - .unwrap(); - - if !rpc - .list_wallets() - .unwrap() - .contains(&wallet_name.to_string()) - { - rpc.load_wallet(wallet_name).unwrap(); - } - - rpc.list_wallets().unwrap().iter().for_each(|wallet| { - if wallet != wallet_name { - rpc.unload_wallet(Some(wallet)).unwrap(); - } - }); - - rpc +pub fn run(name: &str) { + println!("running btc_rpc..."); } From ac6473bb1bc71c18960399958a7263bb21cbf333 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Fri, 21 Jun 2024 17:00:01 +0100 Subject: [PATCH 18/55] Ok the bdk looks like a far better bet! --- Cargo.lock | 818 ++++++++++++++++++++++++++++++- side-node/Cargo.toml | 9 +- side-node/src/clients/btc_rpc.rs | 115 ++++- side-node/src/lib.rs | 2 +- side-node/src/utils.rs | 50 +- 5 files changed, 952 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c46e125..26e1bd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "ark-ec" version = "0.4.2" @@ -318,6 +324,12 @@ dependencies = [ "bitcoin_hashes 0.14.0", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.1" @@ -342,17 +354,74 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bdk" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc1fc1a92e0943bfbcd6eb7d32c1b2a79f2f1357eb1e2eee9d7f36d6d7ca44a" +dependencies = [ + "async-trait", + "bdk-macros", + "bitcoin 0.30.2", + "electrum-client", + "getrandom 0.2.15", + "js-sys", + "log", + "miniscript 10.0.0", + "rand 0.8.5", + "serde", + "serde_json", + "sled", + "tokio", +] + +[[package]] +name = "bdk-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81c1980e50ae23bb6efa9283ae8679d6ea2c6fa6a99fe62533f65f4a25a1a56c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bdk_chain" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "163b064557cee078e8ee5dd2c88944204506f7b2b1524f78e8fcba38c346da7b" dependencies = [ - "bitcoin", - "miniscript", + "bitcoin 0.32.2", + "miniscript 12.0.0", "serde", ] +[[package]] +name = "bdk_esplora" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "089babab213bbb32518bad79a7313ebb4c85a52c18c8b558402dfa810c27de3f" +dependencies = [ + "async-trait", + "bdk_chain", + "esplora-client", + "futures", + "miniscript 12.0.0", +] + +[[package]] +name = "bdk_sqlite" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0926dc5778fb3c5afaf7def9ed9b9c9d9fe9e3ba499e09cb816d3de43211be71" +dependencies = [ + "bdk_chain", + "rusqlite", + "serde", + "serde_json", +] + [[package]] name = "bdk_wallet" version = "1.0.0-alpha.13" @@ -361,10 +430,10 @@ checksum = "2926afdbfc54ebf7df2caa51af5be4435b90c01c6fbe5578b51b7c2c0a264bd9" dependencies = [ "bdk_chain", "bip39", - "bitcoin", + "bitcoin 0.32.2", "getrandom 0.2.15", "js-sys", - "miniscript", + "miniscript 12.0.0", "rand 0.8.5", "serde", "serde_json", @@ -431,6 +500,21 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "bitcoin" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" +dependencies = [ + "base64 0.13.1", + "bech32 0.9.1", + "bitcoin-private", + "bitcoin_hashes 0.12.0", + "hex_lit", + "secp256k1 0.27.0", + "serde", +] + [[package]] name = "bitcoin" version = "0.32.2" @@ -494,6 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" dependencies = [ "bitcoin-private", + "serde", ] [[package]] @@ -747,6 +832,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -762,6 +857,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.4.0" @@ -1044,6 +1148,25 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +[[package]] +name = "electrum-client" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bc133f1c8d829d254f013f946653cbeb2b08674b960146361d1e9b67733ad19" +dependencies = [ + "bitcoin 0.30.2", + "bitcoin-private", + "byteorder", + "libc", + "log", + "rustls", + "serde", + "serde_json", + "webpki", + "webpki-roots 0.22.6", + "winapi", +] + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -1064,6 +1187,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "enfync" version = "0.1.6" @@ -1084,6 +1216,30 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "esplora-client" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69c6d27ef4ff21019edd98aa92199757e10a88065bbfcef6bb750ca6ec5e4a45" +dependencies = [ + "bitcoin 0.32.2", + "hex-conservative", + "log", + "minreq", + "reqwest", + "serde", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -1126,6 +1282,18 @@ dependencies = [ "wasmtimer", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastcrypto" version = "0.1.8" @@ -1190,6 +1358,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "ff" version = "0.13.0" @@ -1206,6 +1380,21 @@ 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.1" @@ -1215,6 +1404,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "futures" version = "0.3.30" @@ -1304,6 +1503,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1363,6 +1571,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -1393,6 +1620,18 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] [[package]] name = "heck" @@ -1471,12 +1710,66 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "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 = "iana-time-zone" version = "0.1.60" @@ -1544,6 +1837,21 @@ dependencies = [ "serde", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -1589,7 +1897,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] @@ -1614,6 +1922,23 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -1636,6 +1961,23 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniscript" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eb102b66b2127a872dbcc73095b7b47aeb9d92f7b03c2b2298253ffc82c7594" +dependencies = [ + "bitcoin 0.30.2", + "bitcoin-private", + "serde", +] + [[package]] name = "miniscript" version = "12.0.0" @@ -1643,7 +1985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b59c67956fd276ceec0cf194fbf80754ef4d88a496d5cf5e4fdf33561466183d" dependencies = [ "bech32 0.11.0", - "bitcoin", + "bitcoin 0.32.2", "serde", ] @@ -1656,6 +1998,22 @@ dependencies = [ "adler", ] +[[package]] +name = "minreq" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fdef521c74c2884a4f3570bcdb6d2a77b3c533feb6b27ac2ae72673cc221c64" +dependencies = [ + "base64 0.12.3", + "log", + "once_cell", + "rustls", + "rustls-webpki", + "serde", + "serde_json", + "webpki-roots 0.25.4", +] + [[package]] name = "mio" version = "0.8.11" @@ -1667,6 +2025,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1777,6 +2152,50 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "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 2.0.67", +] + +[[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.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -1807,6 +2226,17 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1814,7 +2244,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -1825,7 +2269,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -1904,6 +2348,12 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "plotters" version = "0.3.6" @@ -2102,6 +2552,15 @@ dependencies = [ "syn 2.0.67", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.2" @@ -2151,6 +2610,47 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "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", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-socks", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -2161,6 +2661,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rsa" version = "0.8.2" @@ -2182,6 +2697,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags 2.5.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2197,6 +2726,50 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.18" @@ -2212,6 +2785,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "schemars" version = "0.8.21" @@ -2242,6 +2824,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sec1" version = "0.7.3" @@ -2265,6 +2857,7 @@ dependencies = [ "bitcoin_hashes 0.12.0", "rand 0.8.5", "secp256k1-sys 0.8.1", + "serde", ] [[package]] @@ -2297,6 +2890,29 @@ dependencies = [ "cc", ] +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -2355,6 +2971,18 @@ 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 = "serde_with" version = "2.3.3" @@ -2484,11 +3112,15 @@ dependencies = [ name = "side-node" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", + "bdk", + "bdk_esplora", + "bdk_sqlite", "bdk_wallet", "bft-crdt-derive", "bft-json-crdt", - "bitcoin", + "bitcoin 0.32.2", "clap 4.5.7", "dirs", "ezsockets", @@ -2543,6 +3175,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot 0.11.2", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -2565,6 +3213,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.6.0" @@ -2631,6 +3285,45 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "textwrap" version = "0.16.1" @@ -2754,7 +3447,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2773,6 +3466,28 @@ dependencies = [ "syn 2.0.67", ] +[[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-tungstenite" version = "0.20.1" @@ -2803,6 +3518,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.14" @@ -2848,6 +3576,12 @@ dependencies = [ "winnow 0.6.13", ] +[[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.40" @@ -2905,6 +3639,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tungstenite" version = "0.20.1" @@ -2957,6 +3697,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.2" @@ -2995,6 +3741,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -3011,6 +3763,15 @@ dependencies = [ "winapi-util", ] +[[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.9.0+wasi-snapshot-preview1" @@ -3103,7 +3864,7 @@ checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" dependencies = [ "futures", "js-sys", - "parking_lot", + "parking_lot 0.12.3", "pin-utils", "slab", "wasm-bindgen", @@ -3119,6 +3880,31 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" @@ -3316,6 +4102,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "zerocopy" version = "0.7.34" diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index abf102c..7cfa711 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -6,7 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.86" async-trait = "0.1.52" +bdk = "0.29.0" +bdk_esplora = "0.15.0" +bdk_sqlite = "0.2.0" bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys"] } bft-json-crdt = { path = "../crates/bft-json-crdt" } bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" } @@ -15,15 +19,16 @@ clap = { version = "4.5.4", features = ["derive"] } dirs = "5.0.1" ezsockets = { version = "*", features = ["client"] } fastcrypto = "0.1.8" +indexmap = { version = "2.2.6", features = ["serde"] } # serde_cbor = "0.11.2" # move to this once we need to pack things in CBOR serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.117" sha256 = "1.5.0" tokio = { version = "1.37.0", features = ["full"] } +toml = "0.8.14" tracing = "0.1.32" # tracing-subscriber = "0.3.9" -toml = "0.8.14" -indexmap = { version = "2.2.6", features = ["serde"] } + [dev-dependencies] uuid = { version = "1.8.0", features = ["v4"] } diff --git a/side-node/src/clients/btc_rpc.rs b/side-node/src/clients/btc_rpc.rs index 93f8a97..10bc80d 100644 --- a/side-node/src/clients/btc_rpc.rs +++ b/side-node/src/clients/btc_rpc.rs @@ -1,3 +1,114 @@ -pub fn run(name: &str) { - println!("running btc_rpc..."); +use std::{collections::BTreeSet, io::Write, str::FromStr}; + +use bdk_esplora::{esplora_client, EsploraAsyncExt}; +use bdk_wallet::{ + bitcoin::{Address, Amount, Network, Script}, + KeychainKind, SignOptions, Wallet, +}; + +use bdk_sqlite::{rusqlite::Connection, Store}; + +const SEND_AMOUNT: Amount = Amount::from_sat(5000); +const STOP_GAP: usize = 50; +const PARALLEL_REQUESTS: usize = 5; + +pub async fn run() -> Result<(), anyhow::Error> { + let db_path = "/tmp/bdk-esplora-async-example.sqlite"; + let conn = Connection::open(db_path)?; + let mut db = Store::new(conn)?; + let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; + let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; + let changeset = db.read()?; + + let mut wallet = Wallet::new_or_load( + external_descriptor, + internal_descriptor, + changeset, + Network::Signet, + )?; + + let address = wallet.next_unused_address(KeychainKind::External); + if let Some(changeset) = wallet.take_staged() { + db.write(&changeset)?; + } + println!("Generated Address: {}", address); + + let balance = wallet.balance(); + println!("Wallet balance before syncing: {} sats", balance.total()); + + print!("Syncing..."); + let client = esplora_client::Builder::new("http://signet.bitcoindevkit.net").build_async()?; + + fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static { + let mut once = Some(()); + let mut stdout = std::io::stdout(); + move |spk_i, _| { + match once.take() { + Some(_) => print!("\nScanning keychain [{:?}]", kind), + None => print!(" {:<3}", spk_i), + }; + stdout.flush().expect("must flush"); + } + } + let request = wallet + .start_full_scan() + .inspect_spks_for_all_keychains({ + let mut once = BTreeSet::::new(); + move |keychain, spk_i, _| { + match once.insert(keychain) { + true => print!("\nScanning keychain [{:?}]", keychain), + false => print!(" {:<3}", spk_i), + } + std::io::stdout().flush().expect("must flush") + } + }) + .inspect_spks_for_keychain( + KeychainKind::External, + generate_inspect(KeychainKind::External), + ) + .inspect_spks_for_keychain( + KeychainKind::Internal, + generate_inspect(KeychainKind::Internal), + ); + + let mut update = client + .full_scan(request, STOP_GAP, PARALLEL_REQUESTS) + .await?; + let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + let _ = update.graph_update.update_last_seen_unconfirmed(now); + + wallet.apply_update(update)?; + if let Some(changeset) = wallet.take_staged() { + db.write(&changeset)?; + } + println!(); + + let balance = wallet.balance(); + println!("Wallet balance after syncing: {} sats", balance.total()); + + if balance.total() < SEND_AMOUNT { + println!( + "Please send at least {} sats to the receiving address", + SEND_AMOUNT + ); + std::process::exit(0); + } + + let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")? + .require_network(Network::Signet)?; + + let mut tx_builder = wallet.build_tx(); + tx_builder + .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT) + .enable_rbf(); + + let mut psbt = tx_builder.finish()?; + let finalized = wallet.sign(&mut psbt, SignOptions::default())?; + assert!(finalized); + + let tx = psbt.extract_tx()?; + client.broadcast(&tx).await?; + println!("Tx broadcasted! Txid: {}", tx.compute_txid()); + + Ok(()) } diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 88a60a8..25387b6 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -31,7 +31,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - clients::btc_rpc::run("sammy_wallet"); + let _ = clients::btc_rpc::run().await; } None => println!("No command provided. Exiting. See --help for more information."), } diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index 8ecad01..71940f2 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -1,8 +1,6 @@ -use std::path::PathBuf; - use bft_json_crdt::json_crdt::SignedOp; -use bitcoin::{absolute, key::Keypair, transaction::Version, TxIn, TxOut}; use serde_json::{json, Value}; +use std::path::PathBuf; pub(crate) const BITCOIN_KEY_FILE: &str = "bitcoin_keys.pem"; pub(crate) const BFT_CRDT_KEY_FILE: &str = "keys.pem"; @@ -40,29 +38,29 @@ pub fn fake_generic_transaction_json(from: String) -> Value { } /// Generate a Bitcoin transaction from this node's bitcoin_keys -pub fn fake_bitcoin_transaction(key_pair: Keypair) -> bitcoin::Transaction { - let from = key_pair.public_key().to_string(); - let to = "Bob"; - let amount = 1; - let lock_time = absolute::LockTime::from_height(0).expect("couldn't format btc lock time"); - let input = TxIn { - previous_output: todo!(), - script_sig: todo!(), - sequence: todo!(), - witness: todo!(), - }; - let output = TxOut { - value: todo!(), - script_pubkey: todo!(), - }; - let tx = bitcoin::Transaction { - version: Version(1), - lock_time, - input: vec![input], - output: vec![output], - }; - tx -} +// pub fn fake_bitcoin_transaction(key_pair: Keypair) -> bitcoin::Transaction { +// let from = key_pair.public_key().to_string(); +// let to = "Bob"; +// let amount = 1; +// let lock_time = absolute::LockTime::from_height(0).expect("couldn't format btc lock time"); +// let input = TxIn { +// previous_output: todo!(), +// script_sig: todo!(), +// sequence: todo!(), +// witness: todo!(), +// }; +// let output = TxOut { +// value: todo!(), +// script_pubkey: todo!(), +// }; +// let tx = bitcoin::Transaction { +// version: Version(1), +// lock_time, +// input: vec![input], +// output: vec![output], +// }; +// tx +// } pub fn shappy(op: SignedOp) -> String { let b = serde_json::to_string(&op).unwrap().into_bytes(); From d0f75d443b3680b8c22280b2cdb8450314a0f885 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Fri, 21 Jun 2024 17:45:37 +0100 Subject: [PATCH 19/55] Getting ready for mnemonic/key generation --- Cargo.lock | 1 + side-node/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 26e1bd2..54436c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,6 +362,7 @@ checksum = "2fc1fc1a92e0943bfbcd6eb7d32c1b2a79f2f1357eb1e2eee9d7f36d6d7ca44a" dependencies = [ "async-trait", "bdk-macros", + "bip39", "bitcoin 0.30.2", "electrum-client", "getrandom 0.2.15", diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index 7cfa711..dd89008 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] anyhow = "1.0.86" async-trait = "0.1.52" -bdk = "0.29.0" +bdk = { version = "0.29.0", default-feature = false, features = ["all-keys"] } bdk_esplora = "0.15.0" bdk_sqlite = "0.2.0" bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys"] } From d6c118ca3b76a6485bfa12acb445c2aba45ba7e5 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Fri, 21 Jun 2024 18:18:52 +0100 Subject: [PATCH 20/55] Generating a new wallet with mnemonic works nicely --- side-node/src/clients/btc_other_rpc.rs | 38 ++++++++++++++++++++++++++ side-node/src/clients/mod.rs | 1 + side-node/src/lib.rs | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 side-node/src/clients/btc_other_rpc.rs diff --git a/side-node/src/clients/btc_other_rpc.rs b/side-node/src/clients/btc_other_rpc.rs new file mode 100644 index 0000000..9f0c523 --- /dev/null +++ b/side-node/src/clients/btc_other_rpc.rs @@ -0,0 +1,38 @@ +use bdk::{ + bitcoin::Network, + database::MemoryDatabase, + keys::{ + bip39::{Language, Mnemonic, WordCount}, + DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey, + }, + miniscript, + template::Bip84, + KeychainKind, Wallet, +}; + +pub async fn run() -> Result<(), anyhow::Error> { + // Generate a new mnemonic + let mnemonic: GeneratedKey<_, miniscript::Segwitv0> = + Mnemonic::generate((WordCount::Words12, Language::English)).unwrap(); + let mnemonic_words = mnemonic.to_string(); + let mnemonic = Mnemonic::parse(&mnemonic_words).unwrap(); + + // Generate the extended key + let xkey: ExtendedKey = mnemonic.into_extended_key()?; + + // Get private key from the extended key + let xprv = xkey.into_xprv(Network::Signet).unwrap(); + + // Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1") + let wallet = Wallet::new( + Bip84(xprv, KeychainKind::External), + Some(Bip84(xprv, KeychainKind::Internal)), + Network::Signet, + MemoryDatabase::default(), + )?; + + let address = wallet.get_address(bdk::wallet::AddressIndex::New)?; + println!("Generated Address: {:?}", address); + + Ok(()) +} diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs index 3190f48..c2f3427 100644 --- a/side-node/src/clients/mod.rs +++ b/side-node/src/clients/mod.rs @@ -1,2 +1,3 @@ +pub mod btc_other_rpc; pub mod btc_rpc; pub mod websocket; diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 25387b6..9397bbf 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -31,7 +31,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - let _ = clients::btc_rpc::run().await; + let _ = clients::btc_other_rpc::run().await; } None => println!("No command provided. Exiting. See --help for more information."), } From 643a0d7f5299f9a327369c8dcc714581ab26a3c1 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 08:02:17 +0100 Subject: [PATCH 21/55] WIP --- side-node/src/clients/btc_other_rpc.rs | 96 ++++++++++++++++++-------- side-node/src/keys/bitcoin.rs | 69 +++++++++++------- side-node/src/lib.rs | 4 +- side-node/src/node.rs | 7 +- side-node/tests/side_node.rs | 5 +- 5 files changed, 120 insertions(+), 61 deletions(-) diff --git a/side-node/src/clients/btc_other_rpc.rs b/side-node/src/clients/btc_other_rpc.rs index 9f0c523..fe9ca56 100644 --- a/side-node/src/clients/btc_other_rpc.rs +++ b/side-node/src/clients/btc_other_rpc.rs @@ -1,38 +1,76 @@ -use bdk::{ - bitcoin::Network, - database::MemoryDatabase, - keys::{ - bip39::{Language, Mnemonic, WordCount}, - DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey, - }, - miniscript, - template::Bip84, - KeychainKind, Wallet, -}; +use std::io::Write; + +use bdk::{bitcoin::Script, KeychainKind}; +use bdk_esplora::esplora_client; + +use crate::{keys, utils}; pub async fn run() -> Result<(), anyhow::Error> { - // Generate a new mnemonic - let mnemonic: GeneratedKey<_, miniscript::Segwitv0> = - Mnemonic::generate((WordCount::Words12, Language::English)).unwrap(); - let mnemonic_words = mnemonic.to_string(); - let mnemonic = Mnemonic::parse(&mnemonic_words).unwrap(); + let dave = utils::home(&"dave".to_string()); + let sammy = utils::home(&"sammy".to_string()); - // Generate the extended key - let xkey: ExtendedKey = mnemonic.into_extended_key()?; + // Load mnemonics from disk + let dave_wallet = keys::bitcoin::load_from_file(&dave).unwrap(); + let sammy_wallet = keys::bitcoin::load_from_file(&sammy).unwrap(); - // Get private key from the extended key - let xprv = xkey.into_xprv(Network::Signet).unwrap(); + let dave_address = dave_wallet.get_address(bdk::wallet::AddressIndex::New)?; + println!("Dave address: {:?}", dave_address); - // Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1") - let wallet = Wallet::new( - Bip84(xprv, KeychainKind::External), - Some(Bip84(xprv, KeychainKind::Internal)), - Network::Signet, - MemoryDatabase::default(), - )?; + let sammy_address = sammy_wallet.get_address(bdk::wallet::AddressIndex::New)?; + println!("Sammy address: {:?}", sammy_address); - let address = wallet.get_address(bdk::wallet::AddressIndex::New)?; - println!("Generated Address: {:?}", address); + print!("Syncing..."); + let client = esplora_client::Builder::new("http://signet.bitcoindevkit.net").build_async()?; + + fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static { + let mut once = Some(()); + let mut stdout = std::io::stdout(); + move |spk_i, _| { + match once.take() { + Some(_) => print!("\nScanning keychain [{:?}]", kind), + None => print!(" {:<3}", spk_i), + }; + stdout.flush().expect("must flush"); + } + } + + // let request = dave_wallet + // .start_full_scan() + // .inspect_spks_for_all_keychains({ + // let mut once = BTreeSet::::new(); + // move |keychain, spk_i, _| { + // match once.insert(keychain) { + // true => print!("\nScanning keychain [{:?}]", keychain), + // false => print!(" {:<3}", spk_i), + // } + // std::io::stdout().flush().expect("must flush") + // } + // }) + // .inspect_spks_for_keychain( + // KeychainKind::External, + // generate_inspect(KeychainKind::External), + // ) + // .inspect_spks_for_keychain( + // KeychainKind::Internal, + // generate_inspect(KeychainKind::Internal), + // ); + + // let mut update = client + // .full_scan(request, STOP_GAP, PARALLEL_REQUESTS) + // .await?; + // let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + // let _ = update.graph_update.update_last_seen_unconfirmed(now); + + // wallet.apply_update(update)?; + // if let Some(changeset) = wallet.take_staged() { + // db.write(&changeset)?; + // } + + let dave_balance = dave_wallet.get_balance()?; + println!("Wallet balance before syncing: {} sats", dave_balance); + + let sammy_balance = sammy_wallet.get_balance()?; + println!("Wallet balance before syncing: {} sats", sammy_balance); Ok(()) } diff --git a/side-node/src/keys/bitcoin.rs b/side-node/src/keys/bitcoin.rs index 968752f..aaf0a95 100644 --- a/side-node/src/keys/bitcoin.rs +++ b/side-node/src/keys/bitcoin.rs @@ -1,40 +1,59 @@ -use bitcoin::secp256k1::{rand, Keypair, Secp256k1, SecretKey}; +use bdk::{ + bitcoin::Network, + database::MemoryDatabase, + keys::{ + bip39::{Language, Mnemonic, WordCount}, + DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey, + }, + miniscript, + template::Bip84, + KeychainKind, Wallet, +}; + use std::{ fs::{self, File}, io::Write, path::PathBuf, - str::FromStr, }; -pub fn make_keypair() -> bitcoin::secp256k1::Keypair { - let secp = Secp256k1::new(); - let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng()); - Keypair::from_secret_key(&secp, &secret_key) +pub fn make_mnemonic() -> String { + let mnemonic: GeneratedKey<_, miniscript::Segwitv0> = + Mnemonic::generate((WordCount::Words12, Language::English)).unwrap(); + mnemonic.to_string() } -pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { - let key_pair = make_keypair(); - let mut file = File::create(key_path)?; - - let pub_str = key_pair.public_key().to_string(); - let priv_str = key_pair.display_secret().to_string(); - println!("private key: {priv_str}"); - let out = format!("{pub_str}/{priv_str}"); - println!("out: {out}"); - file.write(out.as_bytes())?; +pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> { + let mnemonic = make_mnemonic(); + let mut file = File::create(mnemonic_path)?; + println!("mnemonic: {mnemonic}"); + file.write(mnemonic.as_bytes())?; Ok(()) } -pub(crate) fn load_from_file(side_dir: &PathBuf) -> bitcoin::secp256k1::Keypair { - let bitcoin_key_path = crate::utils::side_paths(side_dir.clone()).1; // TODO: this tuple stinks +pub fn create_wallet(mnemonic_words: String) -> anyhow::Result> { + let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); - let data = fs::read_to_string(bitcoin_key_path).expect("couldn't read bitcoin key file"); - println!("bitcoin keys: {:?}", data); + // Generate the extended key + let xkey: ExtendedKey = mnemonic.into_extended_key()?; - let secret_key_data = data.split("/").collect::>()[1]; - let secp = Secp256k1::new(); - let secret_key = - SecretKey::from_str(secret_key_data).expect("couldn't load secret key from file"); - Keypair::from_secret_key(&secp, &secret_key) + // Get private key from the extended key + let xprv = xkey.into_xprv(Network::Signet).unwrap(); + + // Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1") + let wallet = Wallet::new( + Bip84(xprv, KeychainKind::External), + Some(Bip84(xprv, KeychainKind::Internal)), + Network::Signet, + MemoryDatabase::default(), + )?; + + Ok(wallet) +} + +pub(crate) fn load_from_file(side_dir: &PathBuf) -> anyhow::Result> { + let mnemonic_path = crate::utils::side_paths(side_dir.clone()).1; // TODO: this tuple stinks + let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file"); + println!("Creating wallet from mnemonic: {mnemonic_words}"); + create_wallet(mnemonic_words) } diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 9397bbf..0426b25 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -42,7 +42,7 @@ async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); - let bitcoin_keys = keys::bitcoin::load_from_file(&side_dir); + let bitcoin_wallet = keys::bitcoin::load_from_file(&side_dir).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input @@ -57,7 +57,7 @@ async fn setup(name: &String) -> SideNode { let node = SideNode::new( crdt, bft_crdt_keys, - bitcoin_keys, + bitcoin_wallet, incoming_receiver, stdin_receiver, handle, diff --git a/side-node/src/node.rs b/side-node/src/node.rs index 882b4a0..5496691 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -1,3 +1,4 @@ +use bdk::database::MemoryDatabase; use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use fastcrypto::ed25519::Ed25519KeyPair; use tokio::sync::mpsc; @@ -7,7 +8,7 @@ use crate::{clients::websocket::Client, crdt::TransactionList, utils}; pub struct SideNode { crdt: BaseCrdt, bft_crdt_keys: fastcrypto::ed25519::Ed25519KeyPair, - bitcoin_keys: bitcoin::secp256k1::Keypair, + bitcoin_wallet: bdk::Wallet, incoming_receiver: mpsc::Receiver, stdin_receiver: std::sync::mpsc::Receiver, handle: ezsockets::Client, @@ -17,7 +18,7 @@ impl SideNode { pub fn new( crdt: BaseCrdt, bft_crdt_keys: Ed25519KeyPair, - bitcoin_keys: bitcoin::secp256k1::Keypair, + bitcoin_wallet: bdk::Wallet, incoming_receiver: mpsc::Receiver, stdin_receiver: std::sync::mpsc::Receiver, handle: ezsockets::Client, @@ -25,7 +26,7 @@ impl SideNode { let node = Self { crdt, bft_crdt_keys, - bitcoin_keys, + bitcoin_wallet, incoming_receiver, stdin_receiver, handle, diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index 951a1a9..b7478cf 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -35,7 +35,8 @@ async fn test_distribute_via_websockets() { async fn setup(_: &str) -> SideNode { // First, load up the keys and create a bft-crdt let bft_crdt_keys = make_keypair(); - let bitcoin_keys = keys::bitcoin::make_keypair(); + let mnemonic_words = keys::bitcoin::make_mnemonic(); + let bitcoin_wallet = keys::bitcoin::create_wallet(mnemonic_words).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input @@ -47,7 +48,7 @@ async fn setup(_: &str) -> SideNode { let node = SideNode::new( crdt, bft_crdt_keys, - bitcoin_keys, + bitcoin_wallet, incoming_receiver, stdin_receiver, handle, From 9e19500ab02fabe2360deb7ed200359b75a11ccb Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 13:56:43 +0100 Subject: [PATCH 22/55] Comment on wallet loader --- side-node/src/keys/bitcoin.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/side-node/src/keys/bitcoin.rs b/side-node/src/keys/bitcoin.rs index aaf0a95..0ea9878 100644 --- a/side-node/src/keys/bitcoin.rs +++ b/side-node/src/keys/bitcoin.rs @@ -31,6 +31,7 @@ pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> { Ok(()) } +/// Creates a Signet Bitcoin descriptor wallet from a mnemonic pub fn create_wallet(mnemonic_words: String) -> anyhow::Result> { let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); From 462590b82fb9d8c1ffd41e9e6d0bffc972761ad3 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 13:56:57 +0100 Subject: [PATCH 23/55] Simplified bdk client --- side-node/src/clients/btc_other_rpc.rs | 77 +++++++------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/side-node/src/clients/btc_other_rpc.rs b/side-node/src/clients/btc_other_rpc.rs index fe9ca56..04ae0ab 100644 --- a/side-node/src/clients/btc_other_rpc.rs +++ b/side-node/src/clients/btc_other_rpc.rs @@ -1,9 +1,5 @@ -use std::io::Write; - -use bdk::{bitcoin::Script, KeychainKind}; -use bdk_esplora::esplora_client; - use crate::{keys, utils}; +use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); @@ -13,64 +9,29 @@ pub async fn run() -> Result<(), anyhow::Error> { let dave_wallet = keys::bitcoin::load_from_file(&dave).unwrap(); let sammy_wallet = keys::bitcoin::load_from_file(&sammy).unwrap(); - let dave_address = dave_wallet.get_address(bdk::wallet::AddressIndex::New)?; - println!("Dave address: {:?}", dave_address); + // let dave_address = dave_wallet.get_address(bdk::wallet::AddressIndex::New)?; + // println!("Dave address: {:?}", dave_address); - let sammy_address = sammy_wallet.get_address(bdk::wallet::AddressIndex::New)?; - println!("Sammy address: {:?}", sammy_address); + // let sammy_address = sammy_wallet.get_address(bdk::wallet::AddressIndex::New)?; + // println!("Sammy address: {:?}", sammy_address); - print!("Syncing..."); - let client = esplora_client::Builder::new("http://signet.bitcoindevkit.net").build_async()?; + let blockchain = ElectrumBlockchain::from(electrum_client::Client::new( + "ssl://electrum.blockstream.info:60002", + )?); - fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static { - let mut once = Some(()); - let mut stdout = std::io::stdout(); - move |spk_i, _| { - match once.take() { - Some(_) => print!("\nScanning keychain [{:?}]", kind), - None => print!(" {:<3}", spk_i), - }; - stdout.flush().expect("must flush"); - } - } + // let dave_balance = dave_wallet.get_balance()?; + // println!("Wallet balance before syncing: {} sats", dave_balance); - // let request = dave_wallet - // .start_full_scan() - // .inspect_spks_for_all_keychains({ - // let mut once = BTreeSet::::new(); - // move |keychain, spk_i, _| { - // match once.insert(keychain) { - // true => print!("\nScanning keychain [{:?}]", keychain), - // false => print!(" {:<3}", spk_i), - // } - // std::io::stdout().flush().expect("must flush") - // } - // }) - // .inspect_spks_for_keychain( - // KeychainKind::External, - // generate_inspect(KeychainKind::External), - // ) - // .inspect_spks_for_keychain( - // KeychainKind::Internal, - // generate_inspect(KeychainKind::Internal), - // ); + // let sammy_balance = sammy_wallet.get_balance()?; + // println!("Wallet balance before syncing: {} sats", sammy_balance); - // let mut update = client - // .full_scan(request, STOP_GAP, PARALLEL_REQUESTS) - // .await?; - // let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - // let _ = update.graph_update.update_last_seen_unconfirmed(now); - - // wallet.apply_update(update)?; - // if let Some(changeset) = wallet.take_staged() { - // db.write(&changeset)?; - // } - - let dave_balance = dave_wallet.get_balance()?; - println!("Wallet balance before syncing: {} sats", dave_balance); - - let sammy_balance = sammy_wallet.get_balance()?; - println!("Wallet balance before syncing: {} sats", sammy_balance); + println!("Syncing..."); + dave_wallet.sync(&blockchain, SyncOptions::default())?; + println!( + "Wallet balance for {} after syncing: {:?} sats", + dave_wallet.get_address(bdk::wallet::AddressIndex::LastUnused)?, + dave_wallet.get_balance()? + ); Ok(()) } From 9e4d9a47629880f076854d54fb0bd82619fb15d4 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 15:57:31 +0100 Subject: [PATCH 24/55] Switching to Bitcoin Testnet as Signet does not appear to work with Electrum atm --- Cargo.lock | 248 ++++++++++++++++++++++++- side-node/Cargo.toml | 2 + side-node/src/clients/btc_other_rpc.rs | 70 +++++-- side-node/src/clients/mod.rs | 1 + side-node/src/clients/test.rs | 19 ++ side-node/src/keys/bitcoin.rs | 2 +- 6 files changed, 318 insertions(+), 24 deletions(-) create mode 100644 side-node/src/clients/test.rs diff --git a/Cargo.lock b/Cargo.lock index 54436c0..63dc326 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,33 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-lc-rs" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7d844e282b4b56750b2d4e893b2205581ded8709fddd2b6aa5418c150ca877" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a2c29203f6bf296d01141cc8bb9dbd5ecd4c27843f2ee0767bcd5985a927da" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + [[package]] name = "backtrace" version = "0.3.73" @@ -364,7 +391,7 @@ dependencies = [ "bdk-macros", "bip39", "bitcoin 0.30.2", - "electrum-client", + "electrum-client 0.18.0", "getrandom 0.2.15", "js-sys", "log", @@ -490,6 +517,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.67", + "which", +] + [[package]] name = "bip39" version = "2.0.0" @@ -679,6 +729,20 @@ name = "cc" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -726,6 +790,17 @@ dependencies = [ "half", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "3.2.25" @@ -787,6 +862,15 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.1" @@ -1108,6 +1192,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "dyn-clone" version = "1.0.17" @@ -1160,7 +1250,7 @@ dependencies = [ "byteorder", "libc", "log", - "rustls", + "rustls 0.21.12", "serde", "serde_json", "webpki", @@ -1168,6 +1258,23 @@ dependencies = [ "winapi", ] +[[package]] +name = "electrum-client" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7b1f8783238bb18e6e137875b0a66f3dffe6c7ea84066e05d033cf180b150f" +dependencies = [ + "bitcoin 0.32.2", + "byteorder", + "libc", + "log", + "rustls 0.23.10", + "serde", + "serde_json", + "webpki-roots 0.25.4", + "winapi", +] + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -1415,6 +1522,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.30" @@ -1700,6 +1813,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -1874,6 +1996,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -1901,12 +2032,28 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libloading" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + [[package]] name = "libm" version = "0.2.8" @@ -1968,6 +2115,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniscript" version = "10.0.0" @@ -2008,8 +2161,8 @@ dependencies = [ "base64 0.12.3", "log", "once_cell", - "rustls", - "rustls-webpki", + "rustls 0.21.12", + "rustls-webpki 0.101.7", "serde", "serde_json", "webpki-roots 0.25.4", @@ -2026,6 +2179,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + [[package]] name = "native-tls" version = "0.2.12" @@ -2043,6 +2202,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2395,6 +2564,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.67", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -2718,6 +2897,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -2748,10 +2933,25 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2761,6 +2961,12 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2771,6 +2977,18 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.18" @@ -3109,6 +3327,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "side-node" version = "0.1.0" @@ -3124,9 +3348,11 @@ dependencies = [ "bitcoin 0.32.2", "clap 4.5.7", "dirs", + "electrum-client 0.20.0", "ezsockets", "fastcrypto", "indexmap 2.2.6", + "reqwest", "serde", "serde_json", "sha256", @@ -3906,6 +4132,18 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index dd89008..8f0254a 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -17,9 +17,11 @@ bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" } bitcoin = { version = "0.32.2", features = ["rand"] } clap = { version = "4.5.4", features = ["derive"] } dirs = "5.0.1" +electrum-client = "0.20" ezsockets = { version = "*", features = ["client"] } fastcrypto = "0.1.8" indexmap = { version = "2.2.6", features = ["serde"] } +reqwest = { version = "*", features = ["blocking"] } # serde_cbor = "0.11.2" # move to this once we need to pack things in CBOR serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.117" diff --git a/side-node/src/clients/btc_other_rpc.rs b/side-node/src/clients/btc_other_rpc.rs index 04ae0ab..9390ac0 100644 --- a/side-node/src/clients/btc_other_rpc.rs +++ b/side-node/src/clients/btc_other_rpc.rs @@ -1,37 +1,71 @@ use crate::{keys, utils}; + +use bdk::bitcoin::base64; +use bdk::bitcoin::psbt::PartiallySignedTransaction; +use bdk::blockchain::Progress; +use bdk::database::MemoryDatabase; +use bdk::wallet::AddressIndex::{self, New}; use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; +use bdk::{FeeRate, SignOptions, TransactionDetails, Wallet}; + +#[derive(Debug)] +pub struct CustomProgress; + +impl Progress for CustomProgress { + fn update(&self, progress: f32, message: Option) -> Result<(), bdk::Error> { + println!("Progress: {} - {:?}", progress, message); + Ok(()) + } +} pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); - let sammy = utils::home(&"sammy".to_string()); - - // Load mnemonics from disk let dave_wallet = keys::bitcoin::load_from_file(&dave).unwrap(); - let sammy_wallet = keys::bitcoin::load_from_file(&sammy).unwrap(); - // let dave_address = dave_wallet.get_address(bdk::wallet::AddressIndex::New)?; - // println!("Dave address: {:?}", dave_address); - - // let sammy_address = sammy_wallet.get_address(bdk::wallet::AddressIndex::New)?; - // println!("Sammy address: {:?}", sammy_address); + // Verify explicitly that the address is derived as expected + let expected_address = "tb1q88e45m4akrg2y00n4077vn90q8qw0svz7dss6k"; + let derived_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string(); + assert_eq!(expected_address, derived_address); let blockchain = ElectrumBlockchain::from(electrum_client::Client::new( "ssl://electrum.blockstream.info:60002", )?); - // let dave_balance = dave_wallet.get_balance()?; - // println!("Wallet balance before syncing: {} sats", dave_balance); - - // let sammy_balance = sammy_wallet.get_balance()?; - // println!("Wallet balance before syncing: {} sats", sammy_balance); - println!("Syncing..."); dave_wallet.sync(&blockchain, SyncOptions::default())?; + println!( - "Wallet balance for {} after syncing: {:?} sats", - dave_wallet.get_address(bdk::wallet::AddressIndex::LastUnused)?, - dave_wallet.get_balance()? + "Wallet balance for {} after syncing: {:?} sats on network {}", + dave_wallet.get_address(bdk::wallet::AddressIndex::Peek(0))?, + dave_wallet.get_balance()?, + dave_wallet.network() ); + let transactions = dave_wallet.list_transactions(false)?; + println!("Transaction History: {:?}", transactions); + + let (mut psbt, details) = build_sending_tx(&dave_wallet).expect("psbt fukked"); + + println!("Transaction details: {:#?}", details); + println!("Unsigned PSBT: {}", base64::encode(psbt.serialize())); + // let mut psbt = Psbt::deserialize(&base64::decode(psbt).unwrap())?; + let _finalized = dave_wallet.sign(&mut psbt, SignOptions::default())?; + + println!("Finalized: {}", _finalized); + Ok(()) } + +fn build_sending_tx( + wallet: &Wallet, +) -> anyhow::Result<(PartiallySignedTransaction, TransactionDetails), anyhow::Error> { + let send_to = wallet.get_address(New)?; + + let mut builder = wallet.build_tx(); + builder + .add_recipient(send_to.script_pubkey(), 500) + .enable_rbf() + .do_not_spend_change() + .fee_rate(FeeRate::from_sat_per_vb(5.0)); + Ok(builder.finish()?) +} diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs index c2f3427..e5cc7ba 100644 --- a/side-node/src/clients/mod.rs +++ b/side-node/src/clients/mod.rs @@ -1,3 +1,4 @@ pub mod btc_other_rpc; pub mod btc_rpc; +pub mod test; pub mod websocket; diff --git a/side-node/src/clients/test.rs b/side-node/src/clients/test.rs new file mode 100644 index 0000000..3f46762 --- /dev/null +++ b/side-node/src/clients/test.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use reqwest::blocking::Client; + +pub async fn run() -> Result<(), Box> { + // Set up the Esplora client + let esplora_url = "https://blockstream.info/signet/api"; + let client = Client::new(); + + // Address to check + let address = "tb1q88e45m4akrg2y00n4077vn90q8qw0svz7dss6k"; + + // Query balance + let url = format!("{}/address/{}/utxo", esplora_url, address); + let response = client.get(&url).send()?.text()?; + + println!("UTXO for address: {}", response); + + Ok(()) +} diff --git a/side-node/src/keys/bitcoin.rs b/side-node/src/keys/bitcoin.rs index 0ea9878..e5b9ee2 100644 --- a/side-node/src/keys/bitcoin.rs +++ b/side-node/src/keys/bitcoin.rs @@ -45,7 +45,7 @@ pub fn create_wallet(mnemonic_words: String) -> anyhow::Result Date: Mon, 24 Jun 2024 16:31:03 +0100 Subject: [PATCH 25/55] What an odyssey! Bitcoin sends now work. --- side-node/src/clients/btc_other_rpc.rs | 60 +++++++++++++++++--------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/side-node/src/clients/btc_other_rpc.rs b/side-node/src/clients/btc_other_rpc.rs index 9390ac0..4e3efd2 100644 --- a/side-node/src/clients/btc_other_rpc.rs +++ b/side-node/src/clients/btc_other_rpc.rs @@ -1,12 +1,15 @@ use crate::{keys, utils}; use bdk::bitcoin::base64; -use bdk::bitcoin::psbt::PartiallySignedTransaction; -use bdk::blockchain::Progress; +use bdk::bitcoin::psbt::{PartiallySignedTransaction, Psbt}; +use bdk::blockchain::{Blockchain, Progress}; use bdk::database::MemoryDatabase; use bdk::wallet::AddressIndex::{self, New}; +use bdk::wallet::AddressInfo; use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; use bdk::{FeeRate, SignOptions, TransactionDetails, Wallet}; +use toml::to_string; +use tracing::field::display; #[derive(Debug)] pub struct CustomProgress; @@ -20,12 +23,20 @@ impl Progress for CustomProgress { pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); + let sammy = utils::home(&"sammy".to_string()); let dave_wallet = keys::bitcoin::load_from_file(&dave).unwrap(); + let sammy_wallet = keys::bitcoin::load_from_file(&sammy).unwrap(); // Verify explicitly that the address is derived as expected - let expected_address = "tb1q88e45m4akrg2y00n4077vn90q8qw0svz7dss6k"; + let dave_expected_address = "tb1q88e45m4akrg2y00n4077vn90q8qw0svz7dss6k"; let derived_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string(); - assert_eq!(expected_address, derived_address); + assert_eq!(dave_expected_address, derived_address); + + let dave_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string(); + let sammy_address = sammy_wallet.get_address(AddressIndex::Peek(0))?.to_string(); + + println!("Dave's address: {}", dave_address); + println!("Sammy's address: {}", sammy_address); let blockchain = ElectrumBlockchain::from(electrum_client::Client::new( "ssl://electrum.blockstream.info:60002", @@ -34,38 +45,45 @@ pub async fn run() -> Result<(), anyhow::Error> { println!("Syncing..."); dave_wallet.sync(&blockchain, SyncOptions::default())?; - println!( - "Wallet balance for {} after syncing: {:?} sats on network {}", - dave_wallet.get_address(bdk::wallet::AddressIndex::Peek(0))?, - dave_wallet.get_balance()?, - dave_wallet.network() - ); + display_balance(&dave_wallet); + display_balance(&sammy_wallet); - let transactions = dave_wallet.list_transactions(false)?; - println!("Transaction History: {:?}", transactions); - - let (mut psbt, details) = build_sending_tx(&dave_wallet).expect("psbt fukked"); + let (mut psbt, details) = + build_sending_tx(&dave_wallet, sammy_wallet.get_address(New)?).expect("psbt build error"); println!("Transaction details: {:#?}", details); println!("Unsigned PSBT: {}", base64::encode(psbt.serialize())); - // let mut psbt = Psbt::deserialize(&base64::decode(psbt).unwrap())?; - let _finalized = dave_wallet.sign(&mut psbt, SignOptions::default())?; - println!("Finalized: {}", _finalized); + let finalized = dave_wallet.sign(&mut psbt, SignOptions::default())?; + println!("Finalized: {}", finalized); + let tx_id = blockchain + .broadcast(&psbt.extract_tx()) + .expect("broadcast error"); + println!("Transaction ID: {:?}", tx_id); Ok(()) } +fn display_balance(wallet: &Wallet) { + println!( + "Wallet balance for {} after syncing: {:?} sats on network {}", + wallet + .get_address(bdk::wallet::AddressIndex::Peek(0)) + .expect("couldn't get address"), + wallet.get_balance().expect("couldn't show balance"), + wallet.network(), + ); +} + fn build_sending_tx( wallet: &Wallet, + recipient: AddressInfo, ) -> anyhow::Result<(PartiallySignedTransaction, TransactionDetails), anyhow::Error> { - let send_to = wallet.get_address(New)?; - let mut builder = wallet.build_tx(); builder - .add_recipient(send_to.script_pubkey(), 500) + .add_recipient(recipient.script_pubkey(), 1000) .enable_rbf() .do_not_spend_change() - .fee_rate(FeeRate::from_sat_per_vb(5.0)); + .fee_rate(FeeRate::from_sat_per_vb(7.0)); Ok(builder.finish()?) } From 5c03a77e56fbb67238f4dd9431cb48de60272388 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 16:43:17 +0100 Subject: [PATCH 26/55] Bit of cleanup after all the excitement --- ...tc_other_rpc.rs => btc_electrum_client.rs} | 36 +++++-------------- .../{btc_rpc.rs => btc_esplora_client.rs} | 1 + side-node/src/clients/mod.rs | 5 ++- side-node/src/clients/test.rs | 19 ---------- side-node/src/lib.rs | 2 +- 5 files changed, 12 insertions(+), 51 deletions(-) rename side-node/src/clients/{btc_other_rpc.rs => btc_electrum_client.rs} (64%) rename side-node/src/clients/{btc_rpc.rs => btc_esplora_client.rs} (98%) delete mode 100644 side-node/src/clients/test.rs diff --git a/side-node/src/clients/btc_other_rpc.rs b/side-node/src/clients/btc_electrum_client.rs similarity index 64% rename from side-node/src/clients/btc_other_rpc.rs rename to side-node/src/clients/btc_electrum_client.rs index 4e3efd2..faa14f8 100644 --- a/side-node/src/clients/btc_other_rpc.rs +++ b/side-node/src/clients/btc_electrum_client.rs @@ -1,25 +1,12 @@ use crate::{keys, utils}; -use bdk::bitcoin::base64; -use bdk::bitcoin::psbt::{PartiallySignedTransaction, Psbt}; -use bdk::blockchain::{Blockchain, Progress}; +use bdk::bitcoin::psbt::PartiallySignedTransaction; +use bdk::blockchain::Blockchain; use bdk::database::MemoryDatabase; use bdk::wallet::AddressIndex::{self, New}; use bdk::wallet::AddressInfo; use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; use bdk::{FeeRate, SignOptions, TransactionDetails, Wallet}; -use toml::to_string; -use tracing::field::display; - -#[derive(Debug)] -pub struct CustomProgress; - -impl Progress for CustomProgress { - fn update(&self, progress: f32, message: Option) -> Result<(), bdk::Error> { - println!("Progress: {} - {:?}", progress, message); - Ok(()) - } -} pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); @@ -27,11 +14,6 @@ pub async fn run() -> Result<(), anyhow::Error> { let dave_wallet = keys::bitcoin::load_from_file(&dave).unwrap(); let sammy_wallet = keys::bitcoin::load_from_file(&sammy).unwrap(); - // Verify explicitly that the address is derived as expected - let dave_expected_address = "tb1q88e45m4akrg2y00n4077vn90q8qw0svz7dss6k"; - let derived_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string(); - assert_eq!(dave_expected_address, derived_address); - let dave_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string(); let sammy_address = sammy_wallet.get_address(AddressIndex::Peek(0))?.to_string(); @@ -51,16 +33,14 @@ pub async fn run() -> Result<(), anyhow::Error> { let (mut psbt, details) = build_sending_tx(&dave_wallet, sammy_wallet.get_address(New)?).expect("psbt build error"); - println!("Transaction details: {:#?}", details); - println!("Unsigned PSBT: {}", base64::encode(psbt.serialize())); + println!("About to sign the transaction: {:?}", details); - let finalized = dave_wallet.sign(&mut psbt, SignOptions::default())?; - println!("Finalized: {}", finalized); + dave_wallet.sign(&mut psbt, SignOptions::default())?; + let signed_tx = psbt.extract_tx(); - let tx_id = blockchain - .broadcast(&psbt.extract_tx()) - .expect("broadcast error"); - println!("Transaction ID: {:?}", tx_id); + println!("Broadcasting..."); + blockchain.broadcast(&signed_tx).expect("broadcast error"); + println!("Transaction ID: {:?}", signed_tx.txid()); Ok(()) } diff --git a/side-node/src/clients/btc_rpc.rs b/side-node/src/clients/btc_esplora_client.rs similarity index 98% rename from side-node/src/clients/btc_rpc.rs rename to side-node/src/clients/btc_esplora_client.rs index 10bc80d..880231b 100644 --- a/side-node/src/clients/btc_rpc.rs +++ b/side-node/src/clients/btc_esplora_client.rs @@ -12,6 +12,7 @@ const SEND_AMOUNT: Amount = Amount::from_sat(5000); const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 5; +/// Demonstrates the use of bdk with the Esplora client. pub async fn run() -> Result<(), anyhow::Error> { let db_path = "/tmp/bdk-esplora-async-example.sqlite"; let conn = Connection::open(db_path)?; diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs index e5cc7ba..2c213cd 100644 --- a/side-node/src/clients/mod.rs +++ b/side-node/src/clients/mod.rs @@ -1,4 +1,3 @@ -pub mod btc_other_rpc; -pub mod btc_rpc; -pub mod test; +pub mod btc_electrum_client; +pub mod btc_esplora_client; pub mod websocket; diff --git a/side-node/src/clients/test.rs b/side-node/src/clients/test.rs deleted file mode 100644 index 3f46762..0000000 --- a/side-node/src/clients/test.rs +++ /dev/null @@ -1,19 +0,0 @@ -use anyhow::Result; -use reqwest::blocking::Client; - -pub async fn run() -> Result<(), Box> { - // Set up the Esplora client - let esplora_url = "https://blockstream.info/signet/api"; - let client = Client::new(); - - // Address to check - let address = "tb1q88e45m4akrg2y00n4077vn90q8qw0svz7dss6k"; - - // Query balance - let url = format!("{}/address/{}/utxo", esplora_url, address); - let response = client.get(&url).send()?.text()?; - - println!("UTXO for address: {}", response); - - Ok(()) -} diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 0426b25..dea02c9 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -31,7 +31,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - let _ = clients::btc_other_rpc::run().await; + let _ = clients::btc_electrum_client::run().await; } None => println!("No command provided. Exiting. See --help for more information."), } From 117915bded38e31a69f286fd107ed63b2feec188 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 17:20:52 +0100 Subject: [PATCH 27/55] Splitting key load / wallet creation so we can use keys in esplora client --- side-node/src/clients/btc_electrum_client.rs | 35 ++++++++++++++++---- side-node/src/keys/bitcoin.rs | 26 +++++++-------- side-node/src/lib.rs | 3 +- side-node/tests/side_node.rs | 11 ++++-- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/side-node/src/clients/btc_electrum_client.rs b/side-node/src/clients/btc_electrum_client.rs index faa14f8..c18541a 100644 --- a/side-node/src/clients/btc_electrum_client.rs +++ b/side-node/src/clients/btc_electrum_client.rs @@ -1,8 +1,10 @@ use crate::{keys, utils}; +use bdk::bitcoin::bip32::ExtendedPrivKey; use bdk::bitcoin::psbt::PartiallySignedTransaction; -use bdk::blockchain::Blockchain; +use bdk::bitcoin::Network; use bdk::database::MemoryDatabase; +use bdk::template::Bip84; use bdk::wallet::AddressIndex::{self, New}; use bdk::wallet::AddressInfo; use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; @@ -11,8 +13,11 @@ use bdk::{FeeRate, SignOptions, TransactionDetails, Wallet}; pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); let sammy = utils::home(&"sammy".to_string()); - let dave_wallet = keys::bitcoin::load_from_file(&dave).unwrap(); - let sammy_wallet = keys::bitcoin::load_from_file(&sammy).unwrap(); + let dave_keys = keys::bitcoin::load_from_file(&dave).unwrap(); + let sammy_keys = keys::bitcoin::load_from_file(&sammy).unwrap(); + + let dave_wallet = create_wallet(dave_keys)?; + let sammy_wallet = create_wallet(sammy_keys)?; let dave_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string(); let sammy_address = sammy_wallet.get_address(AddressIndex::Peek(0))?.to_string(); @@ -36,14 +41,30 @@ pub async fn run() -> Result<(), anyhow::Error> { println!("About to sign the transaction: {:?}", details); dave_wallet.sign(&mut psbt, SignOptions::default())?; - let signed_tx = psbt.extract_tx(); + let _signed_tx = psbt.extract_tx(); - println!("Broadcasting..."); - blockchain.broadcast(&signed_tx).expect("broadcast error"); - println!("Transaction ID: {:?}", signed_tx.txid()); + // println!("Broadcasting..."); + // blockchain.broadcast(&signed_tx).expect("broadcast error"); + // println!("Transaction ID: {:?}", signed_tx.txid()); Ok(()) } +/// Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1") +pub fn create_wallet( + keys: (Bip84, Option>), +) -> anyhow::Result> { + let (external_descriptor, internal_descriptor) = keys; + + let wallet = Wallet::new( + external_descriptor, + internal_descriptor, + Network::Testnet, + MemoryDatabase::default(), + )?; + + Ok(wallet) +} + fn display_balance(wallet: &Wallet) { println!( "Wallet balance for {} after syncing: {:?} sats on network {}", diff --git a/side-node/src/keys/bitcoin.rs b/side-node/src/keys/bitcoin.rs index e5b9ee2..8b33719 100644 --- a/side-node/src/keys/bitcoin.rs +++ b/side-node/src/keys/bitcoin.rs @@ -1,13 +1,12 @@ use bdk::{ - bitcoin::Network, - database::MemoryDatabase, + bitcoin::{bip32::ExtendedPrivKey, Network}, keys::{ bip39::{Language, Mnemonic, WordCount}, DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey, }, miniscript, template::Bip84, - KeychainKind, Wallet, + KeychainKind, }; use std::{ @@ -32,7 +31,9 @@ pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> { } /// Creates a Signet Bitcoin descriptor wallet from a mnemonic -pub fn create_wallet(mnemonic_words: String) -> anyhow::Result> { +pub fn get( + mnemonic_words: String, +) -> anyhow::Result<(Bip84, Option>)> { let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); // Generate the extended key @@ -41,20 +42,17 @@ pub fn create_wallet(mnemonic_words: String) -> anyhow::Result anyhow::Result> { +pub(crate) fn load_from_file( + side_dir: &PathBuf, +) -> anyhow::Result<(Bip84, Option>)> { let mnemonic_path = crate::utils::side_paths(side_dir.clone()).1; // TODO: this tuple stinks let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file"); println!("Creating wallet from mnemonic: {mnemonic_words}"); - create_wallet(mnemonic_words) + get(mnemonic_words) } diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index dea02c9..dba3920 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -42,7 +42,8 @@ async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); - let bitcoin_wallet = keys::bitcoin::load_from_file(&side_dir).unwrap(); + let bitcoin_keys = keys::bitcoin::load_from_file(&side_dir).unwrap(); + let bitcoin_wallet = clients::btc_electrum_client::create_wallet(bitcoin_keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index b7478cf..a4547da 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -2,7 +2,13 @@ use bft_json_crdt::{ json_crdt::{BaseCrdt, SignedOp}, keypair::make_keypair, }; -use side_node::{clients::websocket::Client, crdt::TransactionList, keys, node::SideNode, utils}; +use side_node::{ + clients::{btc_electrum_client, websocket::Client}, + crdt::TransactionList, + keys, + node::SideNode, + utils, +}; use tokio::sync::mpsc; #[tokio::test] @@ -36,7 +42,8 @@ async fn setup(_: &str) -> SideNode { // First, load up the keys and create a bft-crdt let bft_crdt_keys = make_keypair(); let mnemonic_words = keys::bitcoin::make_mnemonic(); - let bitcoin_wallet = keys::bitcoin::create_wallet(mnemonic_words).unwrap(); + let bitcoin_keys = keys::bitcoin::get(mnemonic_words).unwrap(); + let bitcoin_wallet = btc_electrum_client::create_wallet(bitcoin_keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input From a516de4bcb06eb2e693170fb9eea3053fd4f8960 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 18:41:55 +0100 Subject: [PATCH 28/55] Esplora client now working with persisted mnemonics --- side-node/src/clients/btc_electrum_client.rs | 24 +++++++------ side-node/src/clients/btc_esplora_client.rs | 38 ++++++++++++++++---- side-node/src/keys/bitcoin.rs | 28 ++++++--------- side-node/src/lib.rs | 2 +- 4 files changed, 55 insertions(+), 37 deletions(-) diff --git a/side-node/src/clients/btc_electrum_client.rs b/side-node/src/clients/btc_electrum_client.rs index c18541a..ddb1271 100644 --- a/side-node/src/clients/btc_electrum_client.rs +++ b/side-node/src/clients/btc_electrum_client.rs @@ -1,23 +1,22 @@ use crate::{keys, utils}; - -use bdk::bitcoin::bip32::ExtendedPrivKey; use bdk::bitcoin::psbt::PartiallySignedTransaction; use bdk::bitcoin::Network; use bdk::database::MemoryDatabase; +use bdk::keys::ExtendedKey; use bdk::template::Bip84; use bdk::wallet::AddressIndex::{self, New}; use bdk::wallet::AddressInfo; use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; -use bdk::{FeeRate, SignOptions, TransactionDetails, Wallet}; +use bdk::{FeeRate, KeychainKind, SignOptions, TransactionDetails, Wallet}; pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); let sammy = utils::home(&"sammy".to_string()); - let dave_keys = keys::bitcoin::load_from_file(&dave).unwrap(); - let sammy_keys = keys::bitcoin::load_from_file(&sammy).unwrap(); + let dave_key = keys::bitcoin::load_from_file(&dave).unwrap(); + let sammy_key = keys::bitcoin::load_from_file(&sammy).unwrap(); - let dave_wallet = create_wallet(dave_keys)?; - let sammy_wallet = create_wallet(sammy_keys)?; + let dave_wallet = create_wallet(dave_key)?; + let sammy_wallet = create_wallet(sammy_key)?; let dave_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string(); let sammy_address = sammy_wallet.get_address(AddressIndex::Peek(0))?.to_string(); @@ -50,10 +49,13 @@ pub async fn run() -> Result<(), anyhow::Error> { } /// Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1") -pub fn create_wallet( - keys: (Bip84, Option>), -) -> anyhow::Result> { - let (external_descriptor, internal_descriptor) = keys; +pub fn create_wallet(xkey: ExtendedKey) -> anyhow::Result> { + let xprv = xkey + .into_xprv(Network::Testnet) + .expect("couldn't turn xkey into xprv"); + + let external_descriptor = Bip84(xprv, KeychainKind::External); + let internal_descriptor = Some(Bip84(xprv, KeychainKind::Internal)); let wallet = Wallet::new( external_descriptor, diff --git a/side-node/src/clients/btc_esplora_client.rs b/side-node/src/clients/btc_esplora_client.rs index 880231b..b14aa12 100644 --- a/side-node/src/clients/btc_esplora_client.rs +++ b/side-node/src/clients/btc_esplora_client.rs @@ -1,32 +1,56 @@ -use std::{collections::BTreeSet, io::Write, str::FromStr}; +use std::{collections::BTreeSet, fs, io::Write, str::FromStr}; +use bdk::keys::bip39::Mnemonic; use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_wallet::{ bitcoin::{Address, Amount, Network, Script}, + keys::{DerivableKey, ExtendedKey}, KeychainKind, SignOptions, Wallet, }; use bdk_sqlite::{rusqlite::Connection, Store}; +use crate::{keys, utils}; + const SEND_AMOUNT: Amount = Amount::from_sat(5000); const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 5; /// Demonstrates the use of bdk with the Esplora client. pub async fn run() -> Result<(), anyhow::Error> { + let dave_dir = utils::home(&"dave".to_string()); + + let mnemonic_path = crate::utils::side_paths(dave_dir).1; // TODO: this tuple stinks + let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file"); + println!("Creating wallet from mnemonic: {mnemonic_words}"); + let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); + + // Generate the extended key + let xkey: ExtendedKey = mnemonic + .into_extended_key() + .expect("couldn't turn mnemonic into xkey"); + + let xprv = xkey + .into_xprv(Network::Signet) + .expect("problem converting xkey to xprv") + .to_string(); + + println!("Setting up esplora database"); + let db_path = "/tmp/bdk-esplora-async-example.sqlite"; let conn = Connection::open(db_path)?; let mut db = Store::new(conn)?; - let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; - let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; - let changeset = db.read()?; + let external_descriptor = format!("wpkh({xprv}/84'/1'/0'/0/*)"); + let internal_descriptor = format!("wpkh({xprv}/84'/1'/0'/1/*)"); + let changeset = db.read().expect("couldn't read esplora database"); let mut wallet = Wallet::new_or_load( - external_descriptor, - internal_descriptor, + &external_descriptor, + &internal_descriptor, changeset, Network::Signet, - )?; + ) + .expect("problem setting up wallet"); let address = wallet.next_unused_address(KeychainKind::External); if let Some(changeset) = wallet.take_staged() { diff --git a/side-node/src/keys/bitcoin.rs b/side-node/src/keys/bitcoin.rs index 8b33719..920e942 100644 --- a/side-node/src/keys/bitcoin.rs +++ b/side-node/src/keys/bitcoin.rs @@ -1,12 +1,9 @@ use bdk::{ - bitcoin::{bip32::ExtendedPrivKey, Network}, keys::{ bip39::{Language, Mnemonic, WordCount}, DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey, }, miniscript, - template::Bip84, - KeychainKind, }; use std::{ @@ -21,6 +18,9 @@ pub fn make_mnemonic() -> String { mnemonic.to_string() } +/// Write the mnemonic to a file in the node's side directory +/// +/// TODO: obviously spitting the mnemonic out to the console is not for production pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> { let mnemonic = make_mnemonic(); let mut file = File::create(mnemonic_path)?; @@ -30,27 +30,19 @@ pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> { Ok(()) } -/// Creates a Signet Bitcoin descriptor wallet from a mnemonic -pub fn get( - mnemonic_words: String, -) -> anyhow::Result<(Bip84, Option>)> { +/// Creates Signet Bitcoin descriptors from a mnemonic +pub fn get(mnemonic_words: String) -> anyhow::Result { let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); // Generate the extended key - let xkey: ExtendedKey = mnemonic.into_extended_key()?; + let xkey: ExtendedKey = mnemonic + .into_extended_key() + .expect("couldn't turn mnemonic into xkey"); - // Get private key from the extended key - let xprv = xkey.into_xprv(Network::Signet).unwrap(); - - let external_descriptor = Bip84(xprv, KeychainKind::External); - let internal_descriptor = Some(Bip84(xprv, KeychainKind::Internal)); - - Ok((external_descriptor, internal_descriptor)) + Ok(xkey) } -pub(crate) fn load_from_file( - side_dir: &PathBuf, -) -> anyhow::Result<(Bip84, Option>)> { +pub(crate) fn load_from_file(side_dir: &PathBuf) -> anyhow::Result { let mnemonic_path = crate::utils::side_paths(side_dir.clone()).1; // TODO: this tuple stinks let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file"); println!("Creating wallet from mnemonic: {mnemonic_words}"); diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index dba3920..f8ac9f2 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -31,7 +31,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - let _ = clients::btc_electrum_client::run().await; + let _ = clients::btc_esplora_client::run().await; } None => println!("No command provided. Exiting. See --help for more information."), } From 0ad430a9f1596c9d18e393d00c5ba765e12f33eb Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 18:47:03 +0100 Subject: [PATCH 29/55] Switching to Mutiny Net for 30 second block times. --- side-node/src/clients/btc_esplora_client.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/side-node/src/clients/btc_esplora_client.rs b/side-node/src/clients/btc_esplora_client.rs index b14aa12..7d53e66 100644 --- a/side-node/src/clients/btc_esplora_client.rs +++ b/side-node/src/clients/btc_esplora_client.rs @@ -22,6 +22,7 @@ pub async fn run() -> Result<(), anyhow::Error> { let mnemonic_path = crate::utils::side_paths(dave_dir).1; // TODO: this tuple stinks let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file"); + println!("Creating wallet from mnemonic: {mnemonic_words}"); let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); @@ -62,7 +63,9 @@ pub async fn run() -> Result<(), anyhow::Error> { println!("Wallet balance before syncing: {} sats", balance.total()); print!("Syncing..."); - let client = esplora_client::Builder::new("http://signet.bitcoindevkit.net").build_async()?; + let client = esplora_client::Builder::new("https://mutinynet.com/api") + .build_async() + .expect("couldn't build esplora client"); fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static { let mut once = Some(()); From b08e69ab1b34b2b1ea821bed419dd081ef02ebb3 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 24 Jun 2024 18:47:27 +0100 Subject: [PATCH 30/55] cleanup --- side-node/src/clients/btc_esplora_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/side-node/src/clients/btc_esplora_client.rs b/side-node/src/clients/btc_esplora_client.rs index 7d53e66..971f633 100644 --- a/side-node/src/clients/btc_esplora_client.rs +++ b/side-node/src/clients/btc_esplora_client.rs @@ -10,7 +10,7 @@ use bdk_wallet::{ use bdk_sqlite::{rusqlite::Connection, Store}; -use crate::{keys, utils}; +use crate::utils; const SEND_AMOUNT: Amount = Amount::from_sat(5000); const STOP_GAP: usize = 50; From bedbd54fae9223e28d79e1f9b828d5abf7d89339 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:07:07 +0100 Subject: [PATCH 31/55] Some docs as to current problems with the bare `bdk` wallet crate. --- side-node/src/clients/btc_electrum_client.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/side-node/src/clients/btc_electrum_client.rs b/side-node/src/clients/btc_electrum_client.rs index ddb1271..52167c2 100644 --- a/side-node/src/clients/btc_electrum_client.rs +++ b/side-node/src/clients/btc_electrum_client.rs @@ -9,6 +9,15 @@ use bdk::wallet::AddressInfo; use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; use bdk::{FeeRate, KeychainKind, SignOptions, TransactionDetails, Wallet}; +/// Run the Bitcoin wallet example +/// +/// This is a bdk example that uses the Electrum client to interact with the Bitcoin network. +/// Electrum is a light client that connects to a server to get information about the Bitcoin network. +/// The BDK itself does not have the ability to connect to e.g. esplora servers. As the Blockstream Electrum Signet +/// server does not appear to be picking up transactions properly at the moment, I've shifted over to using +/// the (more complex) `bdk_wallet` crate and the esplora client there (see the other RPC client). +/// +/// Note:the types below are all completely different than the types in `bdk_wallet`. pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); let sammy = utils::home(&"sammy".to_string()); From d4809a48e60b6b8da120aeb2f9ccb77e6a734a48 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:08:37 +0100 Subject: [PATCH 32/55] Docs on the bdk_wallet esplora client. --- side-node/src/clients/btc_esplora_client.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/side-node/src/clients/btc_esplora_client.rs b/side-node/src/clients/btc_esplora_client.rs index 971f633..3bb329d 100644 --- a/side-node/src/clients/btc_esplora_client.rs +++ b/side-node/src/clients/btc_esplora_client.rs @@ -17,6 +17,11 @@ const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 5; /// Demonstrates the use of bdk with the Esplora client. +/// +/// This is more complex than the bare `bdk` crate, but the esplora client works. +/// +/// Also, it very nadily works with the mutinynet.com esplora server, which is configured +/// with 30 second block times. pub async fn run() -> Result<(), anyhow::Error> { let dave_dir = utils::home(&"dave".to_string()); From 037fc27b7b59cc9b55d77d1524462f655ec5bf39 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:31:02 +0100 Subject: [PATCH 33/55] Starting a refactor into more functional modules --- .idea/.gitignore | 5 +++++ .idea/modules.xml | 8 ++++++++ .idea/side.iml | 17 +++++++++++++++++ .idea/vcs.xml | 6 ++++++ .../bitcoin.rs => bitcoin/bitcoin_keys.rs} | 0 .../clients/btc_electrum_client.rs | 6 +++--- .../{ => bitcoin}/clients/btc_esplora_client.rs | 0 side-node/src/bitcoin/clients/mod.rs | 2 ++ side-node/src/bitcoin/mod.rs | 3 +++ side-node/src/clients/mod.rs | 2 -- side-node/src/init/mod.rs | 4 ++-- side-node/src/keys/mod.rs | 1 - side-node/src/lib.rs | 9 +++++---- side-node/tests/side_node.rs | 12 +++--------- 14 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/modules.xml create mode 100644 .idea/side.iml create mode 100644 .idea/vcs.xml rename side-node/src/{keys/bitcoin.rs => bitcoin/bitcoin_keys.rs} (100%) rename side-node/src/{ => bitcoin}/clients/btc_electrum_client.rs (95%) rename side-node/src/{ => bitcoin}/clients/btc_esplora_client.rs (100%) create mode 100644 side-node/src/bitcoin/clients/mod.rs create mode 100644 side-node/src/bitcoin/mod.rs diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..72a7bff --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/side.iml b/.idea/side.iml new file mode 100644 index 0000000..fdaf31c --- /dev/null +++ b/.idea/side.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/side-node/src/keys/bitcoin.rs b/side-node/src/bitcoin/bitcoin_keys.rs similarity index 100% rename from side-node/src/keys/bitcoin.rs rename to side-node/src/bitcoin/bitcoin_keys.rs diff --git a/side-node/src/clients/btc_electrum_client.rs b/side-node/src/bitcoin/clients/btc_electrum_client.rs similarity index 95% rename from side-node/src/clients/btc_electrum_client.rs rename to side-node/src/bitcoin/clients/btc_electrum_client.rs index 52167c2..15a2f9f 100644 --- a/side-node/src/clients/btc_electrum_client.rs +++ b/side-node/src/bitcoin/clients/btc_electrum_client.rs @@ -1,4 +1,4 @@ -use crate::{keys, utils}; +use crate::{ utils, bitcoin}; use bdk::bitcoin::psbt::PartiallySignedTransaction; use bdk::bitcoin::Network; use bdk::database::MemoryDatabase; @@ -21,8 +21,8 @@ use bdk::{FeeRate, KeychainKind, SignOptions, TransactionDetails, Wallet}; pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); let sammy = utils::home(&"sammy".to_string()); - let dave_key = keys::bitcoin::load_from_file(&dave).unwrap(); - let sammy_key = keys::bitcoin::load_from_file(&sammy).unwrap(); + let dave_key = bitcoin::bitcoin_keys::load_from_file(&dave).unwrap(); + let sammy_key = bitcoin::bitcoin_keys::load_from_file(&sammy).unwrap(); let dave_wallet = create_wallet(dave_key)?; let sammy_wallet = create_wallet(sammy_key)?; diff --git a/side-node/src/clients/btc_esplora_client.rs b/side-node/src/bitcoin/clients/btc_esplora_client.rs similarity index 100% rename from side-node/src/clients/btc_esplora_client.rs rename to side-node/src/bitcoin/clients/btc_esplora_client.rs diff --git a/side-node/src/bitcoin/clients/mod.rs b/side-node/src/bitcoin/clients/mod.rs new file mode 100644 index 0000000..e24c49e --- /dev/null +++ b/side-node/src/bitcoin/clients/mod.rs @@ -0,0 +1,2 @@ +pub mod btc_electrum_client; +pub mod btc_esplora_client; diff --git a/side-node/src/bitcoin/mod.rs b/side-node/src/bitcoin/mod.rs new file mode 100644 index 0000000..59bf335 --- /dev/null +++ b/side-node/src/bitcoin/mod.rs @@ -0,0 +1,3 @@ +pub mod clients; +pub mod bitcoin_keys; + diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs index 2c213cd..6eba44d 100644 --- a/side-node/src/clients/mod.rs +++ b/side-node/src/clients/mod.rs @@ -1,3 +1 @@ -pub mod btc_electrum_client; -pub mod btc_esplora_client; pub mod websocket; diff --git a/side-node/src/init/mod.rs b/side-node/src/init/mod.rs index 2f07328..a0ee5f8 100644 --- a/side-node/src/init/mod.rs +++ b/side-node/src/init/mod.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use config::SideNodeConfig; -use crate::{keys, utils}; +use crate::{keys, utils, bitcoin}; pub(crate) mod config; @@ -14,7 +14,7 @@ pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io: keys::bft_crdt::write(&bft_crdt_key_path)?; println!("Writing bitcoin key to: {:?}", bitcoin_key_path); - keys::bitcoin::write(&bitcoin_key_path)?; + bitcoin::bitcoin_keys::write(&bitcoin_key_path)?; println!("Writing config to: {:?}", config_path); config::write_toml(&config, &config_path).expect("unable to write config file"); diff --git a/side-node/src/keys/mod.rs b/side-node/src/keys/mod.rs index c5a0529..98f8c7f 100644 --- a/side-node/src/keys/mod.rs +++ b/side-node/src/keys/mod.rs @@ -1,2 +1 @@ pub mod bft_crdt; -pub mod bitcoin; diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index f8ac9f2..5de5fa2 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -5,6 +5,7 @@ use crdt::TransactionList; use node::SideNode; use tokio::{sync::mpsc, task}; +pub(crate) mod bitcoin; pub(crate) mod cli; pub mod clients; pub mod crdt; @@ -31,19 +32,19 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - let _ = clients::btc_esplora_client::run().await; + let _ = bitcoin::clients::btc_esplora_client::run().await; } None => println!("No command provided. Exiting. See --help for more information."), } } -/// Wire everything up outside the application so we can test more easily later +/// Wire everything up outside the application so that we can test more easily later async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); - let bitcoin_keys = keys::bitcoin::load_from_file(&side_dir).unwrap(); - let bitcoin_wallet = clients::btc_electrum_client::create_wallet(bitcoin_keys).unwrap(); + let bitcoin_keys = bitcoin::bitcoin_keys::load_from_file(&side_dir).unwrap(); + let bitcoin_wallet = bitcoin::clients::btc_electrum_client::create_wallet(bitcoin_keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index a4547da..ce14f0a 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -2,13 +2,7 @@ use bft_json_crdt::{ json_crdt::{BaseCrdt, SignedOp}, keypair::make_keypair, }; -use side_node::{ - clients::{btc_electrum_client, websocket::Client}, - crdt::TransactionList, - keys, - node::SideNode, - utils, -}; +use side_node::{clients::websocket::Client, crdt::TransactionList, keys, node::SideNode, utils}; use tokio::sync::mpsc; #[tokio::test] @@ -41,8 +35,8 @@ async fn test_distribute_via_websockets() { async fn setup(_: &str) -> SideNode { // First, load up the keys and create a bft-crdt let bft_crdt_keys = make_keypair(); - let mnemonic_words = keys::bitcoin::make_mnemonic(); - let bitcoin_keys = keys::bitcoin::get(mnemonic_words).unwrap(); + let mnemonic_words = bitcoin::bitcoin_keys::make_mnemonic(); + let bitcoin_keys = bitcoin::bitcoin_keys::get(mnemonic_words).unwrap(); let bitcoin_wallet = btc_electrum_client::create_wallet(bitcoin_keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); From 73f33a61e6634395b0810d1449c6569fac7bcf4f Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:36:50 +0100 Subject: [PATCH 34/55] Simplified bitcoin keys module name --- side-node/src/bitcoin/clients/btc_electrum_client.rs | 6 +++--- side-node/src/bitcoin/{bitcoin_keys.rs => keys.rs} | 0 side-node/src/bitcoin/mod.rs | 2 +- side-node/src/init/mod.rs | 4 ++-- side-node/src/lib.rs | 6 +++--- side-node/tests/side_node.rs | 10 ++++++---- 6 files changed, 15 insertions(+), 13 deletions(-) rename side-node/src/bitcoin/{bitcoin_keys.rs => keys.rs} (100%) diff --git a/side-node/src/bitcoin/clients/btc_electrum_client.rs b/side-node/src/bitcoin/clients/btc_electrum_client.rs index 15a2f9f..d062219 100644 --- a/side-node/src/bitcoin/clients/btc_electrum_client.rs +++ b/side-node/src/bitcoin/clients/btc_electrum_client.rs @@ -1,4 +1,4 @@ -use crate::{ utils, bitcoin}; +use crate::{bitcoin, utils}; use bdk::bitcoin::psbt::PartiallySignedTransaction; use bdk::bitcoin::Network; use bdk::database::MemoryDatabase; @@ -21,8 +21,8 @@ use bdk::{FeeRate, KeychainKind, SignOptions, TransactionDetails, Wallet}; pub async fn run() -> Result<(), anyhow::Error> { let dave = utils::home(&"dave".to_string()); let sammy = utils::home(&"sammy".to_string()); - let dave_key = bitcoin::bitcoin_keys::load_from_file(&dave).unwrap(); - let sammy_key = bitcoin::bitcoin_keys::load_from_file(&sammy).unwrap(); + let dave_key = bitcoin::keys::load_from_file(&dave).unwrap(); + let sammy_key = bitcoin::keys::load_from_file(&sammy).unwrap(); let dave_wallet = create_wallet(dave_key)?; let sammy_wallet = create_wallet(sammy_key)?; diff --git a/side-node/src/bitcoin/bitcoin_keys.rs b/side-node/src/bitcoin/keys.rs similarity index 100% rename from side-node/src/bitcoin/bitcoin_keys.rs rename to side-node/src/bitcoin/keys.rs diff --git a/side-node/src/bitcoin/mod.rs b/side-node/src/bitcoin/mod.rs index 59bf335..badf115 100644 --- a/side-node/src/bitcoin/mod.rs +++ b/side-node/src/bitcoin/mod.rs @@ -1,3 +1,3 @@ pub mod clients; -pub mod bitcoin_keys; +pub mod keys; diff --git a/side-node/src/init/mod.rs b/side-node/src/init/mod.rs index a0ee5f8..36253a2 100644 --- a/side-node/src/init/mod.rs +++ b/side-node/src/init/mod.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use config::SideNodeConfig; -use crate::{keys, utils, bitcoin}; +use crate::{bitcoin, keys, utils}; pub(crate) mod config; @@ -14,7 +14,7 @@ pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io: keys::bft_crdt::write(&bft_crdt_key_path)?; println!("Writing bitcoin key to: {:?}", bitcoin_key_path); - bitcoin::bitcoin_keys::write(&bitcoin_key_path)?; + bitcoin::keys::write(&bitcoin_key_path)?; println!("Writing config to: {:?}", config_path); config::write_toml(&config, &config_path).expect("unable to write config file"); diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 5de5fa2..c04b469 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -5,7 +5,7 @@ use crdt::TransactionList; use node::SideNode; use tokio::{sync::mpsc, task}; -pub(crate) mod bitcoin; +pub mod bitcoin; pub(crate) mod cli; pub mod clients; pub mod crdt; @@ -43,8 +43,8 @@ async fn setup(name: &String) -> SideNode { // First, load up the keys and create a bft-crdt let side_dir = utils::home(name); let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); - let bitcoin_keys = bitcoin::bitcoin_keys::load_from_file(&side_dir).unwrap(); - let bitcoin_wallet = bitcoin::clients::btc_electrum_client::create_wallet(bitcoin_keys).unwrap(); + let keys = bitcoin::keys::load_from_file(&side_dir).unwrap(); + let bitcoin_wallet = bitcoin::clients::btc_electrum_client::create_wallet(keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index ce14f0a..56f4ab3 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -2,7 +2,9 @@ use bft_json_crdt::{ json_crdt::{BaseCrdt, SignedOp}, keypair::make_keypair, }; -use side_node::{clients::websocket::Client, crdt::TransactionList, keys, node::SideNode, utils}; +use side_node::{ + bitcoin, clients::websocket::Client, crdt::TransactionList, node::SideNode, utils, +}; use tokio::sync::mpsc; #[tokio::test] @@ -35,9 +37,9 @@ async fn test_distribute_via_websockets() { async fn setup(_: &str) -> SideNode { // First, load up the keys and create a bft-crdt let bft_crdt_keys = make_keypair(); - let mnemonic_words = bitcoin::bitcoin_keys::make_mnemonic(); - let bitcoin_keys = bitcoin::bitcoin_keys::get(mnemonic_words).unwrap(); - let bitcoin_wallet = btc_electrum_client::create_wallet(bitcoin_keys).unwrap(); + let mnemonic_words = bitcoin::keys::make_mnemonic(); + let keys = bitcoin::keys::get(mnemonic_words).unwrap(); + let bitcoin_wallet = bitcoin::clients::btc_electrum_client::create_wallet(keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input From 7fb4585debca37209d7307d2b4235a575eee172f Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:38:23 +0100 Subject: [PATCH 35/55] Renamed bitcoin clients --- .../bitcoin/clients/{btc_electrum_client.rs => electrum.rs} | 0 .../src/bitcoin/clients/{btc_esplora_client.rs => esplora.rs} | 0 side-node/src/bitcoin/clients/mod.rs | 4 ++-- side-node/src/lib.rs | 4 ++-- side-node/tests/side_node.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename side-node/src/bitcoin/clients/{btc_electrum_client.rs => electrum.rs} (100%) rename side-node/src/bitcoin/clients/{btc_esplora_client.rs => esplora.rs} (100%) diff --git a/side-node/src/bitcoin/clients/btc_electrum_client.rs b/side-node/src/bitcoin/clients/electrum.rs similarity index 100% rename from side-node/src/bitcoin/clients/btc_electrum_client.rs rename to side-node/src/bitcoin/clients/electrum.rs diff --git a/side-node/src/bitcoin/clients/btc_esplora_client.rs b/side-node/src/bitcoin/clients/esplora.rs similarity index 100% rename from side-node/src/bitcoin/clients/btc_esplora_client.rs rename to side-node/src/bitcoin/clients/esplora.rs diff --git a/side-node/src/bitcoin/clients/mod.rs b/side-node/src/bitcoin/clients/mod.rs index e24c49e..960b8ac 100644 --- a/side-node/src/bitcoin/clients/mod.rs +++ b/side-node/src/bitcoin/clients/mod.rs @@ -1,2 +1,2 @@ -pub mod btc_electrum_client; -pub mod btc_esplora_client; +pub mod electrum; +pub mod esplora; diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index c04b469..07b3c5e 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -32,7 +32,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - let _ = bitcoin::clients::btc_esplora_client::run().await; + let _ = bitcoin::clients::esplora::run().await; } None => println!("No command provided. Exiting. See --help for more information."), } @@ -44,7 +44,7 @@ async fn setup(name: &String) -> SideNode { let side_dir = utils::home(name); let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); let keys = bitcoin::keys::load_from_file(&side_dir).unwrap(); - let bitcoin_wallet = bitcoin::clients::btc_electrum_client::create_wallet(keys).unwrap(); + let bitcoin_wallet = bitcoin::clients::electrum::create_wallet(keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index 56f4ab3..c1acce3 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -39,7 +39,7 @@ async fn setup(_: &str) -> SideNode { let bft_crdt_keys = make_keypair(); let mnemonic_words = bitcoin::keys::make_mnemonic(); let keys = bitcoin::keys::get(mnemonic_words).unwrap(); - let bitcoin_wallet = bitcoin::clients::btc_electrum_client::create_wallet(keys).unwrap(); + let bitcoin_wallet = bitcoin::clients::electrum::create_wallet(keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); // Channels for internal communication, and a tokio task for stdin input From e6a4fe0fd61c4e8d15737c3bb1ce60fdf18b82a2 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:50:02 +0100 Subject: [PATCH 36/55] Moved all the bft_crdt stuff into its own module --- crates/bft-json-crdt/bft-crdt-derive/src/lib.rs | 2 +- crates/bft-json-crdt/src/json_crdt.rs | 2 +- crates/bft-json-crdt/tests/editing-trace.js | 6 +++--- side-node/src/{keys/bft_crdt.rs => bft_crdt/keys.rs} | 2 +- side-node/src/{crdt.rs => bft_crdt/mod.rs} | 2 ++ side-node/src/clients/websocket.rs | 6 +++--- side-node/src/init/mod.rs | 6 +++--- side-node/src/keys/mod.rs | 1 - side-node/src/lib.rs | 9 ++++----- side-node/src/node.rs | 2 +- side-node/tests/crdt.rs | 4 ++-- side-node/tests/side_node.rs | 4 ++-- 12 files changed, 23 insertions(+), 23 deletions(-) rename side-node/src/{keys/bft_crdt.rs => bft_crdt/keys.rs} (96%) rename side-node/src/{crdt.rs => bft_crdt/mod.rs} (97%) delete mode 100644 side-node/src/keys/mod.rs diff --git a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs index 656cb77..51ccedb 100644 --- a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs +++ b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs @@ -11,7 +11,7 @@ use syn::{ /// Helper to get tokenstream representing the parent crate fn get_crate_name() -> TokenStream { - let cr8 = crate_name("bft-json-crdt").unwrap_or(FoundCrate::Itself); + let cr8 = crate_name("bft-json-bft-crdt").unwrap_or(FoundCrate::Itself); match cr8 { FoundCrate::Itself => quote! { ::bft_json_crdt }, FoundCrate::Name(name) => { diff --git a/crates/bft-json-crdt/src/json_crdt.rs b/crates/bft-json-crdt/src/json_crdt.rs index 4f3648b..a445efd 100644 --- a/crates/bft-json-crdt/src/json_crdt.rs +++ b/crates/bft-json-crdt/src/json_crdt.rs @@ -18,7 +18,7 @@ use fastcrypto::{ // Verifier, }; // TODO: serde's json object serialization and deserialization (correctly) do not define anything -// object field order in JSON objects. However, the hash check impl in bft-json-crdt does take order +// object field order in JSON objects. However, the hash check impl in bft-json-bft-crdt does take order // into account. This is going to cause problems later for non-Rust implementations, BFT hash checking // currently depends on JSON serialization/deserialization object order. This shouldn't be the case // but I've hacked in an IndexMap for the moment to get the PoC working. To see the problem, replace this with diff --git a/crates/bft-json-crdt/tests/editing-trace.js b/crates/bft-json-crdt/tests/editing-trace.js index 42da951..a77d4cb 100644 --- a/crates/bft-json-crdt/tests/editing-trace.js +++ b/crates/bft-json-crdt/tests/editing-trace.js @@ -259796,7 +259796,7 @@ function insertAt(idx, elt) { const pos = new_log.findIndex(log => log[0] === parent_id) new_log.push([ID_COUNTER, pos, 0, elt]) crdt.splice(raw_i + 1, 0, { deleted: false, content: elt, id: ID_COUNTER }) - // console.log(`insert at ${idx} translated as op [${ID_COUNTER}, ${pos}, ${0}, ${escape(elt)}] found at ${raw_i + 1}::`, crdt[raw_i + 1]) + // console.log(`insert at ${idx} translated as op [${ID_COUNTER}, ${pos}, ${0}, ${escape(elt)}] found at ${raw_i + 1}::`, bft-crdt[raw_i + 1]) return } @@ -259816,7 +259816,7 @@ function deleteAt(idx) { const pos = new_log.findIndex(log => log[0] === our_id) new_log.push([ID_COUNTER, pos, 1]); crdt[raw_i].deleted = true - // console.log(`delete at ${idx} translated as op [${ID_COUNTER}, ${pos}, ${1}] found at ${raw_i} with our_id ${our_id}::`, crdt[raw_i]) + // console.log(`delete at ${idx} translated as op [${ID_COUNTER}, ${pos}, ${1}] found at ${raw_i} with our_id ${our_id}::`, bft-crdt[raw_i]) return } @@ -259853,7 +259853,7 @@ function rawJSString(edits) { // deleteAt(edit[0]) // } // } -// console.log(crdt) +// console.log(bft-crdt) // rawJSString(mock_edits) // console.log(new_log) // const subset = edits.slice(0, 50000) diff --git a/side-node/src/keys/bft_crdt.rs b/side-node/src/bft_crdt/keys.rs similarity index 96% rename from side-node/src/keys/bft_crdt.rs rename to side-node/src/bft_crdt/keys.rs index 4057f0f..078ac68 100644 --- a/side-node/src/keys/bft_crdt.rs +++ b/side-node/src/bft_crdt/keys.rs @@ -20,7 +20,7 @@ pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { pub(crate) fn load_from_file(side_dir: &PathBuf) -> Ed25519KeyPair { let key_path = crate::utils::side_paths(side_dir.clone()).0; - let data = fs::read_to_string(key_path).expect("couldn't read bft-crdt key file"); + let data = fs::read_to_string(key_path).expect("couldn't read bft-bft-crdt key file"); println!("data: {:?}", data); Ed25519KeyPair::decode_base64(&data).expect("couldn't load keypair from file") diff --git a/side-node/src/crdt.rs b/side-node/src/bft_crdt/mod.rs similarity index 97% rename from side-node/src/crdt.rs rename to side-node/src/bft_crdt/mod.rs index 847b151..8c24834 100644 --- a/side-node/src/crdt.rs +++ b/side-node/src/bft_crdt/mod.rs @@ -5,6 +5,8 @@ use bft_json_crdt::{ }; use serde::{Deserialize, Serialize}; +pub mod keys; + #[add_crdt_fields] #[derive(Clone, CrdtNode, Serialize, Deserialize)] pub struct TransactionList { diff --git a/side-node/src/clients/websocket.rs b/side-node/src/clients/websocket.rs index f7bfde0..52b30dd 100644 --- a/side-node/src/clients/websocket.rs +++ b/side-node/src/clients/websocket.rs @@ -36,8 +36,8 @@ impl ezsockets::ClientExt for Client { // match on it. type Call = String; - /// When we receive a text message, apply the crdt operation contained in it to our - /// local crdt. + /// When we receive a text message, apply the bft-crdt operation contained in it to our + /// local bft-crdt. async fn on_text(&mut self, text: String) -> Result<(), ezsockets::Error> { let string_sha = utils::shassy(text.clone()); println!("received text, sha: {string_sha}"); @@ -45,7 +45,7 @@ impl ezsockets::ClientExt for Client { let object_sha = utils::shappy(incoming.clone()); println!("deserialized: {}", object_sha); if string_sha != object_sha { - panic!("sha mismatch: {string_sha} != {object_sha}, bft-crdt has failed"); + panic!("sha mismatch: {string_sha} != {object_sha}, bft-bft-crdt has failed"); } self.incoming_sender.send(incoming).await?; Ok(()) diff --git a/side-node/src/init/mod.rs b/side-node/src/init/mod.rs index 36253a2..c1997dd 100644 --- a/side-node/src/init/mod.rs +++ b/side-node/src/init/mod.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use config::SideNodeConfig; -use crate::{bitcoin, keys, utils}; +use crate::{bft_crdt, bitcoin, utils}; pub(crate) mod config; @@ -10,8 +10,8 @@ pub(crate) fn init(home: PathBuf, config: SideNodeConfig) -> Result<(), std::io: ensure_side_directory_exists(&home)?; let (bft_crdt_key_path, bitcoin_key_path, config_path) = utils::side_paths(home.clone()); - println!("Writing bft crdt key to: {:?}", bft_crdt_key_path); - keys::bft_crdt::write(&bft_crdt_key_path)?; + println!("Writing bft bft-crdt key to: {:?}", bft_crdt_key_path); + bft_crdt::keys::write(&bft_crdt_key_path)?; println!("Writing bitcoin key to: {:?}", bitcoin_key_path); bitcoin::keys::write(&bitcoin_key_path)?; diff --git a/side-node/src/keys/mod.rs b/side-node/src/keys/mod.rs deleted file mode 100644 index 98f8c7f..0000000 --- a/side-node/src/keys/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod bft_crdt; diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 07b3c5e..1635475 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -1,16 +1,15 @@ +use bft_crdt::TransactionList; use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use cli::{parse_args, Commands}; use clients::websocket; -use crdt::TransactionList; use node::SideNode; use tokio::{sync::mpsc, task}; +pub mod bft_crdt; pub mod bitcoin; pub(crate) mod cli; pub mod clients; -pub mod crdt; pub(crate) mod init; -pub mod keys; pub mod node; pub(crate) mod stdin; pub mod utils; @@ -40,9 +39,9 @@ pub async fn run() { /// Wire everything up outside the application so that we can test more easily later async fn setup(name: &String) -> SideNode { - // First, load up the keys and create a bft-crdt + // First, load up the keys and create a bft-bft-crdt let side_dir = utils::home(name); - let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); + let bft_crdt_keys = bft_crdt::keys::load_from_file(&side_dir); let keys = bitcoin::keys::load_from_file(&side_dir).unwrap(); let bitcoin_wallet = bitcoin::clients::electrum::create_wallet(keys).unwrap(); let crdt = BaseCrdt::::new(&bft_crdt_keys); diff --git a/side-node/src/node.rs b/side-node/src/node.rs index 5496691..c8af43d 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -3,7 +3,7 @@ use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use fastcrypto::ed25519::Ed25519KeyPair; use tokio::sync::mpsc; -use crate::{clients::websocket::Client, crdt::TransactionList, utils}; +use crate::{bft_crdt::TransactionList, clients::websocket::Client, utils}; pub struct SideNode { crdt: BaseCrdt, diff --git a/side-node/tests/crdt.rs b/side-node/tests/crdt.rs index 57c2180..51408ca 100644 --- a/side-node/tests/crdt.rs +++ b/side-node/tests/crdt.rs @@ -1,12 +1,12 @@ use bft_json_crdt::json_crdt::BaseCrdt; use bft_json_crdt::keypair::make_keypair; use bft_json_crdt::op::ROOT_ID; -use side_node::crdt::TransactionList; +use side_node::bft_crdt::TransactionList; // case 1 - send valid updates #[test] fn test_valid_updates() { - // Insert to crdt.doc on local node, test applying the same operation to a remote node + // Insert to bft-crdt.doc on local node, test applying the same operation to a remote node // and check that the view is the same let keypair1 = make_keypair(); let mut crdt1 = BaseCrdt::::new(&keypair1); diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index c1acce3..4883605 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -3,7 +3,7 @@ use bft_json_crdt::{ keypair::make_keypair, }; use side_node::{ - bitcoin, clients::websocket::Client, crdt::TransactionList, node::SideNode, utils, + bft_crdt::TransactionList, bitcoin, clients::websocket::Client, node::SideNode, utils, }; use tokio::sync::mpsc; @@ -35,7 +35,7 @@ async fn test_distribute_via_websockets() { /// Wire everything up, ignoring things we are not using in the test async fn setup(_: &str) -> SideNode { - // First, load up the keys and create a bft-crdt + // First, load up the keys and create a bft-bft-crdt let bft_crdt_keys = make_keypair(); let mnemonic_words = bitcoin::keys::make_mnemonic(); let keys = bitcoin::keys::get(mnemonic_words).unwrap(); From a1e62ebb516ceb26bc755d9308b0c054a13007ff Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:54:10 +0100 Subject: [PATCH 37/55] Moved the websocket client into bft_crdt module --- side-node/src/bft_crdt/mod.rs | 1 + side-node/src/{clients => bft_crdt}/websocket.rs | 0 side-node/src/clients/mod.rs | 2 +- side-node/src/lib.rs | 3 ++- side-node/src/node.rs | 2 +- side-node/tests/side_node.rs | 2 +- 6 files changed, 6 insertions(+), 4 deletions(-) rename side-node/src/{clients => bft_crdt}/websocket.rs (100%) diff --git a/side-node/src/bft_crdt/mod.rs b/side-node/src/bft_crdt/mod.rs index 8c24834..5025322 100644 --- a/side-node/src/bft_crdt/mod.rs +++ b/side-node/src/bft_crdt/mod.rs @@ -6,6 +6,7 @@ use bft_json_crdt::{ use serde::{Deserialize, Serialize}; pub mod keys; +pub mod websocket; #[add_crdt_fields] #[derive(Clone, CrdtNode, Serialize, Deserialize)] diff --git a/side-node/src/clients/websocket.rs b/side-node/src/bft_crdt/websocket.rs similarity index 100% rename from side-node/src/clients/websocket.rs rename to side-node/src/bft_crdt/websocket.rs diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs index 6eba44d..8b13789 100644 --- a/side-node/src/clients/mod.rs +++ b/side-node/src/clients/mod.rs @@ -1 +1 @@ -pub mod websocket; + diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index 1635475..eb6f4f8 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -1,13 +1,14 @@ +use bft_crdt::websocket; use bft_crdt::TransactionList; use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use cli::{parse_args, Commands}; -use clients::websocket; use node::SideNode; use tokio::{sync::mpsc, task}; pub mod bft_crdt; pub mod bitcoin; pub(crate) mod cli; + pub mod clients; pub(crate) mod init; pub mod node; diff --git a/side-node/src/node.rs b/side-node/src/node.rs index c8af43d..9cde341 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -3,7 +3,7 @@ use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use fastcrypto::ed25519::Ed25519KeyPair; use tokio::sync::mpsc; -use crate::{bft_crdt::TransactionList, clients::websocket::Client, utils}; +use crate::{bft_crdt::websocket::Client, bft_crdt::TransactionList, utils}; pub struct SideNode { crdt: BaseCrdt, diff --git a/side-node/tests/side_node.rs b/side-node/tests/side_node.rs index 4883605..18f597e 100644 --- a/side-node/tests/side_node.rs +++ b/side-node/tests/side_node.rs @@ -3,7 +3,7 @@ use bft_json_crdt::{ keypair::make_keypair, }; use side_node::{ - bft_crdt::TransactionList, bitcoin, clients::websocket::Client, node::SideNode, utils, + bft_crdt::websocket::Client, bft_crdt::TransactionList, bitcoin, node::SideNode, utils, }; use tokio::sync::mpsc; From 3cbde1262e3066dbdfc98b4e6c5c0361156d6665 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:54:55 +0100 Subject: [PATCH 38/55] Removed unused client directory --- side-node/src/clients/mod.rs | 1 - side-node/src/lib.rs | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 side-node/src/clients/mod.rs diff --git a/side-node/src/clients/mod.rs b/side-node/src/clients/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/side-node/src/clients/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index eb6f4f8..eb99cd7 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -8,8 +8,6 @@ use tokio::{sync::mpsc, task}; pub mod bft_crdt; pub mod bitcoin; pub(crate) mod cli; - -pub mod clients; pub(crate) mod init; pub mod node; pub(crate) mod stdin; From 28ddb07126f1da4a0fb5c71510617a602f46dcdb Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 13:57:52 +0100 Subject: [PATCH 39/55] Removing unused code --- side-node/src/utils.rs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index 71940f2..9aeec88 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -37,31 +37,6 @@ pub fn fake_generic_transaction_json(from: String) -> Value { }) } -/// Generate a Bitcoin transaction from this node's bitcoin_keys -// pub fn fake_bitcoin_transaction(key_pair: Keypair) -> bitcoin::Transaction { -// let from = key_pair.public_key().to_string(); -// let to = "Bob"; -// let amount = 1; -// let lock_time = absolute::LockTime::from_height(0).expect("couldn't format btc lock time"); -// let input = TxIn { -// previous_output: todo!(), -// script_sig: todo!(), -// sequence: todo!(), -// witness: todo!(), -// }; -// let output = TxOut { -// value: todo!(), -// script_pubkey: todo!(), -// }; -// let tx = bitcoin::Transaction { -// version: Version(1), -// lock_time, -// input: vec![input], -// output: vec![output], -// }; -// tx -// } - pub fn shappy(op: SignedOp) -> String { let b = serde_json::to_string(&op).unwrap().into_bytes(); sha256::digest(b).to_string() From 9c00a7f30a5464a9790511327bfe4f7c0fbe78c6 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 14:04:43 +0100 Subject: [PATCH 40/55] Getting rid of "Debug" derived implementation in the macro --- crates/bft-json-crdt/bft-crdt-derive/src/lib.rs | 14 +++++++------- side-node/src/bft_crdt/mod.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs index 51ccedb..d53acfd 100644 --- a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs +++ b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs @@ -109,13 +109,13 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream { } } - impl #impl_generics std::fmt::Debug for #ident #ty_generics #where_clause { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut fields = Vec::new(); - #(fields.push(format!("{}", #ident_strings.to_string()));)* - write!(f, "{{ {:?} }}", fields.join(", ")) - } - } + // impl #impl_generics std::fmt::Debug for #ident #ty_generics #where_clause { + // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // let mut fields = Vec::new(); + // #(fields.push(format!("{}", #ident_strings.to_string()));)* + // write!(f, "{{ {:?} }}", fields.join(", ")) + // } + // } impl #impl_generics #crate_name::json_crdt::CrdtNode for #ident #ty_generics #where_clause { fn apply(&mut self, op: #crate_name::op::Op<#crate_name::json_crdt::JsonValue>) -> #crate_name::json_crdt::OpState { diff --git a/side-node/src/bft_crdt/mod.rs b/side-node/src/bft_crdt/mod.rs index 5025322..8dbc346 100644 --- a/side-node/src/bft_crdt/mod.rs +++ b/side-node/src/bft_crdt/mod.rs @@ -9,7 +9,7 @@ pub mod keys; pub mod websocket; #[add_crdt_fields] -#[derive(Clone, CrdtNode, Serialize, Deserialize)] +#[derive(Clone, CrdtNode, Serialize, Deserialize, Debug)] pub struct TransactionList { pub list: ListCrdt, } @@ -22,7 +22,7 @@ impl TransactionList { /// A fake Transaction struct we can use as a simulated payload #[add_crdt_fields] -#[derive(Clone, CrdtNode, Serialize, Deserialize, PartialEq)] +#[derive(Clone, CrdtNode, Serialize, Deserialize, PartialEq, Debug)] pub struct Transaction { from: String, to: String, From 6b29d49aaa3ddff664d640f8b6a0dd0ad3b691af Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 14:09:41 +0100 Subject: [PATCH 41/55] Fixing CRDT tests --- .../bft-json-crdt/bft-crdt-derive/src/lib.rs | 6 +++++ crates/bft-json-crdt/src/json_crdt.rs | 22 +++++++++---------- crates/bft-json-crdt/tests/byzantine.rs | 6 ++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs index d53acfd..7118722 100644 --- a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs +++ b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs @@ -109,6 +109,12 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream { } } + // I'm pulling this out so that we can see actual CRD content in debug output. + // + // The plan is to mostly get rid of the macros anyway, so it's a reasonable first step. + // It could (alternately) be just as good to keep the macros and change this function to + // output actual field content instead of just field names. + // // impl #impl_generics std::fmt::Debug for #ident #ty_generics #where_clause { // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // let mut fields = Vec::new(); diff --git a/crates/bft-json-crdt/src/json_crdt.rs b/crates/bft-json-crdt/src/json_crdt.rs index a445efd..638ba71 100644 --- a/crates/bft-json-crdt/src/json_crdt.rs +++ b/crates/bft-json-crdt/src/json_crdt.rs @@ -549,7 +549,7 @@ mod test { #[test] fn test_derive_basic() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Player { x: LwwRegisterCrdt, y: LwwRegisterCrdt, @@ -564,14 +564,14 @@ mod test { #[test] fn test_derive_nested() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Position { x: LwwRegisterCrdt, y: LwwRegisterCrdt, } #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Player { pos: Position, balance: LwwRegisterCrdt, @@ -589,7 +589,7 @@ mod test { #[test] fn test_lww_ops() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Test { a: LwwRegisterCrdt, b: LwwRegisterCrdt, @@ -649,7 +649,7 @@ mod test { #[test] fn test_vec_and_map_ops() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Test { a: ListCrdt, } @@ -689,14 +689,14 @@ mod test { #[test] fn test_causal_field_dependency() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Item { name: LwwRegisterCrdt, soulbound: LwwRegisterCrdt, } #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Player { inventory: ListCrdt, balance: LwwRegisterCrdt, @@ -755,7 +755,7 @@ mod test { #[test] fn test_2d_grid() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Game { grid: ListCrdt>>, } @@ -816,7 +816,7 @@ mod test { #[test] fn test_arb_json() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Test { reg: LwwRegisterCrdt, } @@ -852,13 +852,13 @@ mod test { #[test] fn test_wrong_json_types() { #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Nested { list: ListCrdt, } #[add_crdt_fields] - #[derive(Clone, CrdtNode)] + #[derive(Clone, CrdtNode, Debug)] struct Test { reg: LwwRegisterCrdt, strct: ListCrdt, diff --git a/crates/bft-json-crdt/tests/byzantine.rs b/crates/bft-json-crdt/tests/byzantine.rs index f195da1..d2044ec 100644 --- a/crates/bft-json-crdt/tests/byzantine.rs +++ b/crates/bft-json-crdt/tests/byzantine.rs @@ -20,7 +20,7 @@ use serde_json::json; // 5. block actual messages from honest actors (eclipse attack) #[add_crdt_fields] -#[derive(Clone, CrdtNode)] +#[derive(Clone, CrdtNode, Debug)] struct ListExample { list: ListCrdt, } @@ -91,13 +91,13 @@ fn test_forge_update() { } #[add_crdt_fields] -#[derive(Clone, CrdtNode)] +#[derive(Clone, CrdtNode, Debug)] struct Nested { a: Nested2, } #[add_crdt_fields] -#[derive(Clone, CrdtNode)] +#[derive(Clone, CrdtNode, Debug)] struct Nested2 { b: LwwRegisterCrdt, } From 5abc05a8a9c6c5177a939fbe7e4c252f930431a8 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 14:13:06 +0100 Subject: [PATCH 42/55] Starting to separate the esplora client funcionality from business logic Having a driver will allow us to start experimenting with transaction signing more easily --- side-node/src/bitcoin/driver.rs | 5 +++++ side-node/src/bitcoin/mod.rs | 2 +- side-node/src/lib.rs | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 side-node/src/bitcoin/driver.rs diff --git a/side-node/src/bitcoin/driver.rs b/side-node/src/bitcoin/driver.rs new file mode 100644 index 0000000..d11c9f0 --- /dev/null +++ b/side-node/src/bitcoin/driver.rs @@ -0,0 +1,5 @@ +use crate::bitcoin; + +pub(crate) async fn run() { + bitcoin::clients::esplora::run().await.unwrap(); +} diff --git a/side-node/src/bitcoin/mod.rs b/side-node/src/bitcoin/mod.rs index badf115..784440b 100644 --- a/side-node/src/bitcoin/mod.rs +++ b/side-node/src/bitcoin/mod.rs @@ -1,3 +1,3 @@ pub mod clients; +pub mod driver; pub mod keys; - diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs index eb99cd7..643805b 100644 --- a/side-node/src/lib.rs +++ b/side-node/src/lib.rs @@ -30,7 +30,7 @@ pub async fn run() { node.start().await; } Some(Commands::Btc {}) => { - let _ = bitcoin::clients::esplora::run().await; + let _ = bitcoin::driver::run().await; } None => println!("No command provided. Exiting. See --help for more information."), } From c6242e99f709441a8f831aad531a5bbb54d684d2 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 14:35:42 +0100 Subject: [PATCH 43/55] Pulled the client apart into (somewhat) re-usable functions --- side-node/src/bitcoin/clients/esplora.rs | 171 +++++++++++++---------- side-node/src/utils.rs | 2 +- 2 files changed, 101 insertions(+), 72 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index 3bb329d..8b8ae82 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -4,7 +4,9 @@ use bdk::keys::bip39::Mnemonic; use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_wallet::{ bitcoin::{Address, Amount, Network, Script}, + chain::ConfirmationTimeHeightAnchor, keys::{DerivableKey, ExtendedKey}, + wallet::AddressInfo, KeychainKind, SignOptions, Wallet, }; @@ -23,55 +25,59 @@ const PARALLEL_REQUESTS: usize = 5; /// Also, it very nadily works with the mutinynet.com esplora server, which is configured /// with 30 second block times. pub async fn run() -> Result<(), anyhow::Error> { - let dave_dir = utils::home(&"dave".to_string()); + let (mut wallet, mut db) = create_wallet("dave")?; - let mnemonic_path = crate::utils::side_paths(dave_dir).1; // TODO: this tuple stinks - let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file"); - - println!("Creating wallet from mnemonic: {mnemonic_words}"); - let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); - - // Generate the extended key - let xkey: ExtendedKey = mnemonic - .into_extended_key() - .expect("couldn't turn mnemonic into xkey"); - - let xprv = xkey - .into_xprv(Network::Signet) - .expect("problem converting xkey to xprv") - .to_string(); - - println!("Setting up esplora database"); - - let db_path = "/tmp/bdk-esplora-async-example.sqlite"; - let conn = Connection::open(db_path)?; - let mut db = Store::new(conn)?; - let external_descriptor = format!("wpkh({xprv}/84'/1'/0'/0/*)"); - let internal_descriptor = format!("wpkh({xprv}/84'/1'/0'/1/*)"); - let changeset = db.read().expect("couldn't read esplora database"); - - let mut wallet = Wallet::new_or_load( - &external_descriptor, - &internal_descriptor, - changeset, - Network::Signet, - ) - .expect("problem setting up wallet"); - - let address = wallet.next_unused_address(KeychainKind::External); - if let Some(changeset) = wallet.take_staged() { - db.write(&changeset)?; - } - println!("Generated Address: {}", address); + let _next_address = next_unused_address(&mut wallet, &mut db)?; let balance = wallet.balance(); println!("Wallet balance before syncing: {} sats", balance.total()); + let client = sync(&mut wallet, &mut db).await?; + + let balance = wallet.balance(); + println!("Wallet balance after syncing: {} sats", balance.total()); + + if balance.total() < SEND_AMOUNT { + println!( + "Please send at least {} sats to the receiving address", + SEND_AMOUNT + ); + std::process::exit(0); + } + + let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")? + .require_network(Network::Signet)?; + + let tx = build_send_tx(wallet, faucet_address)?; + client.broadcast(&tx).await?; + println!("Tx broadcasted! Txid: {}", tx.compute_txid()); + + Ok(()) +} + +fn build_send_tx( + mut wallet: Wallet, + faucet_address: Address, +) -> Result { + let mut tx_builder = wallet.build_tx(); + tx_builder + .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT) + .enable_rbf(); + let mut psbt = tx_builder.finish()?; + let finalized = wallet.sign(&mut psbt, SignOptions::default())?; + assert!(finalized); + let tx = psbt.extract_tx()?; + Ok(tx) +} + +async fn sync( + wallet: &mut Wallet, + db: &mut Store, +) -> Result { print!("Syncing..."); let client = esplora_client::Builder::new("https://mutinynet.com/api") .build_async() .expect("couldn't build esplora client"); - fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static { let mut once = Some(()); let mut stdout = std::io::stdout(); @@ -103,45 +109,68 @@ pub async fn run() -> Result<(), anyhow::Error> { KeychainKind::Internal, generate_inspect(KeychainKind::Internal), ); - let mut update = client .full_scan(request, STOP_GAP, PARALLEL_REQUESTS) .await?; let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); let _ = update.graph_update.update_last_seen_unconfirmed(now); - wallet.apply_update(update)?; if let Some(changeset) = wallet.take_staged() { db.write(&changeset)?; } println!(); - - let balance = wallet.balance(); - println!("Wallet balance after syncing: {} sats", balance.total()); - - if balance.total() < SEND_AMOUNT { - println!( - "Please send at least {} sats to the receiving address", - SEND_AMOUNT - ); - std::process::exit(0); - } - - let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")? - .require_network(Network::Signet)?; - - let mut tx_builder = wallet.build_tx(); - tx_builder - .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT) - .enable_rbf(); - - let mut psbt = tx_builder.finish()?; - let finalized = wallet.sign(&mut psbt, SignOptions::default())?; - assert!(finalized); - - let tx = psbt.extract_tx()?; - client.broadcast(&tx).await?; - println!("Tx broadcasted! Txid: {}", tx.compute_txid()); - - Ok(()) + Ok(client) +} + +fn next_unused_address( + wallet: &mut Wallet, + db: &mut Store, +) -> Result { + let address = wallet.next_unused_address(KeychainKind::External); + if let Some(changeset) = wallet.take_staged() { + db.write(&changeset)?; + } + println!("Generated Address: {}", address); + Ok(address) +} + +fn create_wallet( + name: &str, +) -> anyhow::Result<(Wallet, Store)> { + let keys_dir = utils::home(name); + + let mnemonic_path = crate::utils::side_paths(keys_dir).1; // TODO: this tuple stinks + let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file"); + + println!("Creating wallet from mnemonic: {mnemonic_words}"); + let mnemonic = Mnemonic::parse(mnemonic_words).unwrap(); + + // Generate the extended key + let xkey: ExtendedKey = mnemonic + .into_extended_key() + .expect("couldn't turn mnemonic into xkey"); + + let xprv = xkey + .into_xprv(Network::Signet) + .expect("problem converting xkey to xprv") + .to_string(); + + println!("Setting up esplora database for {name}"); + + let db_path = format!("/tmp/{name}-bdk-esplora-async-example.sqlite"); + let conn = Connection::open(db_path)?; + let mut db = Store::new(conn)?; + let external_descriptor = format!("wpkh({xprv}/84'/1'/0'/0/*)"); + let internal_descriptor = format!("wpkh({xprv}/84'/1'/0'/1/*)"); + let changeset = db.read().expect("couldn't read esplora database"); + + let wallet = Wallet::new_or_load( + &external_descriptor, + &internal_descriptor, + changeset, + Network::Signet, + ) + .expect("problem setting up wallet"); + + Ok((wallet, db)) } diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index 9aeec88..6ed33b8 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -21,7 +21,7 @@ pub(crate) fn side_paths(prefix: PathBuf) -> (PathBuf, PathBuf, PathBuf) { } /// Returns the path to the home directory for this host OS and the given node name -pub(crate) fn home(name: &String) -> std::path::PathBuf { +pub(crate) fn home(name: &str) -> std::path::PathBuf { let mut path = dirs::home_dir().unwrap(); path.push(".side"); path.push(name); From 2474f5186d454434bded72b4d52f11253ebdeecf Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 14:43:57 +0100 Subject: [PATCH 44/55] More refactoring to make the driver do the work --- side-node/src/bitcoin/clients/esplora.rs | 49 +++--------------------- side-node/src/bitcoin/driver.rs | 44 +++++++++++++++++++-- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index 8b8ae82..cce3e88 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -14,54 +14,17 @@ use bdk_sqlite::{rusqlite::Connection, Store}; use crate::utils; -const SEND_AMOUNT: Amount = Amount::from_sat(5000); const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 5; -/// Demonstrates the use of bdk with the Esplora client. -/// -/// This is more complex than the bare `bdk` crate, but the esplora client works. -/// -/// Also, it very nadily works with the mutinynet.com esplora server, which is configured -/// with 30 second block times. -pub async fn run() -> Result<(), anyhow::Error> { - let (mut wallet, mut db) = create_wallet("dave")?; - - let _next_address = next_unused_address(&mut wallet, &mut db)?; - - let balance = wallet.balance(); - println!("Wallet balance before syncing: {} sats", balance.total()); - - let client = sync(&mut wallet, &mut db).await?; - - let balance = wallet.balance(); - println!("Wallet balance after syncing: {} sats", balance.total()); - - if balance.total() < SEND_AMOUNT { - println!( - "Please send at least {} sats to the receiving address", - SEND_AMOUNT - ); - std::process::exit(0); - } - - let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")? - .require_network(Network::Signet)?; - - let tx = build_send_tx(wallet, faucet_address)?; - client.broadcast(&tx).await?; - println!("Tx broadcasted! Txid: {}", tx.compute_txid()); - - Ok(()) -} - -fn build_send_tx( +pub(crate) fn build_send_tx( mut wallet: Wallet, faucet_address: Address, + amount: Amount, ) -> Result { let mut tx_builder = wallet.build_tx(); tx_builder - .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT) + .add_recipient(faucet_address.script_pubkey(), amount) .enable_rbf(); let mut psbt = tx_builder.finish()?; let finalized = wallet.sign(&mut psbt, SignOptions::default())?; @@ -70,7 +33,7 @@ fn build_send_tx( Ok(tx) } -async fn sync( +pub(crate) async fn sync( wallet: &mut Wallet, db: &mut Store, ) -> Result { @@ -122,7 +85,7 @@ async fn sync( Ok(client) } -fn next_unused_address( +pub(crate) fn next_unused_address( wallet: &mut Wallet, db: &mut Store, ) -> Result { @@ -134,7 +97,7 @@ fn next_unused_address( Ok(address) } -fn create_wallet( +pub(crate) fn create_wallet( name: &str, ) -> anyhow::Result<(Wallet, Store)> { let keys_dir = utils::home(name); diff --git a/side-node/src/bitcoin/driver.rs b/side-node/src/bitcoin/driver.rs index d11c9f0..46ce4fd 100644 --- a/side-node/src/bitcoin/driver.rs +++ b/side-node/src/bitcoin/driver.rs @@ -1,5 +1,43 @@ -use crate::bitcoin; +use std::str::FromStr; -pub(crate) async fn run() { - bitcoin::clients::esplora::run().await.unwrap(); +use bdk_wallet::bitcoin::{Address, Amount, Network}; + +use crate::bitcoin::clients; + +/// Demonstrates the use of bdk with the Esplora client. +/// +/// This is more complex than the bare `bdk` crate, but the esplora client works. +/// +/// Also, it very handily works with the mutinynet.com esplora server, which is configured +/// with 30 second block times. +pub(crate) async fn run() -> Result<(), anyhow::Error> { + let (mut wallet, mut db) = clients::esplora::create_wallet("dave")?; + + let _next_address = clients::esplora::next_unused_address(&mut wallet, &mut db)?; + + let balance = wallet.balance(); + println!("Wallet balance before syncing: {} sats", balance.total()); + + let client = clients::esplora::sync(&mut wallet, &mut db).await?; + + let balance = wallet.balance(); + println!("Wallet balance after syncing: {} sats", balance.total()); + + let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")? + .require_network(Network::Signet)?; + let send_amount = Amount::from_sat(5000); + + if balance.total() < send_amount { + println!( + "Please send at least {} sats to the receiving address", + send_amount + ); + std::process::exit(0); + } + + let tx = clients::esplora::build_send_tx(wallet, faucet_address, send_amount)?; + client.broadcast(&tx).await?; + println!("Tx broadcasted! Txid: {}", tx.compute_txid()); + + Ok(()) } From 35deb4a75c72f9a0f84ca9ee00a15c120a939d87 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 14:58:13 +0100 Subject: [PATCH 45/55] Extracted the Esplora wallet into a struct --- side-node/src/bitcoin/clients/esplora.rs | 178 +++++++++++++---------- side-node/src/bitcoin/driver.rs | 11 +- 2 files changed, 106 insertions(+), 83 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index cce3e88..d4ac00f 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -1,7 +1,10 @@ use std::{collections::BTreeSet, fs, io::Write, str::FromStr}; use bdk::keys::bip39::Mnemonic; -use bdk_esplora::{esplora_client, EsploraAsyncExt}; +use bdk_esplora::{ + esplora_client::{self, AsyncClient}, + EsploraAsyncExt, +}; use bdk_wallet::{ bitcoin::{Address, Amount, Network, Script}, chain::ConfirmationTimeHeightAnchor, @@ -17,89 +20,102 @@ use crate::utils; const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 5; -pub(crate) fn build_send_tx( - mut wallet: Wallet, - faucet_address: Address, - amount: Amount, -) -> Result { - let mut tx_builder = wallet.build_tx(); - tx_builder - .add_recipient(faucet_address.script_pubkey(), amount) - .enable_rbf(); - let mut psbt = tx_builder.finish()?; - let finalized = wallet.sign(&mut psbt, SignOptions::default())?; - assert!(finalized); - let tx = psbt.extract_tx()?; - Ok(tx) +pub struct EsploraWallet { + wallet: Wallet, + db: Store, + client: AsyncClient, } -pub(crate) async fn sync( - wallet: &mut Wallet, - db: &mut Store, -) -> Result { - print!("Syncing..."); - let client = esplora_client::Builder::new("https://mutinynet.com/api") - .build_async() - .expect("couldn't build esplora client"); - fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static { - let mut once = Some(()); - let mut stdout = std::io::stdout(); - move |spk_i, _| { - match once.take() { - Some(_) => print!("\nScanning keychain [{:?}]", kind), - None => print!(" {:<3}", spk_i), - }; - stdout.flush().expect("must flush"); - } +impl EsploraWallet { + pub(crate) fn build_send_tx( + &mut self, + faucet_address: Address, + amount: Amount, + ) -> Result { + let mut tx_builder = self.wallet.build_tx(); + tx_builder + .add_recipient(faucet_address.script_pubkey(), amount) + .enable_rbf(); + let mut psbt = tx_builder.finish()?; + let finalized = self.wallet.sign(&mut psbt, SignOptions::default())?; + assert!(finalized); + let tx = psbt.extract_tx()?; + Ok(tx) } - let request = wallet - .start_full_scan() - .inspect_spks_for_all_keychains({ - let mut once = BTreeSet::::new(); - move |keychain, spk_i, _| { - match once.insert(keychain) { - true => print!("\nScanning keychain [{:?}]", keychain), - false => print!(" {:<3}", spk_i), - } - std::io::stdout().flush().expect("must flush") + + pub(crate) async fn sync(&mut self) -> Result<(), anyhow::Error> { + print!("Syncing..."); + + fn generate_inspect( + kind: KeychainKind, + ) -> impl FnMut(u32, &Script) + Send + Sync + 'static { + let mut once = Some(()); + let mut stdout = std::io::stdout(); + move |spk_i, _| { + match once.take() { + Some(_) => print!("\nScanning keychain [{:?}]", kind), + None => print!(" {:<3}", spk_i), + }; + stdout.flush().expect("must flush"); } - }) - .inspect_spks_for_keychain( - KeychainKind::External, - generate_inspect(KeychainKind::External), - ) - .inspect_spks_for_keychain( - KeychainKind::Internal, - generate_inspect(KeychainKind::Internal), - ); - let mut update = client - .full_scan(request, STOP_GAP, PARALLEL_REQUESTS) - .await?; - let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - let _ = update.graph_update.update_last_seen_unconfirmed(now); - wallet.apply_update(update)?; - if let Some(changeset) = wallet.take_staged() { - db.write(&changeset)?; + } + let request = self + .wallet + .start_full_scan() + .inspect_spks_for_all_keychains({ + let mut once = BTreeSet::::new(); + move |keychain, spk_i, _| { + match once.insert(keychain) { + true => print!("\nScanning keychain [{:?}]", keychain), + false => print!(" {:<3}", spk_i), + } + std::io::stdout().flush().expect("must flush") + } + }) + .inspect_spks_for_keychain( + KeychainKind::External, + generate_inspect(KeychainKind::External), + ) + .inspect_spks_for_keychain( + KeychainKind::Internal, + generate_inspect(KeychainKind::Internal), + ); + let mut update = self + .client + .full_scan(request, STOP_GAP, PARALLEL_REQUESTS) + .await?; + let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + let _ = update.graph_update.update_last_seen_unconfirmed(now); + self.wallet.apply_update(update)?; + if let Some(changeset) = self.wallet.take_staged() { + self.db.write(&changeset)?; + } + println!(); + Ok(()) + } + + pub(crate) fn next_unused_address(&mut self) -> Result { + let address = self.wallet.next_unused_address(KeychainKind::External); + if let Some(changeset) = self.wallet.take_staged() { + self.db.write(&changeset)?; + } + println!("Generated Address: {}", address); + Ok(address) + } + + pub(crate) fn balance(&self) -> bdk_wallet::wallet::Balance { + self.wallet.balance() + } + + pub(crate) async fn broadcast( + &self, + tx: &bitcoin::Transaction, + ) -> Result<(), esplora_client::Error> { + self.client.broadcast(tx).await } - println!(); - Ok(client) } -pub(crate) fn next_unused_address( - wallet: &mut Wallet, - db: &mut Store, -) -> Result { - let address = wallet.next_unused_address(KeychainKind::External); - if let Some(changeset) = wallet.take_staged() { - db.write(&changeset)?; - } - println!("Generated Address: {}", address); - Ok(address) -} - -pub(crate) fn create_wallet( - name: &str, -) -> anyhow::Result<(Wallet, Store)> { +pub(crate) fn create_wallet(name: &str) -> anyhow::Result { let keys_dir = utils::home(name); let mnemonic_path = crate::utils::side_paths(keys_dir).1; // TODO: this tuple stinks @@ -135,5 +151,11 @@ pub(crate) fn create_wallet( ) .expect("problem setting up wallet"); - Ok((wallet, db)) + let client = esplora_client::Builder::new("https://mutinynet.com/api") + .build_async() + .expect("couldn't build esplora client"); + + let esplora = EsploraWallet { wallet, db, client }; + + Ok(esplora) } diff --git a/side-node/src/bitcoin/driver.rs b/side-node/src/bitcoin/driver.rs index 46ce4fd..b1c8c93 100644 --- a/side-node/src/bitcoin/driver.rs +++ b/side-node/src/bitcoin/driver.rs @@ -11,14 +11,14 @@ use crate::bitcoin::clients; /// Also, it very handily works with the mutinynet.com esplora server, which is configured /// with 30 second block times. pub(crate) async fn run() -> Result<(), anyhow::Error> { - let (mut wallet, mut db) = clients::esplora::create_wallet("dave")?; + let mut wallet = clients::esplora::create_wallet("dave")?; - let _next_address = clients::esplora::next_unused_address(&mut wallet, &mut db)?; + let _next_address = wallet.next_unused_address()?; let balance = wallet.balance(); println!("Wallet balance before syncing: {} sats", balance.total()); - let client = clients::esplora::sync(&mut wallet, &mut db).await?; + wallet.sync().await?; let balance = wallet.balance(); println!("Wallet balance after syncing: {} sats", balance.total()); @@ -35,8 +35,9 @@ pub(crate) async fn run() -> Result<(), anyhow::Error> { std::process::exit(0); } - let tx = clients::esplora::build_send_tx(wallet, faucet_address, send_amount)?; - client.broadcast(&tx).await?; + let tx = wallet.build_send_tx(faucet_address, send_amount)?; + wallet.broadcast(&tx).await?; + println!("Tx broadcasted! Txid: {}", tx.compute_txid()); Ok(()) From cf116829f8f57afc0b168a1bfded5df0579edbb6 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 14:58:53 +0100 Subject: [PATCH 46/55] Naming driver users --- side-node/src/bitcoin/driver.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/side-node/src/bitcoin/driver.rs b/side-node/src/bitcoin/driver.rs index b1c8c93..b102466 100644 --- a/side-node/src/bitcoin/driver.rs +++ b/side-node/src/bitcoin/driver.rs @@ -11,16 +11,16 @@ use crate::bitcoin::clients; /// Also, it very handily works with the mutinynet.com esplora server, which is configured /// with 30 second block times. pub(crate) async fn run() -> Result<(), anyhow::Error> { - let mut wallet = clients::esplora::create_wallet("dave")?; + let mut dave = clients::esplora::create_wallet("dave")?; - let _next_address = wallet.next_unused_address()?; + let _next_address = dave.next_unused_address()?; - let balance = wallet.balance(); + let balance = dave.balance(); println!("Wallet balance before syncing: {} sats", balance.total()); - wallet.sync().await?; + dave.sync().await?; - let balance = wallet.balance(); + let balance = dave.balance(); println!("Wallet balance after syncing: {} sats", balance.total()); let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")? @@ -35,8 +35,8 @@ pub(crate) async fn run() -> Result<(), anyhow::Error> { std::process::exit(0); } - let tx = wallet.build_send_tx(faucet_address, send_amount)?; - wallet.broadcast(&tx).await?; + let tx = dave.build_send_tx(faucet_address, send_amount)?; + dave.broadcast(&tx).await?; println!("Tx broadcasted! Txid: {}", tx.compute_txid()); From 7effe9455fa5d23e298931712f5324d37cee0767 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 15:02:50 +0100 Subject: [PATCH 47/55] Some docs on the bitcoin client --- side-node/src/bitcoin/clients/esplora.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index d4ac00f..81b2d79 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -27,14 +27,19 @@ pub struct EsploraWallet { } impl EsploraWallet { + /// Builds and signs a send transaction to send coins between addresses. + /// + /// Does NOT send it, you must call `broadcast` to do that. + /// + /// We could split the creation and signing easily if needed. pub(crate) fn build_send_tx( &mut self, - faucet_address: Address, + recipient: Address, amount: Amount, ) -> Result { let mut tx_builder = self.wallet.build_tx(); tx_builder - .add_recipient(faucet_address.script_pubkey(), amount) + .add_recipient(recipient.script_pubkey(), amount) .enable_rbf(); let mut psbt = tx_builder.finish()?; let finalized = self.wallet.sign(&mut psbt, SignOptions::default())?; @@ -43,6 +48,7 @@ impl EsploraWallet { Ok(tx) } + /// Syncs the wallet with the latest state of the Bitcoin blockchain pub(crate) async fn sync(&mut self) -> Result<(), anyhow::Error> { print!("Syncing..."); @@ -94,6 +100,7 @@ impl EsploraWallet { Ok(()) } + /// Gets the next unused address from the wallet. pub(crate) fn next_unused_address(&mut self) -> Result { let address = self.wallet.next_unused_address(KeychainKind::External); if let Some(changeset) = self.wallet.take_staged() { @@ -103,10 +110,12 @@ impl EsploraWallet { Ok(address) } + /// Returns the balance of the wallet. pub(crate) fn balance(&self) -> bdk_wallet::wallet::Balance { self.wallet.balance() } + /// Broadcasts a signed transaction to the network. pub(crate) async fn broadcast( &self, tx: &bitcoin::Transaction, @@ -115,6 +124,7 @@ impl EsploraWallet { } } +/// Creates a Bitcoin Signet descriptor wallet with the mnemonic in the given user directory. pub(crate) fn create_wallet(name: &str) -> anyhow::Result { let keys_dir = utils::home(name); From a6105cf2bfc0fc01269995481ce65c42ad7f224a Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 15:05:33 +0100 Subject: [PATCH 48/55] Made the wallet's Network configurable --- side-node/src/bitcoin/clients/esplora.rs | 9 +++++---- side-node/src/bitcoin/driver.rs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index 81b2d79..9188188 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeSet, fs, io::Write, str::FromStr}; +use std::{collections::BTreeSet, fs, io::Write}; use bdk::keys::bip39::Mnemonic; use bdk_esplora::{ @@ -20,6 +20,7 @@ use crate::utils; const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 5; +/// A wallet that uses the Esplora client to interact with the Bitcoin network. pub struct EsploraWallet { wallet: Wallet, db: Store, @@ -124,8 +125,8 @@ impl EsploraWallet { } } -/// Creates a Bitcoin Signet descriptor wallet with the mnemonic in the given user directory. -pub(crate) fn create_wallet(name: &str) -> anyhow::Result { +/// Creates a Bitcoin descriptor wallet with the mnemonic in the given user directory. +pub(crate) fn create_wallet(name: &str, network: Network) -> anyhow::Result { let keys_dir = utils::home(name); let mnemonic_path = crate::utils::side_paths(keys_dir).1; // TODO: this tuple stinks @@ -157,7 +158,7 @@ pub(crate) fn create_wallet(name: &str) -> anyhow::Result { &external_descriptor, &internal_descriptor, changeset, - Network::Signet, + network, ) .expect("problem setting up wallet"); diff --git a/side-node/src/bitcoin/driver.rs b/side-node/src/bitcoin/driver.rs index b102466..ea2cab1 100644 --- a/side-node/src/bitcoin/driver.rs +++ b/side-node/src/bitcoin/driver.rs @@ -11,7 +11,7 @@ use crate::bitcoin::clients; /// Also, it very handily works with the mutinynet.com esplora server, which is configured /// with 30 second block times. pub(crate) async fn run() -> Result<(), anyhow::Error> { - let mut dave = clients::esplora::create_wallet("dave")?; + let mut dave = clients::esplora::create_wallet("dave", Network::Signet)?; let _next_address = dave.next_unused_address()?; From 6b1aa2b4ca641f6e697a867002d79087e28a3d2d Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 15:18:30 +0100 Subject: [PATCH 49/55] Sending between multiple addresses works nicely --- side-node/src/bitcoin/clients/esplora.rs | 22 ++++++++++++--- side-node/src/bitcoin/driver.rs | 36 ++++++++++++++++-------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index 9188188..262f353 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -22,9 +22,10 @@ const PARALLEL_REQUESTS: usize = 5; /// A wallet that uses the Esplora client to interact with the Bitcoin network. pub struct EsploraWallet { - wallet: Wallet, - db: Store, client: AsyncClient, + db: Store, + name: String, + wallet: Wallet, } impl EsploraWallet { @@ -107,7 +108,10 @@ impl EsploraWallet { if let Some(changeset) = self.wallet.take_staged() { self.db.write(&changeset)?; } - println!("Generated Address: {}", address); + println!( + "Generated address: https://mutinynet.com/address/{}", + address + ); Ok(address) } @@ -121,6 +125,11 @@ impl EsploraWallet { &self, tx: &bitcoin::Transaction, ) -> Result<(), esplora_client::Error> { + println!( + "{} broadcasting tx https://mutinynet.com/tx/{}", + self.name, + tx.compute_txid() + ); self.client.broadcast(tx).await } } @@ -166,7 +175,12 @@ pub(crate) fn create_wallet(name: &str, network: Network) -> anyhow::Result Result<(), anyhow::Error> { let mut dave = clients::esplora::create_wallet("dave", Network::Signet)?; + let mut sammy = clients::esplora::create_wallet("sammy", Network::Signet)?; let _next_address = dave.next_unused_address()?; - let balance = dave.balance(); - println!("Wallet balance before syncing: {} sats", balance.total()); + let dave_balance = dave.balance(); + println!( + "Dave wallet balance before syncing: {} sats", + dave_balance.total() + ); dave.sync().await?; - let balance = dave.balance(); - println!("Wallet balance after syncing: {} sats", balance.total()); + let dave_balance = dave.balance(); + println!("Wallet balance after syncing: {} sats", dave_balance); - let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")? - .require_network(Network::Signet)?; - let send_amount = Amount::from_sat(5000); + let sammy_address = sammy.next_unused_address()?.address; + println!("Sammy's address: {}", sammy_address); - if balance.total() < send_amount { + let sammy_balance = sammy.balance(); + println!( + "Sammy wallet balance before syncing: {} sats", + sammy_balance + ); + + sammy.sync().await?; + + let sammy_balance = sammy.balance(); + println!("Sammy wallet balance after syncing: {} sats", sammy_balance); + + let send_amount = Amount::from_sat(500); + + if dave_balance.total() < send_amount { println!( "Please send at least {} sats to the receiving address", send_amount @@ -35,10 +51,8 @@ pub(crate) async fn run() -> Result<(), anyhow::Error> { std::process::exit(0); } - let tx = dave.build_send_tx(faucet_address, send_amount)?; + let tx = dave.build_send_tx(sammy_address, send_amount)?; dave.broadcast(&tx).await?; - println!("Tx broadcasted! Txid: {}", tx.compute_txid()); - Ok(()) } From 93e66ba8b5155ce4cfabeda12af12a060cba0ce1 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 15:20:13 +0100 Subject: [PATCH 50/55] Noted deprectaion of the glorious electrum client. I'll keep it around for a while in case I run into trouble with esplora/mutiny --- side-node/src/bitcoin/clients/electrum.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/side-node/src/bitcoin/clients/electrum.rs b/side-node/src/bitcoin/clients/electrum.rs index d062219..a4a0a9a 100644 --- a/side-node/src/bitcoin/clients/electrum.rs +++ b/side-node/src/bitcoin/clients/electrum.rs @@ -9,13 +9,13 @@ use bdk::wallet::AddressInfo; use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions}; use bdk::{FeeRate, KeychainKind, SignOptions, TransactionDetails, Wallet}; -/// Run the Bitcoin wallet example +/// DEPRECATED /// /// This is a bdk example that uses the Electrum client to interact with the Bitcoin network. /// Electrum is a light client that connects to a server to get information about the Bitcoin network. /// The BDK itself does not have the ability to connect to e.g. esplora servers. As the Blockstream Electrum Signet /// server does not appear to be picking up transactions properly at the moment, I've shifted over to using -/// the (more complex) `bdk_wallet` crate and the esplora client there (see the other RPC client). +/// the (more complex) `bdk_wallet` crate and the esplora client there (see the other bitcoin client). /// /// Note:the types below are all completely different than the types in `bdk_wallet`. pub async fn run() -> Result<(), anyhow::Error> { From 70d1b1eed9101b9aeabad798fbc7fc8d8a705917 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 15:23:42 +0100 Subject: [PATCH 51/55] Extracted a persist_local() function --- side-node/src/bitcoin/clients/esplora.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index 262f353..db5e3bd 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -99,6 +99,7 @@ impl EsploraWallet { self.db.write(&changeset)?; } println!(); + println!("Sync complete for {}", self.name); Ok(()) } From c7095ced7b8d50d3f8292611f44a0951a7261e33 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 15:23:52 +0100 Subject: [PATCH 52/55] ibid --- side-node/src/bitcoin/clients/esplora.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index db5e3bd..07fe848 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -95,20 +95,21 @@ impl EsploraWallet { let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); let _ = update.graph_update.update_last_seen_unconfirmed(now); self.wallet.apply_update(update)?; - if let Some(changeset) = self.wallet.take_staged() { - self.db.write(&changeset)?; - } - println!(); + self.persist_local()?; println!("Sync complete for {}", self.name); Ok(()) } + fn persist_local(&mut self) -> Result<(), anyhow::Error> { + Ok(if let Some(changeset) = self.wallet.take_staged() { + self.db.write(&changeset)?; + }) + } + /// Gets the next unused address from the wallet. pub(crate) fn next_unused_address(&mut self) -> Result { let address = self.wallet.next_unused_address(KeychainKind::External); - if let Some(changeset) = self.wallet.take_staged() { - self.db.write(&changeset)?; - } + self.persist_local()?; println!( "Generated address: https://mutinynet.com/address/{}", address From 446efc2fbfa44360888f5b16dd30c1fc6c05d885 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 25 Jun 2024 17:32:44 +0100 Subject: [PATCH 53/55] Noted that we're signing as well as building tx in the method signature --- side-node/src/bitcoin/clients/esplora.rs | 2 +- side-node/src/bitcoin/driver.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/side-node/src/bitcoin/clients/esplora.rs b/side-node/src/bitcoin/clients/esplora.rs index 07fe848..038d0a7 100644 --- a/side-node/src/bitcoin/clients/esplora.rs +++ b/side-node/src/bitcoin/clients/esplora.rs @@ -34,7 +34,7 @@ impl EsploraWallet { /// Does NOT send it, you must call `broadcast` to do that. /// /// We could split the creation and signing easily if needed. - pub(crate) fn build_send_tx( + pub(crate) fn build_and_sign_send_tx( &mut self, recipient: Address, amount: Amount, diff --git a/side-node/src/bitcoin/driver.rs b/side-node/src/bitcoin/driver.rs index 115ebc4..e5e69fc 100644 --- a/side-node/src/bitcoin/driver.rs +++ b/side-node/src/bitcoin/driver.rs @@ -10,7 +10,9 @@ use crate::bitcoin::clients; /// /// Also, it very handily works with the mutinynet.com esplora server, which is configured /// with 30 second block times. -pub(crate) async fn run() -> Result<(), anyhow::Error> { +pub(crate) async fn run() -> Result<(), anyhow::Error> {} + +async fn simple_transfer() -> Result<(), anyhow::Error> { let mut dave = clients::esplora::create_wallet("dave", Network::Signet)?; let mut sammy = clients::esplora::create_wallet("sammy", Network::Signet)?; @@ -51,7 +53,7 @@ pub(crate) async fn run() -> Result<(), anyhow::Error> { std::process::exit(0); } - let tx = dave.build_send_tx(sammy_address, send_amount)?; + let tx = dave.build_and_sign_send_tx(sammy_address, send_amount)?; dave.broadcast(&tx).await?; Ok(()) From 213b8f22fe2123c7f624a5fb52bda5845077c0dc Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 26 Jun 2024 18:11:42 +0100 Subject: [PATCH 54/55] Added some docs regaring the Bitcoin clients --- README.md | 16 ++++++++++++++++ side-node/src/bitcoin/driver.rs | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0dc1f7c..0e4ce90 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,19 @@ To fulfill the promises in the Lite Paper, the Side Watcher needs to: [ ] submit P2P chain data to the Side Chain Later, we will aim to remove the Side Watcher from the architecture, by (a) moving to pure P2P transactions between Side Nodes, and (b) doing leader election of a Side Node to reach agreement on the submitted block. + +## Bitcoin integration + +There is a Bitcoin client integrated into the node, which can do simple coin transfers using esplora and the Mutinynet server's Signet (30 second blocktime). + +The client's demo driver can be run by doing: + +``` +cargo run -- init dave +cargo run -- init sammy +cargo run -- btc +``` + +You'll need to have funded the "dave" address prior to running the `btc` command - otherwise the transfer will fail gracefully. + +I was using this primarily as a way to experiment with constructing and broadcasting Bitcoin transactions, with the hope that it would be possible to move on to more advanced constructions (e.g. state channels). However, now that I look at all the options, it seems that multi-party state channels in Bitcoin are (probably) impossible to construct. diff --git a/side-node/src/bitcoin/driver.rs b/side-node/src/bitcoin/driver.rs index e5e69fc..3286043 100644 --- a/side-node/src/bitcoin/driver.rs +++ b/side-node/src/bitcoin/driver.rs @@ -10,7 +10,9 @@ use crate::bitcoin::clients; /// /// Also, it very handily works with the mutinynet.com esplora server, which is configured /// with 30 second block times. -pub(crate) async fn run() -> Result<(), anyhow::Error> {} +pub(crate) async fn run() -> Result<(), anyhow::Error> { + simple_transfer().await +} async fn simple_transfer() -> Result<(), anyhow::Error> { let mut dave = clients::esplora::create_wallet("dave", Network::Signet)?; From ba585d0888aa439de35d296ec5a0c8186d973675 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 26 Jun 2024 18:12:52 +0100 Subject: [PATCH 55/55] Noted the (dis-)use of Electrum client --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0e4ce90..45d7356 100644 --- a/README.md +++ b/README.md @@ -73,3 +73,5 @@ cargo run -- btc You'll need to have funded the "dave" address prior to running the `btc` command - otherwise the transfer will fail gracefully. I was using this primarily as a way to experiment with constructing and broadcasting Bitcoin transactions, with the hope that it would be possible to move on to more advanced constructions (e.g. state channels). However, now that I look at all the options, it seems that multi-party state channels in Bitcoin are (probably) impossible to construct. + +There is a second, unused Bitcoin client in place which uses Blockstream's Electrum server, but this didn't seem to be working properly with respect to Signet Bitcoin network during my testing, so I went with the esplora / Mutiny version instead.