Esplora client now working with persisted mnemonics
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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}");
|
||||
|
||||
@@ -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."),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user