Ok the bdk looks like a far better bet!

This commit is contained in:
Dave Hrycyszyn
2024-06-21 17:00:01 +01:00
parent 933fea76df
commit ac6473bb1b
5 changed files with 952 additions and 42 deletions

818
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.86"
async-trait = "0.1.52"
bdk = "0.29.0"
bdk_esplora = "0.15.0"
bdk_sqlite = "0.2.0"
bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys"] }
bft-json-crdt = { path = "../crates/bft-json-crdt" }
bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" }
@@ -15,15 +19,16 @@ clap = { version = "4.5.4", features = ["derive"] }
dirs = "5.0.1"
ezsockets = { version = "*", features = ["client"] }
fastcrypto = "0.1.8"
indexmap = { version = "2.2.6", features = ["serde"] }
# serde_cbor = "0.11.2" # move to this once we need to pack things in CBOR
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.117"
sha256 = "1.5.0"
tokio = { version = "1.37.0", features = ["full"] }
toml = "0.8.14"
tracing = "0.1.32"
# tracing-subscriber = "0.3.9"
toml = "0.8.14"
indexmap = { version = "2.2.6", features = ["serde"] }
[dev-dependencies]
uuid = { version = "1.8.0", features = ["v4"] }

View File

@@ -1,3 +1,114 @@
pub fn run(name: &str) {
println!("running btc_rpc...");
use std::{collections::BTreeSet, io::Write, str::FromStr};
use bdk_esplora::{esplora_client, EsploraAsyncExt};
use bdk_wallet::{
bitcoin::{Address, Amount, Network, Script},
KeychainKind, SignOptions, Wallet,
};
use bdk_sqlite::{rusqlite::Connection, Store};
const SEND_AMOUNT: Amount = Amount::from_sat(5000);
const STOP_GAP: usize = 50;
const PARALLEL_REQUESTS: usize = 5;
pub async fn run() -> Result<(), anyhow::Error> {
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 mut wallet = Wallet::new_or_load(
external_descriptor,
internal_descriptor,
changeset,
Network::Signet,
)?;
let address = wallet.next_unused_address(KeychainKind::External);
if let Some(changeset) = wallet.take_staged() {
db.write(&changeset)?;
}
println!("Generated Address: {}", address);
let balance = wallet.balance();
println!("Wallet balance before syncing: {} sats", balance.total());
print!("Syncing...");
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 = 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)?;
}
println!();
let balance = wallet.balance();
println!("Wallet balance after syncing: {} sats", balance.total());
if balance.total() < SEND_AMOUNT {
println!(
"Please send at least {} sats to the receiving address",
SEND_AMOUNT
);
std::process::exit(0);
}
let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?
.require_network(Network::Signet)?;
let mut tx_builder = wallet.build_tx();
tx_builder
.add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
.enable_rbf();
let mut psbt = tx_builder.finish()?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
assert!(finalized);
let tx = psbt.extract_tx()?;
client.broadcast(&tx).await?;
println!("Tx broadcasted! Txid: {}", tx.compute_txid());
Ok(())
}

View File

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

View File

@@ -1,8 +1,6 @@
use std::path::PathBuf;
use bft_json_crdt::json_crdt::SignedOp;
use bitcoin::{absolute, key::Keypair, transaction::Version, TxIn, TxOut};
use serde_json::{json, Value};
use std::path::PathBuf;
pub(crate) const BITCOIN_KEY_FILE: &str = "bitcoin_keys.pem";
pub(crate) const BFT_CRDT_KEY_FILE: &str = "keys.pem";
@@ -40,29 +38,29 @@ pub fn fake_generic_transaction_json(from: String) -> Value {
}
/// Generate a Bitcoin transaction from this node's bitcoin_keys
pub fn fake_bitcoin_transaction(key_pair: Keypair) -> bitcoin::Transaction {
let from = key_pair.public_key().to_string();
let to = "Bob";
let amount = 1;
let lock_time = absolute::LockTime::from_height(0).expect("couldn't format btc lock time");
let input = TxIn {
previous_output: todo!(),
script_sig: todo!(),
sequence: todo!(),
witness: todo!(),
};
let output = TxOut {
value: todo!(),
script_pubkey: todo!(),
};
let tx = bitcoin::Transaction {
version: Version(1),
lock_time,
input: vec![input],
output: vec![output],
};
tx
}
// pub fn fake_bitcoin_transaction(key_pair: Keypair) -> bitcoin::Transaction {
// let from = key_pair.public_key().to_string();
// let to = "Bob";
// let amount = 1;
// let lock_time = absolute::LockTime::from_height(0).expect("couldn't format btc lock time");
// let input = TxIn {
// previous_output: todo!(),
// script_sig: todo!(),
// sequence: todo!(),
// witness: todo!(),
// };
// let output = TxOut {
// value: todo!(),
// script_pubkey: todo!(),
// };
// let tx = bitcoin::Transaction {
// version: Version(1),
// lock_time,
// input: vec![input],
// output: vec![output],
// };
// tx
// }
pub fn shappy(op: SignedOp) -> String {
let b = serde_json::to_string(&op).unwrap().into_bytes();