Skip to content

Commit

Permalink
load wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
darioAnongba committed Jan 17, 2025
1 parent 1ea1199 commit 187660e
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 79 deletions.
26 changes: 20 additions & 6 deletions src/bitcoin/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use wasm_bindgen::{prelude::wasm_bindgen, JsError, JsValue};
use crate::{
result::JsResult,
types::{
AddressInfo, Balance, ChangeSet, CheckPoint, DescriptorPair, FeeRate, FullScanRequest, KeychainKind, Network,
Psbt, Recipient, SyncRequest, Update,
AddressInfo, Balance, ChangeSet, CheckPoint, FeeRate, FullScanRequest, KeychainKind, Network, Psbt, Recipient,
SyncRequest, Update,
},
};

Expand All @@ -17,16 +17,30 @@ pub struct Wallet(BdkWallet);

#[wasm_bindgen]
impl Wallet {
pub fn create(network: Network, descriptors: DescriptorPair) -> JsResult<Wallet> {
let wallet = BdkWallet::create(descriptors.external(), descriptors.internal())
pub fn create(network: Network, external_descriptor: String, internal_descriptor: String) -> JsResult<Wallet> {
let wallet = BdkWallet::create(external_descriptor, internal_descriptor)
.network(network.into())
.create_wallet_no_persist()?;

Ok(Wallet(wallet))
}

pub fn load(changeset: ChangeSet) -> JsResult<Wallet> {
let wallet_opt = BdkWallet::load().load_wallet_no_persist(changeset.into())?;
pub fn load(
changeset: ChangeSet,
external_descriptor: Option<String>,
internal_descriptor: Option<String>,
) -> JsResult<Wallet> {
let mut builder = BdkWallet::load();

if external_descriptor.is_some() {
builder = builder.descriptor(KeychainKind::External.into(), external_descriptor);
}

if internal_descriptor.is_some() {
builder = builder.descriptor(KeychainKind::Internal.into(), internal_descriptor);
}

let wallet_opt = builder.extract_keys().load_wallet_no_persist(changeset.into())?;

let wallet = match wallet_opt {
Some(wallet) => wallet,
Expand Down
29 changes: 0 additions & 29 deletions src/types/descriptor.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ mod block;
mod chain;
mod changeset;
mod checkpoint;
mod descriptor;
mod keychain;
mod network;
mod psbt;
Expand All @@ -19,7 +18,6 @@ pub use block::*;
pub use chain::*;
pub use changeset::*;
pub use checkpoint::*;
pub use descriptor::*;
pub use keychain::*;
pub use network::*;
pub use psbt::*;
Expand Down
30 changes: 29 additions & 1 deletion src/utils/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,39 @@ use bdk_wallet::keys::ExtendedKey;
use bitcoin::bip32::{Fingerprint, Xpriv, Xpub};
use serde_wasm_bindgen::from_value;

use crate::types::{AddressType, DescriptorPair, Network, SLIP10Node};
use crate::types::{AddressType, Network, SLIP10Node};
use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue};

use super::result::JsResult;

/// Pair of descriptors for external and internal keychains
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct DescriptorPair {
/// External descriptor
external: String,
/// Internal descriptor
internal: String,
}

#[wasm_bindgen]
impl DescriptorPair {
#[wasm_bindgen(constructor)]
pub fn new(external: String, internal: String) -> Self {
DescriptorPair { external, internal }
}

#[wasm_bindgen(getter)]
pub fn internal(&self) -> String {
self.internal.clone()
}

#[wasm_bindgen(getter)]
pub fn external(&self) -> String {
self.external.clone()
}
}

#[wasm_bindgen]
pub fn seed_to_descriptor(seed: &[u8], network: Network, address_type: AddressType) -> JsResult<DescriptorPair> {
let (external, internal) = crate::bitcoin::seed_to_descriptor(seed, network.into(), address_type.into())
Expand Down
64 changes: 25 additions & 39 deletions tests/esplora.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

extern crate wasm_bindgen_test;

use bdk_wallet::bip39::Mnemonic;
use bitcoindevkit::{
bitcoin::{EsploraClient, Wallet},
seed_to_descriptor, set_panic_hook,
types::{Address, AddressType, Amount, DescriptorPair, FeeRate, KeychainKind, Network, Recipient},
set_panic_hook,
types::{Address, Amount, FeeRate, KeychainKind, Network, Recipient},
};
use wasm_bindgen_test::*;

Expand All @@ -18,17 +17,18 @@ const ESPLORA_URL: &str = "https://mutinynet.com/api";
const STOP_GAP: usize = 5;
const PARALLEL_REQUESTS: usize = 1;
const NETWORK: Network = Network::Signet;
const EXTERNAL_DESC: &str = "wpkh([aafa6322/84'/1'/0']tpubDCfvzhCuifJtWDVdrBcPvZU7U5uyixL7QULk8hXA7KjqiNnry9Te1nwm7yStqenPCQhy5MwzxKkLBD2GmKNgvMYqXgo53iYqQ7Vu4vQbN2N/0/*)#mlua264t";
const INTERNAL_DESC: &str = "wpkh([aafa6322/84'/1'/0']tpubDCfvzhCuifJtWDVdrBcPvZU7U5uyixL7QULk8hXA7KjqiNnry9Te1nwm7yStqenPCQhy5MwzxKkLBD2GmKNgvMYqXgo53iYqQ7Vu4vQbN2N/1/*)#2teuh09n";
const SEND_ADMOUNT: u64 = 1000;
const FEE_RATE: u64 = 2;
const RECIPIENT_ADDRESS: &str = "tb1qd28npep0s8frcm3y7dxqajkcy2m40eysplyr9v";
const SEND_AMOUNT: u64 = 10000;

#[wasm_bindgen_test]
async fn test_esplora_client() {
set_panic_hook();

let mut wallet =
Wallet::create(NETWORK, DescriptorPair::new(EXTERNAL_DESC.into(), INTERNAL_DESC.into())).expect("wallet");
let external_desc = "wpkh(tprv8ZgxMBicQKsPf6vydw7ixvsLKY79hmeXujBkGCNCApyft92yVYng2y28JpFZcneBYTTHycWSRpokhHE25GfHPBxnW5GpSm2dMWzEi9xxEyU/84'/1'/0'/0/*)#uel0vg9p";
let internal_desc = "wpkh(tprv8ZgxMBicQKsPf6vydw7ixvsLKY79hmeXujBkGCNCApyft92yVYng2y28JpFZcneBYTTHycWSRpokhHE25GfHPBxnW5GpSm2dMWzEi9xxEyU/84'/1'/0'/1/*)#dd6w3a4e";

let mut wallet = Wallet::create(NETWORK, external_desc.into(), internal_desc.into()).expect("wallet");
let mut blockchain_client = EsploraClient::new(ESPLORA_URL).expect("esplora_client");

let block_height = wallet.latest_checkpoint().height();
Expand All @@ -54,44 +54,30 @@ async fn test_esplora_client() {
wallet.apply_update(update).expect("full_scan apply_update");

let balance = wallet.balance();
assert!(balance.total().to_sat() > 0);

let loaded_wallet = Wallet::load(wallet.take_staged().unwrap()).expect("load");
web_sys::console::log_2(&"balance: ".into(), &balance.total().to_sat().into());
assert!(balance.trusted_spendable().to_sat() > SEND_ADMOUNT);

// Important to test that we can load the wallet from a changeset with the signing descriptors and be able to sign a transaction
// as the changeset does not contain the private signing information.
let mut loaded_wallet = Wallet::load(
wallet.take_staged().unwrap(),
Some(external_desc.into()),
Some(internal_desc.into()),
)
.expect("load");
assert_eq!(loaded_wallet.balance(), wallet.balance());
}

#[wasm_bindgen_test]
async fn test_send() {
set_panic_hook();

let seed = Mnemonic::parse("journey embrace permit coil indoor stereo welcome maid movie easy clock spider tent slush bright luxury awake waste legal modify awkward answer acid goose")
.unwrap()
.to_seed("");
let descriptors = seed_to_descriptor(&seed, NETWORK, AddressType::P2wpkh).expect("seed_to_descriptor");
let mut wallet = Wallet::create(NETWORK, descriptors).expect("wallet");
let mut blockchain_client = EsploraClient::new(ESPLORA_URL).expect("esplora_client");

let full_scan_request = wallet.start_full_scan();
let update = blockchain_client
.full_scan(full_scan_request, STOP_GAP, PARALLEL_REQUESTS)
.await
.expect("full_scan");
wallet.apply_update(update).expect("full_scan apply_update");

let balance = wallet.balance();
assert!(balance.total().to_sat() > SEND_AMOUNT);
web_sys::console::log_2(&"Balance: ".into(), &balance.total().to_btc().into());

let recipient = Address::new(RECIPIENT_ADDRESS, NETWORK).expect("recipient_address");
let amount = Amount::from_sat(SEND_AMOUNT);
let mut psbt = wallet
.build_tx(FeeRate::new(2), vec![Recipient::new(recipient, amount)])
let amount = Amount::from_sat(SEND_ADMOUNT);
let mut psbt = loaded_wallet
.build_tx(FeeRate::new(FEE_RATE), vec![Recipient::new(recipient, amount)])
.expect("build_tx");

assert!(wallet.sign(&mut psbt).expect("sign"));
let finalized = loaded_wallet.sign(&mut psbt).expect("sign");
assert!(finalized);

let tx = psbt.extract_tx().expect("extract_tx");
blockchain_client.broadcast(&tx).await.expect("broadcast");

web_sys::console::log_1(&tx.compute_txid().into());
web_sys::console::log_2(&"txid: ".into(), &tx.compute_txid().into());
}
4 changes: 2 additions & 2 deletions tests/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async fn test_wallet() {

let seed = Mnemonic::parse(MNEMONIC).unwrap().to_seed("");
let descriptors = seed_to_descriptor(&seed, NETWORK, ADDRESS_TYPE).expect("seed_to_descriptor");
let mut wallet = Wallet::create(NETWORK, descriptors).expect("wallet");
let mut wallet = Wallet::create(NETWORK, descriptors.external(), descriptors.internal()).expect("wallet");

let balance = wallet.balance();
assert_eq!(balance.total().to_sat(), 0);
Expand All @@ -50,7 +50,7 @@ async fn test_changeset() {

let seed = Mnemonic::parse(MNEMONIC).unwrap().to_seed("");
let descriptors = seed_to_descriptor(&seed, NETWORK, ADDRESS_TYPE).expect("seed_to_descriptor");
let mut wallet = Wallet::create(NETWORK, descriptors).expect("wallet");
let mut wallet = Wallet::create(NETWORK, descriptors.external(), descriptors.internal()).expect("wallet");

let mut changeset = wallet.take_staged().expect("initial_changeset");
assert!(!changeset.is_empty());
Expand Down

0 comments on commit 187660e

Please sign in to comment.