From 413c4cfe5d0c9fa6237d6794b8a92c68063a6f07 Mon Sep 17 00:00:00 2001 From: River Kanies Date: Mon, 21 Oct 2024 06:59:08 -0400 Subject: [PATCH] feat: delete basics.md, wip new full-wallet example --- .gitignore | 7 +- .vscode/settings.json | 3 +- docs/cookbook/basics.md | 193 ----------------------- docs/cookbook/starter.md | 2 +- examples/README.md | 4 + examples/kotlin/full-wallet/build.gradle | 10 ++ examples/kotlin/full-wallet/main.kt | 46 ++++++ examples/rust/full-wallet/Cargo.toml | 8 + examples/rust/full-wallet/src/main.rs | 73 +++++++++ examples/rust/quickstart/src/main.rs | 13 +- examples/swift/full-wallet/main.swift | 49 ++++++ mkdocs.yml | 1 - 12 files changed, 202 insertions(+), 207 deletions(-) delete mode 100644 docs/cookbook/basics.md create mode 100644 examples/kotlin/full-wallet/build.gradle create mode 100644 examples/kotlin/full-wallet/main.kt create mode 100644 examples/rust/full-wallet/Cargo.toml create mode 100644 examples/rust/full-wallet/src/main.rs create mode 100644 examples/swift/full-wallet/main.swift diff --git a/.gitignore b/.gitignore index 020e9a8..861e352 100644 --- a/.gitignore +++ b/.gitignore @@ -8,11 +8,8 @@ drafts/ target/ *Cargo.lock -# Tests -tests/Cargo.lock -tests/target -tests/src -tests/config.txt +# MacOS +*.DS_Store # Python virtual environments venv/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 97dfafb..30349ec 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "rust-analyzer.linkedProjects": [ "examples/descriptors/Cargo.toml", - "examples/rust/quickstart/Cargo.toml" + "examples/rust/quickstart/Cargo.toml", + "examples/rust/full-wallet/Cargo.toml" ] } \ No newline at end of file diff --git a/docs/cookbook/basics.md b/docs/cookbook/basics.md deleted file mode 100644 index a6519c5..0000000 --- a/docs/cookbook/basics.md +++ /dev/null @@ -1,193 +0,0 @@ -# Basics - -!!! tip - This page is up-to-date with version `1.0.0-beta.1` of `bdk_wallet`. - -Add BDK - -=== "Rust" - - ``` toml - [dependencies] - bdk_wallet = { version = "1.0.0-beta.1", features = ["keys-bip39"] } - bdk_esplora = { version = "0.16.0" , features = ["blocking"] } - ``` - -=== "Swift" - - 1. From the Xcode File menu, select Add Package Dependencies... - 2. Enter `https://github.com/bitcoindevkit/bdk-swift` into the package repository URL text field - -=== "Kotlin" - - ``` kotlin - repositories { - mavenCentral() - } - - dependencies { - - // for jvm - implementation 'org.bitcoindevkit:bdk-jvm:' - // OR for android - implementation 'org.bitcoindevkit:bdk-android:' - - } - ``` - -Create Wallet Mnemonic - -=== "Rust" - - ``` toml - let mnemonic: GeneratedKey<_, Tap> = - Mnemonic::generate((WordCount::Words12, Language::English)) - .expect("Failed to generate mnemonic"); - ``` - -=== "Swift" - - ``` swift - let mnemonic = try Mnemonic(wordCount: .words12) - ``` - -=== "Kotlin" - - ``` kotlin - val mnemonic = Mnemonic(WordCount.WORDS12) - ``` - -Create Wallet - -=== "Rust" - - ``` toml - let seed = mnemonic.to_seed(""); - let xprv: Xpriv = - Xpriv::new_master(Network::Signet, &seed).expect("Failed to create master key"); - let (descriptor, key_map, _) = Bip86(xprv, KeychainKind::External) - .build(Network::Signet) - .expect("Failed to build external descriptor"); - let (change_descriptor, change_key_map, _) = Bip86(xprv, KeychainKind::Internal) - .build(Network::Signet) - .expect("Failed to build internal descriptor"); - let mut wallet = Wallet::create(descriptor, change_descriptor) - .network(Network::Signet) - .create_wallet_no_persist() - .expect("Failed to create wallet"); - ``` - -=== "Swift" - - ``` swift - let secretKey = DescriptorSecretKey( - network: Network.signet, - mnemonic: mnemonic, - password: nil - ) - let descriptor = Descriptor.newBip86( - secretKey: secretKey, - keychain: .external, - network: Network.signet - ) - let changeDescriptor = Descriptor.newBip86( - secretKey: secretKey, - keychain: .internal, - network: Network.signet - ) - let wallet = try Wallet( - descriptor: descriptor, - changeDescriptor: changeDescriptor, - network: Network.signet - ) - ``` - -=== "Kotlin" - - ``` kotlin - val secretKey = DescriptorSecretKey( - Network.SIGNET, - mnemonic, - null - ) - val descriptor = Descriptor.newBip86( - descriptorSecretKey, - KeychainKind.EXTERNAL, - Network.SIGNET - ) - val changeDescriptor = Descriptor.newBip86( - secretKey, - KeychainKind.INTERNAL, - Network.SIGNET - ) - val wallet = Wallet( - descriptor, - changeDescriptor, - Network.SIGNET - ) - ``` - -Get Address - -=== "Rust" - - ``` toml - let address_info = wallet.reveal_next_address(KeychainKind::External); - ``` - -=== "Swift" - - ``` swift - let addressInfo = wallet.revealNextAddress(keychain: .external) - ``` - -=== "Kotlin" - - ``` kotlin - val addressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL) - ``` - -Get Balance - -=== "Rust" - - ``` toml - let client = esplora_client::Builder::new("https://mutinynet.com/api").build_blocking(); - let request = wallet.start_full_scan(); - let mut update = client - .full_scan(request, 5, 5) - .expect("Failed to perform full scan"); - let now = UNIX_EPOCH - .elapsed() - .expect("Failed to get current time") - .as_secs(); - let _ = update.graph_update.update_last_seen_unconfirmed(now); - wallet.apply_update(update).expect("Failed to apply update"); - let balance = wallet.balance(); - ``` - -=== "Swift" - - ``` swift - let esploraClient = EsploraClient(url: "https://mutinynet.com/api") - let syncRequest = wallet.startSyncWithRevealedSpks() - let update = try esploraClient.sync( - syncRequest: syncRequest, - parallelRequests: UInt64(5) - ) - try wallet.applyUpdate(update: update) - let balance = wallet.balance() - ``` - -=== "Kotlin" - - ``` kotlin - val esploraClient: EsploraClient = EsploraClient("https://mutinynet.com/api") - val syncRequest = wallet.startSyncWithRevealedSpks() - val update = try esploraClient.sync( - syncRequest, - 5uL - ) - wallet.applyUpdate(update) - val balance = wallet.balance() - ``` \ No newline at end of file diff --git a/docs/cookbook/starter.md b/docs/cookbook/starter.md index d16ff37..925c30a 100644 --- a/docs/cookbook/starter.md +++ b/docs/cookbook/starter.md @@ -114,7 +114,7 @@ First we need some BIP86. The `EXTERNAL_DESCRIPTOR` is an HD wallet with a path for generating addresses to give out externally for payment. We also have a second `INTERNAL_DESCRIPTOR` that we can use to generate addresses to pay ourseves change when sending payments (remeber that UTXOs must be spent if full, so you often want to make change). +These are taproot `tr()` descriptors using a public key on testnet (or signet) `tpub` as described in BIP86. The `descriptor` is an HD wallet with a path for generating addresses to give out externally for payment. We also have a second `change_descriptor` that we can use to generate addresses to pay ourseves change when sending payments (remeber that UTXOs must be spent if full, so you often want to make change). ## Blockchain Client and Network diff --git a/examples/README.md b/examples/README.md index 5275f67..d1dd549 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,3 +18,7 @@ Alternatively, you can use cargo to run the examples directly using something li cd syncing/ cargo run --bin ``` + + +### rust-analyzer vscode extention +To enable rust-analyzer features for a rust example in the project you must add the path to the `Cargo.toml` file for the example to `.vscode/settings.json` \ No newline at end of file diff --git a/examples/kotlin/full-wallet/build.gradle b/examples/kotlin/full-wallet/build.gradle new file mode 100644 index 0000000..c35fc9f --- /dev/null +++ b/examples/kotlin/full-wallet/build.gradle @@ -0,0 +1,10 @@ +repositories { + mavenCentral() +} + +dependencies { + // for jvm + implementation 'org.bitcoindevkit:bdk-jvm:' + // OR for android + implementation 'org.bitcoindevkit:bdk-android:' +} \ No newline at end of file diff --git a/examples/kotlin/full-wallet/main.kt b/examples/kotlin/full-wallet/main.kt new file mode 100644 index 0000000..bf78afc --- /dev/null +++ b/examples/kotlin/full-wallet/main.kt @@ -0,0 +1,46 @@ +// --8<-- [start:file] + +val mnemonic = Mnemonic(WordCount.WORDS12) +val secretKey = DescriptorSecretKey( + Network.SIGNET, + mnemonic, + null +) + +val descriptor = Descriptor.newBip86( + descriptorSecretKey, + KeychainKind.EXTERNAL, + Network.SIGNET +) +val changeDescriptor = Descriptor.newBip86( + secretKey, + KeychainKind.INTERNAL, + Network.SIGNET +) + +val wallet = Wallet( + descriptor, + changeDescriptor, + Network.SIGNET +) + +val addressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL) + + +// --8<-- [start:client] +val esploraClient: EsploraClient = EsploraClient("https://mutinynet.com/api") +// --8<-- [end:client] + +// --8<-- [start:scan] +val syncRequest = wallet.startSyncWithRevealedSpks() +val update = try esploraClient.sync( + syncRequest, + 5uL +) +wallet.applyUpdate(update) +val balance = wallet.balance() +// --8<-- [end:scan] + +// TODO: tx build + broadcast, recovery, storage, etc. + +// --8<-- [end:file] \ No newline at end of file diff --git a/examples/rust/full-wallet/Cargo.toml b/examples/rust/full-wallet/Cargo.toml new file mode 100644 index 0000000..b81818b --- /dev/null +++ b/examples/rust/full-wallet/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "full-wallet" +version = "0.1.0" +edition = "2021" + +[dependencies] +bdk_wallet = { version = "=1.0.0-beta.5", features = ["keys-bip39"] } +bdk_esplora = { version = "=0.19.0", features = ["blocking"] } diff --git a/examples/rust/full-wallet/src/main.rs b/examples/rust/full-wallet/src/main.rs new file mode 100644 index 0000000..eba018f --- /dev/null +++ b/examples/rust/full-wallet/src/main.rs @@ -0,0 +1,73 @@ +// detailed documentation for this code can be found at https://bitcoindevkit.github.io/book-of-bdk/cookbook/starter/ +// --8<-- [start:file] +use bdk_wallet::AddressInfo; +use bdk_wallet::KeychainKind; +use bdk_wallet::bitcoin::Network; +use bdk_wallet::Wallet; +use bdk_esplora::EsploraExt; +use bdk_esplora::esplora_client::Builder; +use bdk_esplora::esplora_client; +use bdk_wallet::chain::spk_client::{FullScanRequestBuilder, FullScanResult}; +use bdk_wallet::keys::bip39::{Language, Mnemonic, WordCount}; +use bdk_wallet::keys::{GeneratedKey, GeneratableKey}; +use bdk_wallet::miniscript::Tap; +use bdk_wallet::bitcoin::bip32::Xpriv; +use bdk_wallet::template::{Bip86, DescriptorTemplate}; + +const STOP_GAP: usize = 50; +const PARALLEL_REQUESTS: usize = 1; + + +fn main() -> () { + + let mnemonic: GeneratedKey<_, Tap> = + Mnemonic::generate((WordCount::Words12, Language::English)) + .expect("Failed to generate mnemonic"); + println!("generated Seed Words:"); + println!("{}", mnemonic.to_string()); + println!("save these to recover your wallet later"); + + let seed = mnemonic.to_seed(""); + let xprv: Xpriv = + Xpriv::new_master(Network::Signet, &seed).expect("Failed to create master key"); + println!("created Master Private Key:"); + println!("{}", xprv); + + let (descriptor, _key_map, _) = Bip86(xprv, KeychainKind::External) + .build(Network::Signet) + .expect("Failed to build external descriptor"); + println!("external descriptor: {}", descriptor); + + let (change_descriptor, _change_key_map, _) = Bip86(xprv, KeychainKind::Internal) + .build(Network::Signet) + .expect("Failed to build internal descriptor"); + println!("internal descriptor: {}", change_descriptor); + + // Create the wallet + let mut wallet: Wallet = Wallet::create(descriptor, change_descriptor) + .network(Network::Signet) + .create_wallet_no_persist() + .unwrap(); + + // Reveal a new address from your external keychain + // doing this just to show it is an HD wallet + let address: AddressInfo = wallet.reveal_next_address(KeychainKind::External); + println!("Generated address {} at index {}", address.address, address.index); + + // Sync the wallet + // --8<-- [start:client] + let client: esplora_client::BlockingClient = Builder::new("http://signet.bitcoindevkit.net").build_blocking(); + // --8<-- [end:client] + + // --8<-- [start:scan] + let full_scan_request: FullScanRequestBuilder = wallet.start_full_scan(); + let update: FullScanResult = client.full_scan(full_scan_request, STOP_GAP, PARALLEL_REQUESTS).unwrap(); + // Apply the update from the full scan to the wallet + wallet.apply_update(update).unwrap(); + let balance = wallet.balance(); + println!("Wallet balance: {} sat", balance.total().to_sat()); + // --8<-- [end:scan] + + // TODO: tx build + broadcast, recovery, storage, etc. +} +// --8<-- [end:file] \ No newline at end of file diff --git a/examples/rust/quickstart/src/main.rs b/examples/rust/quickstart/src/main.rs index bb6bdad..9e47d78 100644 --- a/examples/rust/quickstart/src/main.rs +++ b/examples/rust/quickstart/src/main.rs @@ -12,14 +12,15 @@ use bdk_wallet::chain::spk_client::{FullScanRequestBuilder, FullScanResult}; const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 1; -// --8<-- [start:descriptors] -const EXTERNAL_DESCRIPTOR: &str = "tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/0/*)#z3x5097m"; -const INTERNAL_DESCRIPTOR: &str = "tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/1/*)#n9r4jswr"; -// --8<-- [end:descriptors] - fn main() -> () { + + // --8<-- [start:descriptors] + let descriptor: &str = "tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/0/*)#z3x5097m"; + let change_descriptor: &str = "tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/1/*)#n9r4jswr"; + // --8<-- [end:descriptors] + // Create the wallet - let mut wallet: Wallet = Wallet::create(EXTERNAL_DESCRIPTOR, INTERNAL_DESCRIPTOR) + let mut wallet: Wallet = Wallet::create(descriptor, change_descriptor) .network(Network::Signet) .create_wallet_no_persist() .unwrap(); diff --git a/examples/swift/full-wallet/main.swift b/examples/swift/full-wallet/main.swift new file mode 100644 index 0000000..690865a --- /dev/null +++ b/examples/swift/full-wallet/main.swift @@ -0,0 +1,49 @@ +// --8<-- [start:file] + +let mnemonic = try Mnemonic(wordCount: .words12) +let secretKey = DescriptorSecretKey( + network: Network.signet, + mnemonic: mnemonic, + password: nil +) + +let descriptor = Descriptor.newBip86( + secretKey: secretKey, + keychain: .external, + network: Network.signet +) +let changeDescriptor = Descriptor.newBip86( + secretKey: secretKey, + keychain: .internal, + network: Network.signet +) + +let wallet = try Wallet( + descriptor: descriptor, + changeDescriptor: changeDescriptor, + network: Network.signet +) + +// --8<-- [start:address] +let addressInfo = wallet.revealNextAddress(keychain: .external) +print("Generated address \(addressInfo.address) at index \(addressInfo.index)") +// --8<-- [end:address] + +// --8<-- [start:client] +let esploraClient = EsploraClient(url: "https://mutinynet.com/api") +// --8<-- [end:client] + +// --8<-- [start:scan] +let syncRequest = wallet.startSyncWithRevealedSpks() +let update = try esploraClient.sync( + syncRequest: syncRequest, + parallelRequests: UInt64(5) +) +try wallet.applyUpdate(update: update) +let balance = wallet.balance() +print("Wallet balance: \(balance.total) sat") +// --8<-- [end:scan] + +// TODO: tx build + broadcast, recovery, storage, etc. + +// --8<-- [end:file] \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index f3b5e52..d31b029 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,7 +83,6 @@ nav: - Companion Codebase: getting-started/companion-code.md - Build a Wallet: getting-started/build-a-wallet.md - Cookbook: - - Basics: cookbook/basics.md - Starter Example: cookbook/starter.md - Syncing: - Full Scan vs Sync: cookbook/syncing/full-scan-vs-sync.md