fix(core): avoid bdk bip39 helper; use local bip39 -> xpub descriptors; normalize PSBT serialization; remove stale imports
This commit is contained in:
parent
3ed29dc179
commit
1c15e8f150
2 changed files with 27 additions and 20 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `AesGcm`
|
use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `AesGcm`
|
||||||
use aes_gcm::aead::{Aead, NewAead};
|
use aes_gcm::aead::Aead;
|
||||||
use argon2::{Argon2, password_hash::{SaltString, PasswordHasher, PasswordVerifier}, PasswordHash, PasswordHasher as _};
|
use argon2::{Argon2, password_hash::{SaltString, PasswordHasher, PasswordVerifier}, PasswordHash, PasswordHasher as _};
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ use bdk::Wallet;
|
||||||
use bdk::database::{MemoryDatabase, SqliteDatabase};
|
use bdk::database::{MemoryDatabase, SqliteDatabase};
|
||||||
use bdk::wallet::AddressIndex;
|
use bdk::wallet::AddressIndex;
|
||||||
use bdk::blockchain::{noop_progress::NoopProgress, ElectrumBlockchain, ElectrumBlockchainConfig};
|
use bdk::blockchain::{noop_progress::NoopProgress, ElectrumBlockchain, ElectrumBlockchainConfig};
|
||||||
use bdk::keys::bip39::{Mnemonic as BdkMnemonic};
|
use crate::bip39::Mnemonic as LocalMnemonic;
|
||||||
use bdk::descriptor::DescriptorTemplateOut;
|
use bdk::descriptor::DescriptorTemplateOut;
|
||||||
|
use bitcoin::util::bip32::ExtendedPubKey;
|
||||||
use bitcoin::Network;
|
use bitcoin::Network;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use bitcoin::consensus::encode as consensus_encode;
|
||||||
|
|
||||||
/// Minimal BDK wallet scaffold with Electrum support (in-memory)
|
/// Minimal BDK wallet scaffold with Electrum support (in-memory)
|
||||||
pub struct BdkWallet {
|
pub struct BdkWallet {
|
||||||
|
|
@ -16,14 +18,14 @@ pub struct BdkWallet {
|
||||||
impl BdkWallet {
|
impl BdkWallet {
|
||||||
/// Create a new BDK wallet from a BIP39 mnemonic (and passphrase) for the specified network
|
/// Create a new BDK wallet from a BIP39 mnemonic (and passphrase) for the specified network
|
||||||
pub fn new_from_mnemonic(phrase: &str, passphrase: &str, network: Network) -> Result<Self> {
|
pub fn new_from_mnemonic(phrase: &str, passphrase: &str, network: Network) -> Result<Self> {
|
||||||
// Use BDK's bip39 helper to create descriptor templates
|
// Derive master xprv using our local bip39 helper and build simple `wpkh(<xpub>/0/*)` descriptors.
|
||||||
let mnemonic = BdkMnemonic::from_str(phrase).map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
let local_m = LocalMnemonic::from_phrase(phrase).map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
||||||
let xkey: DescriptorTemplateOut = bdk::keys::bip39::translate_mnemonic(&mnemonic, passphrase, network)
|
let xprv = local_m.to_master_xprv(passphrase, network);
|
||||||
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
// neuter to xpub for descriptor
|
||||||
|
let secp = secp256k1::Secp256k1::new();
|
||||||
// For demo, use the first "external" descriptor for a single-key wallet
|
let xpub = ExtendedPubKey::from_private(&secp, &xprv);
|
||||||
let descriptor = xkey.external.clone();
|
let descriptor = format!("wpkh({}/0/*)", xpub.to_string());
|
||||||
let change_descriptor = xkey.internal.clone();
|
let change_descriptor = format!("wpkh({}/1/*)", xpub.to_string());
|
||||||
|
|
||||||
let wallet = Wallet::new_offline(&descriptor, Some(&change_descriptor), network, MemoryDatabase::default())?;
|
let wallet = Wallet::new_offline(&descriptor, Some(&change_descriptor), network, MemoryDatabase::default())?;
|
||||||
Ok(Self { wallet })
|
Ok(Self { wallet })
|
||||||
|
|
@ -49,7 +51,8 @@ impl BdkWallet {
|
||||||
let mut builder = bdk::TxBuilder::new();
|
let mut builder = bdk::TxBuilder::new();
|
||||||
builder = builder.add_recipient(addr.script_pubkey(), satoshis);
|
builder = builder.add_recipient(addr.script_pubkey(), satoshis);
|
||||||
let (psbt, _details) = self.wallet.build_tx(builder)?;
|
let (psbt, _details) = self.wallet.build_tx(builder)?;
|
||||||
let bs = base64::encode(&psbt.serialize());
|
let serialized = consensus_encode::serialize(&psbt);
|
||||||
|
let bs = base64::encode(&serialized);
|
||||||
Ok(bs)
|
Ok(bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +61,8 @@ impl BdkWallet {
|
||||||
let raw = base64::decode(psbt_b64)?;
|
let raw = base64::decode(psbt_b64)?;
|
||||||
let mut psbt: bitcoin::util::psbt::PartiallySignedTransaction = bitcoin::consensus::deserialize(&raw)?;
|
let mut psbt: bitcoin::util::psbt::PartiallySignedTransaction = bitcoin::consensus::deserialize(&raw)?;
|
||||||
self.wallet.sign(&mut psbt, bdk::SignOptions::default())?;
|
self.wallet.sign(&mut psbt, bdk::SignOptions::default())?;
|
||||||
let out = base64::encode(&psbt.serialize());
|
let serialized = consensus_encode::serialize(&psbt);
|
||||||
|
let out = base64::encode(&serialized);
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,12 +82,13 @@ impl PersistentBdkWallet {
|
||||||
/// Create or open a persistent wallet using a SQLite DB file at `db_path`.
|
/// Create or open a persistent wallet using a SQLite DB file at `db_path`.
|
||||||
/// `db_path` should be a filesystem path like `/path/to/wallet.db`.
|
/// `db_path` should be a filesystem path like `/path/to/wallet.db`.
|
||||||
pub fn new_from_mnemonic_sqlite(phrase: &str, passphrase: &str, network: Network, db_path: &str) -> Result<Self> {
|
pub fn new_from_mnemonic_sqlite(phrase: &str, passphrase: &str, network: Network, db_path: &str) -> Result<Self> {
|
||||||
let mnemonic = BdkMnemonic::from_str(phrase).map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
// derive master xprv & descriptors just like in BdkWallet
|
||||||
let xkey: DescriptorTemplateOut = bdk::keys::bip39::translate_mnemonic(&mnemonic, passphrase, network)
|
let local_m = LocalMnemonic::from_phrase(phrase).map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
||||||
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
let xprv = local_m.to_master_xprv(passphrase, network);
|
||||||
|
let secp = secp256k1::Secp256k1::new();
|
||||||
let descriptor = xkey.external.clone();
|
let xpub = ExtendedPubKey::from_private(&secp, &xprv);
|
||||||
let change_descriptor = xkey.internal.clone();
|
let descriptor = format!("wpkh({}/0/*)", xpub.to_string());
|
||||||
|
let change_descriptor = format!("wpkh({}/1/*)", xpub.to_string());
|
||||||
|
|
||||||
// Ensure directory exists
|
// Ensure directory exists
|
||||||
let p = Path::new(db_path);
|
let p = Path::new(db_path);
|
||||||
|
|
@ -116,7 +121,8 @@ impl PersistentBdkWallet {
|
||||||
let mut builder = bdk::TxBuilder::new();
|
let mut builder = bdk::TxBuilder::new();
|
||||||
builder = builder.add_recipient(addr.script_pubkey(), satoshis);
|
builder = builder.add_recipient(addr.script_pubkey(), satoshis);
|
||||||
let (psbt, _details) = self.wallet.build_tx(builder)?;
|
let (psbt, _details) = self.wallet.build_tx(builder)?;
|
||||||
let bs = base64::encode(&psbt.serialize());
|
let serialized = consensus_encode::serialize(&psbt);
|
||||||
|
let bs = base64::encode(&serialized);
|
||||||
Ok(bs)
|
Ok(bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,7 +130,8 @@ impl PersistentBdkWallet {
|
||||||
let raw = base64::decode(psbt_b64)?;
|
let raw = base64::decode(psbt_b64)?;
|
||||||
let mut psbt: bitcoin::util::psbt::PartiallySignedTransaction = bitcoin::consensus::deserialize(&raw)?;
|
let mut psbt: bitcoin::util::psbt::PartiallySignedTransaction = bitcoin::consensus::deserialize(&raw)?;
|
||||||
self.wallet.sign(&mut psbt, bdk::SignOptions::default())?;
|
self.wallet.sign(&mut psbt, bdk::SignOptions::default())?;
|
||||||
let out = base64::encode(&psbt.serialize());
|
let serialized = consensus_encode::serialize(&psbt);
|
||||||
|
let out = base64::encode(&serialized);
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue