]> Untitled Git - bitcoindevkit.org/commitdiff
Make getting_started_with_rust_hwi.md a blog post
authorDaniela Brozzoni <danielabrozzoni@protonmail.com>
Thu, 27 Oct 2022 15:52:23 +0000 (17:52 +0200)
committerDaniela Brozzoni <danielabrozzoni@protonmail.com>
Mon, 14 Nov 2022 17:18:05 +0000 (18:18 +0100)
It was previously a tutorial, but, since it goes quite in deep
into the rust-hwi internals, it's better suited as a blog post.

docs/.vuepress/config.js
docs/_blog/getting_started_with_rust_hwi.md [new file with mode: 0644]
docs/tutorials/getting_started_with_rust_hwi.md [deleted file]

index 12a846bdb1a1b9d69956b11146dc0069384f8516..4a682b9eccf712c1af7cb060c7fe96906ad1b86b 100644 (file)
@@ -56,7 +56,6 @@ const tutorialSidebar = [
       '/tutorials/descriptor_based_paper_wallet',
       '/tutorials/spending_policy_demo',
       '/tutorials/exploring_bdk_rn',
-      '/tutorials/getting_started_with_rust_hwi'
     ],
   }
 ]
diff --git a/docs/_blog/getting_started_with_rust_hwi.md b/docs/_blog/getting_started_with_rust_hwi.md
new file mode 100644 (file)
index 0000000..7ea7298
--- /dev/null
@@ -0,0 +1,152 @@
+---
+title: "Getting Started with rust-hwi"
+description: "This post will help one understand and develop for hardware wallets using BDK"
+authors:
+    - Wszdexdrf
+date: "2022-08-16"
+tags: ["BDK", "Development", "Hardware Wallets"]
+draft: false
+---
+
+[bitcoindevkit/rust-hwi](https://github.com/bitcoindevkit/rust-hwi) is a sub-project for [bitcoindevkit](https://bitcoindevkit.org/) (BDK) which is used to interact with hardware wallets using the Rust programming language. It is a wrapper around [bitcoin-core/HWI](https://github.com/bitcoin-core/HWI) and its behaviour is closely linked with the same.
+
+## Fundamentals
+
+As mentioned before, rust-hwi is a wrapper around bitcoin-core/HWI. The functions in it, when called, pass on the arguments to related functions in bitcoin-core/HWI. More information about the functions and their arguments is available on rust-hwi [docs](https://docs.rs/hwi/latest/hwi/index.html) and bitcoin-core/HWI [docs](https://hwi.readthedocs.io/en/stable/).
+
+rust-hwi uses `PyO3` to call the Python functions from Rust. Let us take an example from the documentation:
+
+```rust
+use bitcoin::util::bip32::{ChildNumber, DerivationPath};
+use hwi::error::Error;
+use hwi::interface::HWIClient;
+use hwi::types;
+use std::str::FromStr;
+
+fn main() -> Result<(), Error> {
+    // Find information about devices
+    let devices = HWIClient::enumerate()?;
+    let device = devices.first().expect("No devices");
+    // Create a client for a device
+    let client = HWIClient::get_client(&device, true, types::HWIChain::Test)?;
+    // Display the address from path
+    let derivation_path = DerivationPath::from_str("m/44'/1'/0'/0/0")?;
+    let hwi_address =
+        client.display_address_with_path(&derivation_path, types::HWIAddressType::Tap)?;
+    println!("{}", hwi_address.address);
+    Ok(())
+}
+```
+
+In the first line, we call `HWIClient::enumerate()`. This function is equivalent to HWI's `enumerate` function, which returns a list of connected devices. Here, `HWIClient::enumerate()` returns a `Vec<HWIDevice>`, where `HWIDevice` is a struct representing a single device and contains all the information related to it, for example, fingerprint, path, etc.
+
+Then we store the first device available into `device`, which is straightforward. We then call `HWIClient::get_client()` and pass the reference to the device info and the Chain we are going to use that device on. (the boolean is for setting `expert` mode, which allows for some more functions and detailed information. That is implemented in bitcoin-core/HWI, see the [docs](https://hwi.readthedocs.io/en/latest/usage/cli-usage.html#cmdoption-hwi-expert)) There are 4 chains available, as usual, Main, Test, Regtest & Signet.
+
+HWI contains a python base class known as `HardwareWalletClient`. All the functions and their arguments are defined in it. Hardware Wallet developers create their own implementations of the base class. The function `get_client()` returns an instance of `HWIClient` struct, which contains a reference to the Python object of `HardwareWalletClient` (and also a reference to the Python code of HWI itself). rust-hwi's `HWIClient` struct tries to mimic the base class `HardwareWalletClient` and thus all the functions used for communicating with a hardware wallet belong to `HWIClient`.
+
+In the next line, we generate a derivation path and use the client instance we created to get an address for the aforementioned path. We then print out the address and return an `Ok`.
+
+## Integration with BDK
+
+BDK is an amazing project. It is one of the easiest ways to integrate Bitcoin wallet features into any application. rust-hwi aims to help BDK to work with hardware wallets. One of the ways to do so is to implement a Custom Signer.
+
+Let us look at a basic example from BDK's docs:
+
+```rust
+use bdk::{FeeRate, Wallet, SyncOptions, SignOptions};
+use bdk::database::MemoryDatabase;
+use bdk::blockchain::ElectrumBlockchain;
+use bdk::electrum_client::Client;
+
+use bdk::wallet::AddressIndex::New;
+
+fn main() -> Result<(), bdk::Error> {
+    let client = Client::new("ssl://electrum.blockstream.info:60002")?;
+    let wallet = Wallet::new(
+        "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
+        Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
+        bdk::bitcoin::Network::Testnet,
+        MemoryDatabase::default(),
+    )?;
+    let blockchain = ElectrumBlockchain::from(client);
+
+    wallet.sync(&blockchain, SyncOptions::default())?;
+
+    let send_to = wallet.get_address(New)?;
+    let (mut psbt, details) = {
+        let mut builder =  wallet.build_tx();
+        builder
+            .add_recipient(send_to.script_pubkey(), 50_000)
+            .enable_rbf()
+            .do_not_spend_change()
+            .fee_rate(FeeRate::from_sat_per_vb(5.0));
+        builder.finish()?
+    };
+
+    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
+
+    Ok(())
+}
+```
+
+This creates a wallet instance with the given descriptors and uses an electrum backend to sync the wallet. It then creates a new transaction and then signs it using the same wallet.
+
+If we were to do this using a hardware wallet, how would we do this?
+
+First, we create a client instance of the device.
+
+```rust
+let devices = HWIClient::enumerate().unwrap();
+let client = HWIClient::get_client(
+    devices
+        .first()
+        .expect("No devices found. Either plug in a hardware wallet, or start a simulator."),
+    true,
+    types::HWIChain::Test,
+)
+.unwrap();
+```
+
+We would then need a descriptor from the device for BDK.
+
+```rust
+let descriptors = client.get_descriptors(None).unwrap();
+```
+
+We would now need to create an instance of a custom signer. A basic version is provided in `bdk/wallet/hardwaresigner/HWISigner`. The basic signer has no extra features, it just takes a psbt and simply hands it over to the hardware wallet.
+
+```rust
+let custom_signer = HWISigner::from_device(devices.first().unwrap(), types::HWIChain::Test).unwrap();
+```
+
+We now create a wallet instance using the descriptor from the hardware wallet and add the custom signer.
+
+```rust
+let mut wallet = Wallet::new(
+    &descriptors.internal[0],
+    Some(&descriptors.receive[0]),
+    Network::Testnet,
+    MemoryDatabase::default(),
+)?;
+wallet.add_signer(
+    KeychainKind::External,
+    SignerOrdering(200),
+    Arc::new(custom_signer),
+);
+```
+
+The rest of the PSBT signing process remains the same!
+
+```rust
+let client = bdk::electrum_client::Client::new("ssl://electrum.blockstream.info:60002")?;
+let blockchain = bdk::blockchain::ElectrumBlockchain::from(client);
+wallet.sync(&blockchain, bdk::SyncOptions::default())?;
+
+let send_to = wallet.get_address(New)?;
+let mut tx_builder = wallet.build_tx();
+tx_builder
+    .add_recipient(send_to.script_pubkey(), 50_000)
+    .enable_rbf();
+let (mut psbt, _tx_details) = tx_builder.finish()?;
+let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
+```
diff --git a/docs/tutorials/getting_started_with_rust_hwi.md b/docs/tutorials/getting_started_with_rust_hwi.md
deleted file mode 100644 (file)
index e57f6aa..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
----
-title: "Getting Started with rust-hwi"
-description: "This post will help one understand and develop for hardware wallets using BDK"
-authors:
-    - Wszdexdrf
-date: "2022-08-16"
-tags: ["BDK", "Development", "Hardware Wallets"]
-hidden: true
-draft: false
----
-
-## Introduction
-
-[bitcoindevkit/rust-hwi](https://github.com/bitcoindevkit/rust-hwi) is a sub-project for [bitcoindevkit](https://bitcoindevkit.org/) (BDK) which is used to interact with hardware wallets using the Rust programming language. It is a wrapper around [bitcoin-core/HWI](https://github.com/bitcoin-core/HWI) and its behaviour is closely linked with the same.
-
-## Fundamentals
-
-As mentioned before, rust-hwi is a wrapper around bitcoin-core/HWI. The functions in it, when called, pass on the arguments to related functions in bitcoin-core/HWI. More information about the functions and their arguments is available on rust-hwi [docs](https://docs.rs/hwi/latest/hwi/index.html) and bitcoin-core/HWI [docs](https://hwi.readthedocs.io/en/stable/).
-
-rust-hwi uses `PyO3` to call the Python functions from Rust. Let us take an example from the documentation:
-
-```rust
-use bitcoin::util::bip32::{ChildNumber, DerivationPath};
-use hwi::error::Error;
-use hwi::interface::HWIClient;
-use hwi::types;
-use std::str::FromStr;
-
-fn main() -> Result<(), Error> {
-    // Find information about devices
-    let devices = HWIClient::enumerate()?;
-    let device = devices.first().expect("No devices");
-    // Create a client for a device
-    let client = HWIClient::get_client(&device, true, types::HWIChain::Test)?;
-    // Display the address from path
-    let derivation_path = DerivationPath::from_str("m/44'/1'/0'/0/0")?;
-    let hwi_address =
-        client.display_address_with_path(&derivation_path, types::HWIAddressType::Tap)?;
-    println!("{}", hwi_address.address);
-    Ok(())
-}
-```
-
-In the first line, we call `HWIClient::enumerate()`. This function is equivalent to HWI's `enumerate` function, which returns a list of connected devices. Here, `HWIClient::enumerate()` returns a `Vec<HWIDevice>`, where `HWIDevice` is a struct representing a single device and contains all the information related to it, for example, fingerprint, path, etc.
-
-Then we store the first device available into `device`, which is straightforward. We then call `HWIClient::get_client()` and pass the reference to the device info and the Chain we are going to use that device on. (the boolean is for setting `expert` mode, which allows for some more functions and detailed information. That is implemented in bitcoin-core/HWI, see the [docs](https://hwi.readthedocs.io/en/latest/usage/cli-usage.html#cmdoption-hwi-expert)) There are 4 chains available, as usual, Main, Test, Regtest & Signet.
-
-HWI contains a python base class known as `HardwareWalletClient`. All the functions and their arguments are defined in it. Hardware Wallet developers create their own implementations of the base class. The function `get_client()` returns an instance of `HWIClient` struct, which contains a reference to the Python object of `HardwareWalletClient` (and also a reference to the Python code of HWI itself). rust-hwi's `HWIClient` struct tries to mimic the base class `HardwareWalletClient` and thus all the functions used for communicating with a hardware wallet belong to `HWIClient`.
-
-In the next line, we generate a derivation path and use the client instance we created to get an address for the aforementioned path. We then print out the address and return an `Ok`.
-
-## Integration with BDK
-
-BDK is an amazing project. It is one of the easiest ways to integrate Bitcoin wallet features into any application. rust-hwi aims to help BDK to work with hardware wallets. One of the ways to do so is to implement a Custom Signer.
-
-Let us look at a basic example from BDK's docs:
-
-```rust
-use bdk::{FeeRate, Wallet, SyncOptions, SignOptions};
-use bdk::database::MemoryDatabase;
-use bdk::blockchain::ElectrumBlockchain;
-use bdk::electrum_client::Client;
-
-use bdk::wallet::AddressIndex::New;
-
-fn main() -> Result<(), bdk::Error> {
-    let client = Client::new("ssl://electrum.blockstream.info:60002")?;
-    let wallet = Wallet::new(
-        "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
-        Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
-        bdk::bitcoin::Network::Testnet,
-        MemoryDatabase::default(),
-    )?;
-    let blockchain = ElectrumBlockchain::from(client);
-
-    wallet.sync(&blockchain, SyncOptions::default())?;
-
-    let send_to = wallet.get_address(New)?;
-    let (mut psbt, details) = {
-        let mut builder =  wallet.build_tx();
-        builder
-            .add_recipient(send_to.script_pubkey(), 50_000)
-            .enable_rbf()
-            .do_not_spend_change()
-            .fee_rate(FeeRate::from_sat_per_vb(5.0));
-        builder.finish()?
-    };
-
-    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-
-    Ok(())
-}
-```
-
-This creates a wallet instance with the given descriptors and uses an electrum backend to sync the wallet. It then creates a new transaction and then signs it using the same wallet.
-
-If we were to do this using a hardware wallet, how would we do this?
-
-First, we create a client instance of the device.
-
-```rust
-let devices = HWIClient::enumerate().unwrap();
-let client = HWIClient::get_client(
-    devices
-        .first()
-        .expect("No devices found. Either plug in a hardware wallet, or start a simulator."),
-    true,
-    types::HWIChain::Test,
-)
-.unwrap();
-```
-
-We would then need a descriptor from the device for BDK.
-
-```rust
-let descriptors = client.get_descriptors(None).unwrap();
-```
-
-We would now need to create an instance of a custom signer. A basic version is provided in `bdk/wallet/hardwaresigner/HWISigner`. The basic signer has no extra features, it just takes a psbt and simply hands it over to the hardware wallet.
-
-```rust
-let custom_signer = HWISigner::from_device(devices.first().unwrap(), types::HWIChain::Test).unwrap();
-```
-
-We now create a wallet instance using the descriptor from the hardware wallet and add the custom signer.
-
-```rust
-let mut wallet = Wallet::new(
-    &descriptors.internal[0],
-    Some(&descriptors.receive[0]),
-    Network::Testnet,
-    MemoryDatabase::default(),
-)?;
-wallet.add_signer(
-    KeychainKind::External,
-    SignerOrdering(200),
-    Arc::new(custom_signer),
-);
-```
-
-The rest of the PSBT signing process remains the same!
-
-```rust
-let client = bdk::electrum_client::Client::new("ssl://electrum.blockstream.info:60002")?;
-let blockchain = bdk::blockchain::ElectrumBlockchain::from(client);
-wallet.sync(&blockchain, bdk::SyncOptions::default())?;
-
-let send_to = wallet.get_address(New)?;
-let mut tx_builder = wallet.build_tx();
-tx_builder
-    .add_recipient(send_to.script_pubkey(), 50_000)
-    .enable_rbf();
-let (mut psbt, _tx_details) = tx_builder.finish()?;
-let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-```