Esplora client now working with persisted mnemonics

This commit is contained in:
Dave Hrycyszyn
2024-06-24 18:41:55 +01:00
parent 117915bded
commit a516de4bcb
4 changed files with 55 additions and 37 deletions

View File

@@ -1,23 +1,22 @@
use crate::{keys, utils};
use bdk::bitcoin::bip32::ExtendedPrivKey;
use bdk::bitcoin::psbt::PartiallySignedTransaction;
use bdk::bitcoin::Network;
use bdk::database::MemoryDatabase;
use bdk::keys::ExtendedKey;
use bdk::template::Bip84;
use bdk::wallet::AddressIndex::{self, New};
use bdk::wallet::AddressInfo;
use bdk::{blockchain::ElectrumBlockchain, electrum_client, SyncOptions};
use bdk::{FeeRate, SignOptions, TransactionDetails, Wallet};
use bdk::{FeeRate, KeychainKind, SignOptions, TransactionDetails, Wallet};
pub async fn run() -> Result<(), anyhow::Error> {
let dave = utils::home(&"dave".to_string());
let sammy = utils::home(&"sammy".to_string());
let dave_keys = keys::bitcoin::load_from_file(&dave).unwrap();
let sammy_keys = keys::bitcoin::load_from_file(&sammy).unwrap();
let dave_key = keys::bitcoin::load_from_file(&dave).unwrap();
let sammy_key = keys::bitcoin::load_from_file(&sammy).unwrap();
let dave_wallet = create_wallet(dave_keys)?;
let sammy_wallet = create_wallet(sammy_keys)?;
let dave_wallet = create_wallet(dave_key)?;
let sammy_wallet = create_wallet(sammy_key)?;
let dave_address = dave_wallet.get_address(AddressIndex::Peek(0))?.to_string();
let sammy_address = sammy_wallet.get_address(AddressIndex::Peek(0))?.to_string();
@@ -50,10 +49,13 @@ pub async fn run() -> Result<(), anyhow::Error> {
}
/// Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1")
pub fn create_wallet(
keys: (Bip84<ExtendedPrivKey>, Option<Bip84<ExtendedPrivKey>>),
) -> anyhow::Result<Wallet<MemoryDatabase>> {
let (external_descriptor, internal_descriptor) = keys;
pub fn create_wallet(xkey: ExtendedKey) -> anyhow::Result<Wallet<MemoryDatabase>> {
let xprv = xkey
.into_xprv(Network::Testnet)
.expect("couldn't turn xkey into xprv");
let external_descriptor = Bip84(xprv, KeychainKind::External);
let internal_descriptor = Some(Bip84(xprv, KeychainKind::Internal));
let wallet = Wallet::new(
external_descriptor,

View File

@@ -1,32 +1,56 @@
use std::{collections::BTreeSet, io::Write, str::FromStr};
use std::{collections::BTreeSet, fs, io::Write, str::FromStr};
use bdk::keys::bip39::Mnemonic;
use bdk_esplora::{esplora_client, EsploraAsyncExt};
use bdk_wallet::{
bitcoin::{Address, Amount, Network, Script},
keys::{DerivableKey, ExtendedKey},
KeychainKind, SignOptions, Wallet,
};
use bdk_sqlite::{rusqlite::Connection, Store};
use crate::{keys, utils};
const SEND_AMOUNT: Amount = Amount::from_sat(5000);
const STOP_GAP: usize = 50;
const PARALLEL_REQUESTS: usize = 5;
/// Demonstrates the use of bdk with the Esplora client.
pub async fn run() -> Result<(), anyhow::Error> {
let dave_dir = utils::home(&"dave".to_string());
let mnemonic_path = crate::utils::side_paths(dave_dir).1; // TODO: this tuple stinks
let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file");
println!("Creating wallet from mnemonic: {mnemonic_words}");
let mnemonic = Mnemonic::parse(mnemonic_words).unwrap();
// Generate the extended key
let xkey: ExtendedKey = mnemonic
.into_extended_key()
.expect("couldn't turn mnemonic into xkey");
let xprv = xkey
.into_xprv(Network::Signet)
.expect("problem converting xkey to xprv")
.to_string();
println!("Setting up esplora database");
let db_path = "/tmp/bdk-esplora-async-example.sqlite";
let conn = Connection::open(db_path)?;
let mut db = Store::new(conn)?;
let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
let changeset = db.read()?;
let external_descriptor = format!("wpkh({xprv}/84'/1'/0'/0/*)");
let internal_descriptor = format!("wpkh({xprv}/84'/1'/0'/1/*)");
let changeset = db.read().expect("couldn't read esplora database");
let mut wallet = Wallet::new_or_load(
external_descriptor,
internal_descriptor,
&external_descriptor,
&internal_descriptor,
changeset,
Network::Signet,
)?;
)
.expect("problem setting up wallet");
let address = wallet.next_unused_address(KeychainKind::External);
if let Some(changeset) = wallet.take_staged() {

View File

@@ -1,12 +1,9 @@
use bdk::{
bitcoin::{bip32::ExtendedPrivKey, Network},
keys::{
bip39::{Language, Mnemonic, WordCount},
DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey,
},
miniscript,
template::Bip84,
KeychainKind,
};
use std::{
@@ -21,6 +18,9 @@ pub fn make_mnemonic() -> String {
mnemonic.to_string()
}
/// Write the mnemonic to a file in the node's side directory
///
/// TODO: obviously spitting the mnemonic out to the console is not for production
pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> {
let mnemonic = make_mnemonic();
let mut file = File::create(mnemonic_path)?;
@@ -30,27 +30,19 @@ pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> {
Ok(())
}
/// Creates a Signet Bitcoin descriptor wallet from a mnemonic
pub fn get(
mnemonic_words: String,
) -> anyhow::Result<(Bip84<ExtendedPrivKey>, Option<Bip84<ExtendedPrivKey>>)> {
/// Creates Signet Bitcoin descriptors from a mnemonic
pub fn get(mnemonic_words: String) -> anyhow::Result<ExtendedKey> {
let mnemonic = Mnemonic::parse(mnemonic_words).unwrap();
// Generate the extended key
let xkey: ExtendedKey = mnemonic.into_extended_key()?;
let xkey: ExtendedKey = mnemonic
.into_extended_key()
.expect("couldn't turn mnemonic into xkey");
// Get private key from the extended key
let xprv = xkey.into_xprv(Network::Signet).unwrap();
let external_descriptor = Bip84(xprv, KeychainKind::External);
let internal_descriptor = Some(Bip84(xprv, KeychainKind::Internal));
Ok((external_descriptor, internal_descriptor))
Ok(xkey)
}
pub(crate) fn load_from_file(
side_dir: &PathBuf,
) -> anyhow::Result<(Bip84<ExtendedPrivKey>, Option<Bip84<ExtendedPrivKey>>)> {
pub(crate) fn load_from_file(side_dir: &PathBuf) -> anyhow::Result<ExtendedKey> {
let mnemonic_path = crate::utils::side_paths(side_dir.clone()).1; // TODO: this tuple stinks
let mnemonic_words = fs::read_to_string(mnemonic_path).expect("couldn't read bitcoin key file");
println!("Creating wallet from mnemonic: {mnemonic_words}");

View File

@@ -31,7 +31,7 @@ pub async fn run() {
node.start().await;
}
Some(Commands::Btc {}) => {
let _ = clients::btc_electrum_client::run().await;
let _ = clients::btc_esplora_client::run().await;
}
None => println!("No command provided. Exiting. See --help for more information."),
}