mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-07-19 17:23:26 +00:00
added signing PSBTs
This commit is contained in:
35
docs/TapTrade_obs/.obsidian/workspace.json
vendored
35
docs/TapTrade_obs/.obsidian/workspace.json
vendored
@ -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"
|
||||
]
|
||||
|
@ -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) => {
|
||||
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);
|
||||
|
||||
// }
|
||||
// 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 });
|
||||
// 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");
|
||||
|
||||
// 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);
|
||||
// }
|
||||
// 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());
|
||||
// }
|
||||
}
|
||||
|
Reference in New Issue
Block a user