This commit is contained in:
Dave Hrycyszyn
2024-06-24 08:02:17 +01:00
parent d6c118ca3b
commit 643a0d7f52
5 changed files with 120 additions and 61 deletions

View File

@@ -1,38 +1,76 @@
use bdk::{ use std::io::Write;
bitcoin::Network,
database::MemoryDatabase, use bdk::{bitcoin::Script, KeychainKind};
keys::{ use bdk_esplora::esplora_client;
bip39::{Language, Mnemonic, WordCount},
DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey, use crate::{keys, utils};
},
miniscript,
template::Bip84,
KeychainKind, Wallet,
};
pub async fn run() -> Result<(), anyhow::Error> { pub async fn run() -> Result<(), anyhow::Error> {
// Generate a new mnemonic let dave = utils::home(&"dave".to_string());
let mnemonic: GeneratedKey<_, miniscript::Segwitv0> = let sammy = utils::home(&"sammy".to_string());
Mnemonic::generate((WordCount::Words12, Language::English)).unwrap();
let mnemonic_words = mnemonic.to_string();
let mnemonic = Mnemonic::parse(&mnemonic_words).unwrap();
// Generate the extended key // Load mnemonics from disk
let xkey: ExtendedKey = mnemonic.into_extended_key()?; let dave_wallet = keys::bitcoin::load_from_file(&dave).unwrap();
let sammy_wallet = keys::bitcoin::load_from_file(&sammy).unwrap();
// Get private key from the extended key let dave_address = dave_wallet.get_address(bdk::wallet::AddressIndex::New)?;
let xprv = xkey.into_xprv(Network::Signet).unwrap(); println!("Dave address: {:?}", dave_address);
// Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1") let sammy_address = sammy_wallet.get_address(bdk::wallet::AddressIndex::New)?;
let wallet = Wallet::new( println!("Sammy address: {:?}", sammy_address);
Bip84(xprv, KeychainKind::External),
Some(Bip84(xprv, KeychainKind::Internal)),
Network::Signet,
MemoryDatabase::default(),
)?;
let address = wallet.get_address(bdk::wallet::AddressIndex::New)?; print!("Syncing...");
println!("Generated Address: {:?}", address); let client = esplora_client::Builder::new("http://signet.bitcoindevkit.net").build_async()?;
fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static {
let mut once = Some(());
let mut stdout = std::io::stdout();
move |spk_i, _| {
match once.take() {
Some(_) => print!("\nScanning keychain [{:?}]", kind),
None => print!(" {:<3}", spk_i),
};
stdout.flush().expect("must flush");
}
}
// let request = dave_wallet
// .start_full_scan()
// .inspect_spks_for_all_keychains({
// let mut once = BTreeSet::<KeychainKind>::new();
// move |keychain, spk_i, _| {
// match once.insert(keychain) {
// true => print!("\nScanning keychain [{:?}]", keychain),
// false => print!(" {:<3}", spk_i),
// }
// std::io::stdout().flush().expect("must flush")
// }
// })
// .inspect_spks_for_keychain(
// KeychainKind::External,
// generate_inspect(KeychainKind::External),
// )
// .inspect_spks_for_keychain(
// KeychainKind::Internal,
// generate_inspect(KeychainKind::Internal),
// );
// let mut update = client
// .full_scan(request, STOP_GAP, PARALLEL_REQUESTS)
// .await?;
// let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
// let _ = update.graph_update.update_last_seen_unconfirmed(now);
// wallet.apply_update(update)?;
// if let Some(changeset) = wallet.take_staged() {
// db.write(&changeset)?;
// }
let dave_balance = dave_wallet.get_balance()?;
println!("Wallet balance before syncing: {} sats", dave_balance);
let sammy_balance = sammy_wallet.get_balance()?;
println!("Wallet balance before syncing: {} sats", sammy_balance);
Ok(()) Ok(())
} }

View File

