From 86dd0888699a4e29a47bf112d7ddefe7c5a3ceff Mon Sep 17 00:00:00 2001 From: ngutech21 Date: Sun, 25 Feb 2024 15:16:54 +0100 Subject: [PATCH] feat: generate deterministic secrets --- Cargo.lock | 222 +++++++++++++++++++-- moksha-cli/src/main.rs | 42 +++- moksha-wallet/Cargo.toml | 1 + moksha-wallet/src/deterministic_secrets.rs | 47 +++++ moksha-wallet/src/error.rs | 7 + moksha-wallet/src/lib.rs | 1 + 6 files changed, 293 insertions(+), 27 deletions(-) create mode 100644 moksha-wallet/src/deterministic_secrets.rs diff --git a/Cargo.lock b/Cargo.lock index 5b3c9109..56003be0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,7 +208,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 0.1.2", "tower", "tower-layer", "tower-service", @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core 0.4.3", @@ -240,7 +240,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.0", "tokio", "tower", "tower-layer", @@ -280,7 +280,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", "tracing", @@ -325,6 +325,39 @@ 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" @@ -337,6 +370,17 @@ version = "0.10.0-beta" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" +[[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" @@ -354,6 +398,7 @@ 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", @@ -672,6 +717,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" @@ -873,6 +927,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" @@ -1001,6 +1074,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" @@ -1051,7 +1134,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot", + "parking_lot 0.12.1", ] [[package]] @@ -1101,6 +1184,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" @@ -1542,13 +1634,22 @@ 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 = "integrationtests" version = "0.2.1" dependencies = [ "anyhow", "assert_cmd", - "axum 0.7.4", + "axum 0.7.5", "bitcoin_hashes 0.12.0", "bitcoincore-rpc", "fedimint-tonic-lnd", @@ -1656,7 +1757,7 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.5.0", "libc", - "redox_syscall", + "redox_syscall 0.4.1", ] [[package]] @@ -1791,6 +1892,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" @@ -1880,7 +1992,7 @@ version = "0.2.1" dependencies = [ "anyhow", "async-trait", - "axum 0.7.4", + "axum 0.7.5", "chrono", "clap", "cln-grpc", @@ -1923,6 +2035,7 @@ version = "0.2.1" dependencies = [ "anyhow", "async-trait", + "bdk", "dirs", "gloo-net", "lightning-invoice 0.29.0", @@ -2166,6 +2279,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" @@ -2173,7 +2297,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]] @@ -2184,7 +2322,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -2340,9 +2478,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", "syn 2.0.55", @@ -2436,7 +2574,7 @@ dependencies = [ "multimap", "once_cell", "petgraph", - "prettyplease 0.2.16", + "prettyplease 0.2.17", "prost 0.12.3", "prost-types 0.12.3", "regex", @@ -2528,6 +2666,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" @@ -2617,7 +2764,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tower-service", @@ -2657,7 +2804,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "tokio", "tokio-rustls 0.25.0", "tokio-socks", @@ -3141,6 +3288,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" @@ -3451,6 +3614,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3603,7 +3772,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", @@ -3811,7 +3980,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ - "prettyplease 0.2.16", + "prettyplease 0.2.17", "proc-macro2", "prost-build 0.12.3", "quote", @@ -4013,9 +4182,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", ] @@ -4104,7 +4273,7 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b39868d43c011961e04b41623e050aedf2cc93652562ff7935ce0f819aaf2da" dependencies = [ - "axum 0.7.4", + "axum 0.7.5", "mime_guess", "regex", "rust-embed", @@ -4278,6 +4447,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" @@ -4311,7 +4489,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "redox_syscall", + "redox_syscall 0.4.1", "wasite", ] diff --git a/moksha-cli/src/main.rs b/moksha-cli/src/main.rs index bb7d232f..70700298 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 3a4b74d1..8f42eeb7 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 9075fa04..7117ad29 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; @@ -62,4 +63,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;