added signing PSBTs

This commit is contained in:
aaravm
2024-07-21 14:34:08 +05:30
parent 09e505e849
commit e96e02764a
2 changed files with 170 additions and 151 deletions

View File

@ -13,11 +13,11 @@
"state": {
"type": "canvas",
"state": {
"file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas",
"file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas",
"viewState": {
"x": 31,
"y": -44.99563086871524,
"zoom": -0.432684559607355
"x": 656.8883048329865,
"y": 805.4168609598363,
"zoom": -0.6000000000000001
}
}
}
@ -88,7 +88,7 @@
"state": {
"type": "backlink",
"state": {
"file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas",
"file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
@ -105,7 +105,7 @@
"state": {
"type": "outgoing-link",
"state": {
"file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas",
"file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas",
"linksCollapsed": false,
"unlinkedCollapsed": true
}
@ -128,7 +128,7 @@
"state": {
"type": "outline",
"state": {
"file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas"
"file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas"
}
}
}
@ -151,26 +151,29 @@
},
"active": "bdb9fd88a01a8909",
"lastOpenFiles": [
"Research/Trade Pipelines/new concepts/concept pipeline 1.canvas",
"Research/Implementation/CLI demonstrator architecture/demonstrator architecture.canvas",
"Research/Trade Pipelines/new concepts/concept locking script 1.canvas",
"Research/Bitcoin fundamentals/Knowledge sources.md",
"Research/Trade Pipelines/Existing research.md",
"Research/Trade Pipelines/new concepts/concept pipeline 1.canvas",
"Research/Bitcoin fundamentals/Taproot output structure.canvas",
"Research/Bitcoin fundamentals/Spending Taproot UTXOs.md",
"Research/Implementation/Libraries.md",
"Research/Implementation/BDK.md",
"signet-instance.md",
"Project Timeline.md",
"Obsidian How-To.md",
"Research/sighash_flags.webp",
"Research/Bitcoin fundamentals/Signature and Flags.canvas",
"assets/ptlc2.png",
"assets/ptlc.png",
"assets/Pasted image 20240611120722.png",
"assets/Pasted image 20240611105421.png",
"Research/Bitcoin fundamentals/Taproot output structure.canvas",
"Pasted image 20240611120722.png",
"Pasted image 20240611105421.png",
"Pasted image 20240605185325.png",
"Research/Trade Pipelines/current trade flow.canvas",
"Research/Implementation/Libraries.md",
"Research/Bitcoin fundamentals/Spending Taproot UTXOs.md",
"Research/Bitcoin fundamentals/Signature and Flags.canvas",
"Research/Implementation/CLI demonstrator architecture/demonstrator architecture.canvas",
"Research/Implementation/Libraries.md",
"Research/Implementation/BDK.md",
"Research/Implementation/UI ideas.canvas",
"Research/Trade Pipelines/Existing research.md",
"Research/Implementation/CLI demonstrator architecture",
"Research/Trade Pipelines/new concepts/Untitled.md"
]

View File

@ -4,7 +4,7 @@ use anyhow::Context;
/// from multiple participants, create a Taproot script descriptor, create a PSBT from the
/// descriptor, and handle the case when the taker is unresponsive.
use bdk::bitcoin::address::NetworkUnchecked;
use bdk::bitcoin::bip32::ExtendedPrivKey;
use bdk::bitcoin::bip32::{ExtendedPrivKey, DerivationPath, ChildNumber};
use bdk::bitcoin::psbt::PartiallySignedTransaction;
use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::blockchain::{ElectrumBlockchain, EsploraBlockchain};
@ -18,6 +18,8 @@ use bdk::template::Bip86;
use bdk::wallet::AddressIndex;
use bdk::{sled, SignOptions};
use bdk::{FeeRate, KeychainKind, SyncOptions, Wallet};
use bdk::wallet::signer::{SignerWrapper, SignerContext, SignerOrdering};
use bdk::bitcoin::PrivateKey;
use std::collections::BTreeMap;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
@ -114,65 +116,7 @@ pub struct CoordinatorWallet<D: bdk::database::BatchDatabase> {
pub backend: Arc<ElectrumBlockchain>,
}
pub fn init_coordinator_wallet(wallet_xprv: &str) -> Result<CoordinatorWallet<sled::Tree>, Box<dyn std::error::Error>> {
println!("Hello init_coordinator_wallet");
let wallet_xprv = ExtendedPrivKey::from_str(wallet_xprv)?;
// let backend = ElectrumBlockchain::from(Client::new(
// &env::var("ELECTRUM_BACKEND")
// .context("Parsing ELECTRUM_BACKEND from .env failed, is it set?")?,
// )?);
// println!("ELECTRUM_BACKEND: {:?}", backend);
let electrum_backend = "ssl://mempool.space:40002";
let client = match Client::new(&electrum_backend) {
Ok(c) => {
println!("Electrum client created");
c
},
Err(e) => {
println!("Failed to create Electrum client: {}", e);
return Err(e.into());
}
};
let backend = ElectrumBlockchain::from(client);
println!("Electrum blockchain backend created");
// let backend = EsploraBlockchain::new(&env::var("ESPLORA_BACKEND")?, 1000);
let sled_db = sled::open("./dbs/bdk-wallet")?.open_tree("default_wallet")?;
println!("HELLO???");
// let wallet = Wallet::new(
// Bip86(wallet_xprv, KeychainKind::External),
// Some(Bip86(wallet_xprv, KeychainKind::Internal)),
// bdk::bitcoin::Network::Testnet,
// sled_db,
// )?;
let wallet_result = Wallet::new(
Bip86(wallet_xprv, KeychainKind::External),
Some(Bip86(wallet_xprv, KeychainKind::Internal)),
bdk::bitcoin::Network::Testnet,
sled_db,
);
match wallet_result {
Ok(wallet) => {
println!("Wallet created successfully");
wallet
.sync(&backend, SyncOptions::default())
.context("Connection to electrum server failed.")?; // we could also use Esplora to make this async
dbg!(wallet.get_balance()?);
println!{"{:?}", wallet.get_balance()};
return Ok(CoordinatorWallet {
wallet: Arc::new(Mutex::new(wallet)),
backend: Arc::new(backend),
});
},
Err(e) => {
println!("Failed to create wallet: {}", e);
return Err(e.into());
}
}
}
/// the provided keys, and `create_psbt`, which creates a PSBT from the descriptor
/// Figure out how to put UTXO's
// pub async fn fund_psbt(descriptor: Descriptor<String>)-> Result<(PartiallySignedTransaction), Box<dyn std::error::Error>> {
@ -221,7 +165,7 @@ pub async fn fund_psbt(descriptor: Descriptor<String>)-> Result<(), Box<dyn std:
fn taker_unresponsive_psbt(
taker_address: &str,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<PartiallySignedTransaction, Box<dyn std::error::Error>> {
// If taker is unresponsive, coordinator signs using alternative path
let taker_responsive = false; // Assume taker is unresponsive
if !taker_responsive {
@ -283,65 +227,157 @@ fn taker_unresponsive_psbt(
// debug!("PSBT: {:?}", psbt);
// println!("psbt is {:?}", psbt);
let json = to_string_pretty(&psbt).unwrap();
println!("psbt is {}", json);
// println!("psbt is {}", json);
// Ok(psbt)
Ok(psbt)
}
Err(e) => {
println!("Error creating wallet: {:?}", e);
Err(Box::new(e)) // Convert bdk::Error to Box<dyn std::error::Error>
}
}
}
Ok(())
} else {
Err("Taker is responsive, no need to sign using alternative path".into())
}
}
pub fn init_wallet(wallet_xprv: &str) -> Result<CoordinatorWallet<sled::Tree>, Box<dyn std::error::Error>> {
println!("Hello init_coordinator_wallet");
let wallet_xprv = ExtendedPrivKey::from_str(wallet_xprv)?;
// let backend = ElectrumBlockchain::from(Client::new(
// &env::var("ELECTRUM_BACKEND")
// .context("Parsing ELECTRUM_BACKEND from .env failed, is it set?")?,
// )?);
// println!("ELECTRUM_BACKEND: {:?}", backend);
let electrum_backend = "ssl://mempool.space:40002";
let client = match Client::new(&electrum_backend) {
Ok(c) => {
println!("Electrum client created");
c
},
Err(e) => {
println!("Failed to create Electrum client: {}", e);
return Err(e.into());
}
};
let backend = ElectrumBlockchain::from(client);
println!("Electrum blockchain backend created");
// let backend = EsploraBlockchain::new(&env::var("ESPLORA_BACKEND")?, 1000);
let sled_db = sled::open("./dbs/bdk-wallet")?.open_tree("default_wallet")?;
println!("HELLO???");
// let wallet = Wallet::new(
// Bip86(wallet_xprv, KeychainKind::External),
// Some(Bip86(wallet_xprv, KeychainKind::Internal)),
// bdk::bitcoin::Network::Testnet,
// sled_db,
// )?;
let wallet_result = Wallet::new(
Bip86(wallet_xprv, KeychainKind::External),
Some(Bip86(wallet_xprv, KeychainKind::Internal)),
bdk::bitcoin::Network::Testnet,
sled_db,
);
match wallet_result {
Ok(wallet) => {
println!("Wallet created successfully");
wallet
.sync(&backend, SyncOptions::default())
.context("Connection to electrum server failed.")?; // we could also use Esplora to make this async
dbg!(wallet.get_balance()?);
println!{"{:?}", wallet.get_balance()};
Ok(CoordinatorWallet {
wallet: Arc::new(Mutex::new(wallet)),
backend: Arc::new(backend),
})
},
Err(e) => {
println!("Failed to create wallet: {}", e);
Err(e.into())
}
}
}
/// The `taker_unresponsive` function handles the case when the taker is unresponsive and
/// the coordinator needs to sign the PSBT using an alternative path.
// TODO: Figure out how to use UTXO's
// fn taker_unresponsive(
// psbt: PartiallySignedTransaction,
// wallet: Wallet<MemoryDatabase>,
// // maker_utxos: Vec<UTXO>,
// // taker_utxos: Vec<UTXO>,
// recipient_address: Address,
// ) -> Result<(), Box<dyn std::error::Error>> {
// // If taker is unresponsive, coordinator signs using alternative path
// let taker_responsive = false; // Assume taker is unresponsive
// if !taker_responsive {
// let wallet_result = Wallet::new(
// "tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum",
// None,
// bdk::bitcoin::Network::Testnet,
// MemoryDatabase::new()
// );
fn taker_unresponsive(
mut psbt: PartiallySignedTransaction,
) -> Result<(), Box<dyn std::error::Error>> {
println!("here");
// If taker is unresponsive, coordinator signs using alternative path
let taker_responsive = false; // Assume taker is unresponsive
if !taker_responsive {
let wallet_result = Wallet::new(
"tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum",
None,
bdk::bitcoin::Network::Testnet,
MemoryDatabase::new()
);
// match wallet_result {
// Ok(wallet) => {
// }
// Err(e) => {
// println!("Error creating wallet: {:?}", e);
// }
// }
// // // Step 2: Add the BDK signer
// let mut private_key_str = String::new();
// File::open("key.txt")?.read_to_string(&mut private_key_str)?;
// println!("{}", private_key_str);
// let private_key = PrivateKey::from_str(&private_key_str)?;
// let signer = SignerWrapper::new(private_key, SignerContext::Tap { is_internal_key: false });
match wallet_result {
Ok(mut wallet) => {
let electrum_url = "ssl://electrum.blockstream.info:60002";
let client = Client::new(electrum_url)?;
let blockchain = ElectrumBlockchain::from(client);
// wallet.add_signer(
// KeychainKind::External,
// SignerOrdering(0),
// Arc::new(signer)
// );
// Sync the wallet with the blockchain
wallet.sync(&blockchain, Default::default())?;
// Recipient address (where funds will be sent)
match wallet.get_balance() {
Ok(balance) => {
println!("Wallet balance: {}", balance);
}
Err(e) => {
println!("Error fetching wallet balance: {:?}", e);
}
}
println!("here");
// // // Step 3: Sign the transaction
// let mut psbt = PartiallySignedTransaction::from_str("TODO: paste the PSBT obtained in step 3 here")?;
// let finalized = wallet.sign(&mut psbt, SignOptions::default());
// println!("{}", psbt);
// }
// Ok(())
// }
// // Step 2: Add the BDK signer
// let mut private_key_str = String::new();
// File::open("key.txt")?.read_to_string(&mut private_key_str)?;
// println!("{}", private_key_str);
let xprv_str = "xprv9yu6ywkjYByRSjaivcWau9vXmwXz8MpwhAMNqwgAqeeXjFTiWuxZpz69LjXTauM4nqYuQpEwPbZ71tKK7hvwehK5k8U6JWW8v9odujQx2en";
let secp = Secp256k1::new();
// Parse the xprv key
let xprv = ExtendedPrivKey::from_str(xprv_str)?;
// Derive the private key (you can adjust the derivation path as needed)
let derivation_path = DerivationPath::from(vec![ChildNumber::from(0)]);
let derived_priv_key = xprv.derive_priv(&secp, &derivation_path)?;
// Convert the derived key to a PrivateKey
let private_key = PrivateKey::new(derived_priv_key.private_key, Network::Bitcoin);
// let private_key = PrivateKey::from_str(&private)?;
let signer = SignerWrapper::new(private_key, SignerContext::Tap { is_internal_key: false });
println!("here");
wallet.add_signer(
KeychainKind::External,
SignerOrdering(0),
Arc::new(signer)
);
// // Step 3: Sign the transaction
// let mut psbt = PartiallySignedTransaction::from_str("TODO: paste the PSBT obtained in step 3 here")?;
let finalized = wallet.sign(&mut psbt, SignOptions::default());
println!("{}", psbt);
}
Err(e) => {
println!("Error creating wallet: {:?}", e);
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
@ -360,10 +396,15 @@ mod tests {
Ok(descriptor) => {
// println!("{}", descriptor);
let _ = fund_psbt(descriptor.clone()).await;
let _ = taker_unresponsive_psbt("tb1qqw8ledhkhezru0rwj7acpker8srtcs28sng0d6");
let psbt = taker_unresponsive_psbt("tb1qqw8ledhkhezru0rwj7acpker8srtcs28sng0d6");
match &psbt {
Ok(psbt)=>{
let _ = taker_unresponsive(psbt.clone());
}
Err(e) => {println!("Error: {}", e)},
}
},
Err(e) => println!("Error: {}", e),
Err(e) => {println!("Error: {}", e)},
}
assert!(result.is_ok());
Ok(())
@ -436,29 +477,4 @@ mod tests {
// let result = blockchain.broadcast(&finalized_tx).await;
// assert!(result.is_ok());
// }
// #[test]
// fn test_taker_unresponsive() {
// let psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000";
// let psbt_bytes = Vec::<u8>::from_hex(psbt_hex).unwrap();
// let psbt: BdkPsbt = deserialize(&psbt_bytes).unwrap();
// let wallet = Wallet::new(
// "tr(youshouldputyourdescriptorhere)",
// None,
// bdk::bitcoin::Network::Testnet,
// MemoryDatabase::new(),
// )
// .unwrap();
// let maker_utxos = vec![];
// let taker_utxos = vec![];
// let recipient_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap();
// let result = taker_unresponsive(psbt, wallet, maker_utxos, taker_utxos, recipient_address);
// assert!(result.is_ok());
// }
}