@@ -1,40 +1,59 @@
use bitcoin::secp256k1::{rand, Keypair, Secp256k1, SecretKey}; use bdk::{
bitcoin::Network,
database::MemoryDatabase,
keys::{
bip39::{Language, Mnemonic, WordCount},
DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey,
},
miniscript,
template::Bip84,
KeychainKind, Wallet,
};
use std::{ use std::{
fs::{self, File}, fs::{self, File},
io::Write, io::Write,
path::PathBuf, path::PathBuf,
str::FromStr,
}; };
pub fn make_keypair() -> bitcoin::secp256k1::Keypair { pub fn make_mnemonic() -> String {
let secp = Secp256k1::new(); let mnemonic: GeneratedKey<_, miniscript::Segwitv0> =
let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng()); Mnemonic::generate((WordCount::Words12, Language::English)).unwrap();
Keypair::from_secret_key(&secp, &secret_key) mnemonic.to_string()
} }
pub(crate) fn write(key_path: &PathBuf) -> Result<(), std::io::Error> { pub(crate) fn write(mnemonic_path: &PathBuf) -> Result<(), std::io::Error> {
let key_pair = make_keypair(); let mnemonic = make_mnemonic();
let mut file = File::create(key_path)?; let mut file = File::create(mnemonic_path)?;
println!("mnemonic: {mnemonic}");
let pub_str = key_pair.public_key().to_string(); file.write(mnemonic.as_bytes())?;
let priv_str = key_pair.display_secret().to_string();
println!("private key: {priv_str}");
let out = format!("{pub_str}/{priv_str}");
println!("out: {out}");
file.write(out.as_bytes())?;
Ok(()) Ok(())
} }
pub(crate) fn load_from_file(side_dir: &PathBuf) -> bitcoin::secp256k1::Keypair { pub fn create_wallet(mnemonic_words: String) -> anyhow::Result<Wallet<MemoryDatabase>> {
let bitcoin_key_path = crate::utils::side_paths(side_dir.clone()).1; // TODO: this tuple stinks let mnemonic = Mnemonic::parse(mnemonic_words).unwrap();
let data = fs::read_to_string(bitcoin_key_path).expect("couldn't read bitcoin key file"); // Generate the extended key
println!("bitcoin keys: {:?}", data); let xkey: ExtendedKey = mnemonic.into_extended_key()?;
let secret_key_data = data.split("/").collect::<Vec<&str>>()[1]; // Get private key from the extended key
let secp = Secp256k1::new(); let xprv = xkey.into_xprv(Network::Signet).unwrap();
let secret_key =
SecretKey::from_str(secret_key_data).expect("couldn't load secret key from file"); // Create a BDK wallet using BIP 84 descriptor ("m/84h/1h/0h/0" and "m/84h/1h/0h/1")
Keypair::from_secret_key(&secp, &secret_key) let wallet = Wallet::new(
Bip84(xprv, KeychainKind::External),
Some(Bip84(xprv, KeychainKind::Internal)),
Network::Signet,
MemoryDatabase::default(),
)?;
Ok(wallet)
}
pub(crate) fn load_from_file(side_dir: &PathBuf) -> anyhow::Result<Wallet<MemoryDatabase>> {
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}");
create_wallet(mnemonic_words)
} }

View File

@@ -42,7 +42,7 @@ async fn setup(name: &String) -> SideNode {
// First, load up the keys and create a bft-crdt // First, load up the keys and create a bft-crdt
let side_dir = utils::home(name); let side_dir = utils::home(name);
let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir); let bft_crdt_keys = keys::bft_crdt::load_from_file(&side_dir);
let bitcoin_keys = keys::bitcoin::load_from_file(&side_dir); let bitcoin_wallet = keys::bitcoin::load_from_file(&side_dir).unwrap();
let crdt = BaseCrdt::<TransactionList>::new(&bft_crdt_keys); let crdt = BaseCrdt::<TransactionList>::new(&bft_crdt_keys);
// Channels for internal communication, and a tokio task for stdin input // Channels for internal communication, and a tokio task for stdin input
@@ -57,7 +57,7 @@ async fn setup(name: &String) -> SideNode {
let node = SideNode::new( let node = SideNode::new(
crdt, crdt,
bft_crdt_keys, bft_crdt_keys,
bitcoin_keys, bitcoin_wallet,
incoming_receiver, incoming_receiver,
stdin_receiver, stdin_receiver,
handle, handle,

View File

@@ -1,3 +1,4 @@
use bdk::database::MemoryDatabase;
use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp};
use fastcrypto::ed25519::Ed25519KeyPair; use fastcrypto::ed25519::Ed25519KeyPair;
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -7,7 +8,7 @@ use crate::{clients::websocket::Client, crdt::TransactionList, utils};
pub struct SideNode { pub struct SideNode {
crdt: BaseCrdt<TransactionList>, crdt: BaseCrdt<TransactionList>,
bft_crdt_keys: fastcrypto::ed25519::Ed25519KeyPair, bft_crdt_keys: fastcrypto::ed25519::Ed25519KeyPair,
bitcoin_keys: bitcoin::secp256k1::Keypair, bitcoin_wallet: bdk::Wallet<MemoryDatabase>,
incoming_receiver: mpsc::Receiver<SignedOp>, incoming_receiver: mpsc::Receiver<SignedOp>,
stdin_receiver: std::sync::mpsc::Receiver<String>, stdin_receiver: std::sync::mpsc::Receiver<String>,
handle: ezsockets::Client<Client>, handle: ezsockets::Client<Client>,
@@ -17,7 +18,7 @@ impl SideNode {
pub fn new( pub fn new(
crdt: BaseCrdt<TransactionList>, crdt: BaseCrdt<TransactionList>,
bft_crdt_keys: Ed25519KeyPair, bft_crdt_keys: Ed25519KeyPair,
bitcoin_keys: bitcoin::secp256k1::Keypair, bitcoin_wallet: bdk::Wallet<MemoryDatabase>,
incoming_receiver: mpsc::Receiver<SignedOp>, incoming_receiver: mpsc::Receiver<SignedOp>,
stdin_receiver: std::sync::mpsc::Receiver<String>, stdin_receiver: std::sync::mpsc::Receiver<String>,
handle: ezsockets::Client<Client>, handle: ezsockets::Client<Client>,
@@ -25,7 +26,7 @@ impl SideNode {
let node = Self { let node = Self {
crdt, crdt,
bft_crdt_keys, bft_crdt_keys,
bitcoin_keys, bitcoin_wallet,
incoming_receiver, incoming_receiver,
stdin_receiver, stdin_receiver,
handle, handle,

View File

@@ -35,7 +35,8 @@ async fn test_distribute_via_websockets() {
async fn setup(_: &str) -> SideNode { async fn setup(_: &str) -> SideNode {
// First, load up the keys and create a bft-crdt // First, load up the keys and create a bft-crdt
let bft_crdt_keys = make_keypair(); let bft_crdt_keys = make_keypair();
let bitcoin_keys = keys::bitcoin::make_keypair(); let mnemonic_words = keys::bitcoin::make_mnemonic();
let bitcoin_wallet = keys::bitcoin::create_wallet(mnemonic_words).unwrap();
let crdt = BaseCrdt::<TransactionList>::new(&bft_crdt_keys); let crdt = BaseCrdt::<TransactionList>::new(&bft_crdt_keys);
// Channels for internal communication, and a tokio task for stdin input // Channels for internal communication, and a tokio task for stdin input
@@ -47,7 +48,7 @@ async fn setup(_: &str) -> SideNode {
let node = SideNode::new( let node = SideNode::new(
crdt, crdt,
bft_crdt_keys, bft_crdt_keys,
bitcoin_keys, bitcoin_wallet,
incoming_receiver, incoming_receiver,
stdin_receiver, stdin_receiver,
handle, handle,