diff --git a/Cargo.lock b/Cargo.lock index 4ccf03e2..ad9dae55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,12 +314,56 @@ 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", + "bip39", + "bitcoin 0.30.2", + "electrum-client", + "getrandom", + "js-sys", + "log", + "miniscript", + "rand", + "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 = "bech32" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[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.29.2" @@ -337,6 +381,7 @@ version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" dependencies = [ + "base64 0.13.1", "bech32", "bitcoin-private", "bitcoin_hashes 0.12.0", @@ -600,6 +645,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -789,6 +843,25 @@ dependencies = [ "serde", ] +[[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 0.21.10", + "serde", + "serde_json", + "webpki", + "webpki-roots 0.22.6", + "winapi", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -932,6 +1005,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[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" @@ -982,7 +1065,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot", + "parking_lot 0.12.1", ] [[package]] @@ -1032,6 +1115,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" @@ -1169,9 +1261,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" [[package]] name = "hex" @@ -1439,6 +1531,15 @@ dependencies = [ "serde", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "integer-encoding" version = "3.0.4" @@ -1522,7 +1623,7 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.4.2", "libc", - "redox_syscall", + "redox_syscall 0.4.1", ] [[package]] @@ -1657,6 +1758,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[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 = "miniz_oxide" version = "0.7.2" @@ -1806,6 +1918,7 @@ version = "0.2.1" dependencies = [ "anyhow", "async-trait", + "bdk", "dirs", "gloo-net", "lightning-invoice 0.29.0", @@ -2111,6 +2224,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[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.1" @@ -2118,7 +2242,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.9", +] + +[[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]] @@ -2129,7 +2267,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -2472,6 +2610,15 @@ dependencies = [ "getrandom", ] +[[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.4.1" @@ -2574,7 +2721,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] @@ -3049,6 +3196,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.1" @@ -3525,7 +3688,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", @@ -3904,9 +4067,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", ] @@ -4144,6 +4307,15 @@ dependencies = [ "untrusted 0.9.0", ] +[[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" diff --git a/moksha-cli/src/main.rs b/moksha-cli/src/main.rs index 1c75cb03..1b2e228a 100644 --- a/moksha-cli/src/main.rs +++ b/moksha-cli/src/main.rs @@ -4,6 +4,7 @@ use moksha_core::primitives::{ CurrencyUnit, PaymentMethod, PostMeltOnchainResponse, PostMintQuoteBolt11Response, PostMintQuoteOnchainResponse, }; +use moksha_wallet::deterministic_secrets; use moksha_wallet::http::CrossPlatformHttpClient; use num_format::{Locale, ToFormattedString}; use std::io::Write; @@ -26,25 +27,38 @@ struct Opts { #[derive(Subcommand, Clone)] enum Command { /// Mint tokens - Mint { amount: u64 }, + Mint { + amount: u64, + }, /// Pay Lightning invoice - Pay { invoice: String }, + Pay { + invoice: String, + }, /// Pay Bitcoin on chain - PayOnchain { address: String, amount: u64 }, + PayOnchain { + address: String, + amount: u64, + }, /// Send tokens - Send { amount: u64 }, + Send { + amount: u64, + }, /// Receive tokens - Receive { token: String }, + Receive { + token: String, + }, /// Show local balance Balance, /// Show version and configuration Info, + + Seed, } #[tokio::main] @@ -82,6 +96,24 @@ async fn main() -> anyhow::Result<()> { })?; match cli.command { + Command::Seed => { + let master = deterministic_secrets::generate_master_key().unwrap(); + let first = deterministic_secrets::derive_private_key( + master, + 0, + 0, + deterministic_secrets::DerivationType::Secret, + ); + println!("Seed: {}", master); + println!("first: {:?}", first); + let second = deterministic_secrets::derive_private_key( + master, + 0, + 1, + deterministic_secrets::DerivationType::Secret, + ); + println!("second: {:?}", second); + } Command::Info => { let wallet_version = env!("CARGO_PKG_VERSION"); println!( diff --git a/moksha-wallet/Cargo.toml b/moksha-wallet/Cargo.toml index 4394b6a6..8d30f366 100644 --- a/moksha-wallet/Cargo.toml +++ b/moksha-wallet/Cargo.toml @@ -24,6 +24,7 @@ async-trait = "0.1.77" lightning-invoice = "0.29.0" url = "2.5.0" dirs = "5.0.1" +bdk = { version = "0.29.0", features = ["all-keys"] } [target.'cfg(target_family = "wasm")'.dependencies] gloo-net = { version = "0.5.0" } diff --git a/moksha-wallet/src/deterministic_secrets.rs b/moksha-wallet/src/deterministic_secrets.rs new file mode 100644 index 00000000..d4c38c86 --- /dev/null +++ b/moksha-wallet/src/deterministic_secrets.rs @@ -0,0 +1,47 @@ +use bdk::bitcoin::bip32::{DerivationPath, ExtendedPrivKey}; +use bdk::bitcoin::secp256k1::SecretKey; +use bdk::bitcoin::Network; +use bdk::keys::GeneratableKey; +use bdk::{ + keys::{ + bip39::{Language, Mnemonic, WordCount}, + GeneratedKey, + }, + miniscript::Tap, +}; + +use std::str::FromStr; + +use crate::error::MokshaWalletError; + +pub fn generate_master_key() -> Result { + let mnemonic: GeneratedKey<_, Tap> = + Mnemonic::generate((WordCount::Words12, Language::English)) + .expect("Cannot generate mnemonic"); + Ok(ExtendedPrivKey::new_master( + Network::Bitcoin, + &mnemonic.to_seed_normalized(""), + )?) +} + +pub enum DerivationType { + Secret = 0, + Blinding = 1, +} + +pub fn derive_private_key( + master: ExtendedPrivKey, + keyset_id: u32, + counter: u32, + secret_or_blinding: DerivationType, +) -> Result { + let derivation_path = format!( + "m/129372'/{}'/{}'/{}", + keyset_id, counter, secret_or_blinding as u32 + ); + let derivation_path = DerivationPath::from_str(&derivation_path)?; + + Ok(master + .derive_priv(&bdk::bitcoin::secp256k1::Secp256k1::new(), &derivation_path)? + .private_key) +} diff --git a/moksha-wallet/src/error.rs b/moksha-wallet/src/error.rs index ef77cde0..ae4cc6d7 100644 --- a/moksha-wallet/src/error.rs +++ b/moksha-wallet/src/error.rs @@ -1,5 +1,6 @@ use std::string::FromUtf8Error; +use bdk::{bitcoin::bip32, keys::bip39}; use lightning_invoice::ParseOrSemanticError; use thiserror::Error; @@ -58,4 +59,10 @@ pub enum MokshaWalletError { #[error("Unsupported version: Only mints with /v1 api are supported")] UnsupportedApiVersion, + + #[error("Bip32Error {0}")] + Bip32(#[from] bip32::Error), + + #[error("Bip39Error {0}")] + Bip39(#[from] bip39::Error), } diff --git a/moksha-wallet/src/lib.rs b/moksha-wallet/src/lib.rs index 24618146..8365c943 100644 --- a/moksha-wallet/src/lib.rs +++ b/moksha-wallet/src/lib.rs @@ -1,6 +1,7 @@ pub mod btcprice; pub mod client; pub mod config_path; +pub mod deterministic_secrets; pub mod error; pub mod http; pub mod localstore;