mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-07-25 20:23:18 +00:00
testnet faucet comment
This commit is contained in:
@ -9,7 +9,7 @@ bitcoin = "0.32.2"
|
|||||||
miniscript = "12.0.0"
|
miniscript = "12.0.0"
|
||||||
axum = { version = "0.7.5", features = ["tokio", "json"] }
|
axum = { version = "0.7.5", features = ["tokio", "json"] }
|
||||||
# "use-esplora-async", "async-interface", for async esplora
|
# "use-esplora-async", "async-interface", for async esplora
|
||||||
bdk = { version = "0.29.0", default-features = false, features = ["key-value-db", "bitcoinconsensus", "std", "electrum","use-esplora-ureq","compiler", "verify"] }
|
bdk = { version = "0.29.0", default-features = false, features = ["key-value-db", "bitcoinconsensus", "std", "electrum", "use-esplora-ureq","compiler", "verify"] }
|
||||||
# bitcoinconsensus = "0.106.0"
|
# bitcoinconsensus = "0.106.0"
|
||||||
|
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
@ -2,167 +2,192 @@
|
|||||||
/// It includes functions to combine and broadcast the partially signed transactions (PSBTs)
|
/// It includes functions to combine and broadcast the partially signed transactions (PSBTs)
|
||||||
/// from multiple participants, create a Taproot script descriptor, create a PSBT from the
|
/// from multiple participants, create a Taproot script descriptor, create a PSBT from the
|
||||||
/// descriptor, and handle the case when the taker is unresponsive.
|
/// descriptor, and handle the case when the taker is unresponsive.
|
||||||
|
|
||||||
use bdk::bitcoin::address::NetworkUnchecked;
|
use bdk::bitcoin::address::NetworkUnchecked;
|
||||||
use bitcoin::address::NetworkChecked;
|
|
||||||
use bitcoin::Address;
|
|
||||||
use bdk::descriptor::Descriptor;
|
|
||||||
use bdk::miniscript::psbt::PsbtExt;
|
|
||||||
use bdk::bitcoin::psbt::PartiallySignedTransaction;
|
use bdk::bitcoin::psbt::PartiallySignedTransaction;
|
||||||
use bdk::blockchain::EsploraBlockchain;
|
|
||||||
use bdk::SignOptions;
|
|
||||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||||
|
use bdk::blockchain::EsploraBlockchain;
|
||||||
|
use bdk::database::MemoryDatabase;
|
||||||
|
use bdk::descriptor::Descriptor;
|
||||||
use bdk::miniscript::descriptor::TapTree;
|
use bdk::miniscript::descriptor::TapTree;
|
||||||
use bdk::miniscript::policy::Concrete;
|
use bdk::miniscript::policy::Concrete;
|
||||||
use std::sync::Arc;
|
use bdk::miniscript::psbt::PsbtExt;
|
||||||
use bdk::database::MemoryDatabase;
|
|
||||||
use bdk::wallet::AddressIndex;
|
use bdk::wallet::AddressIndex;
|
||||||
use bdk::{FeeRate, Wallet, KeychainKind, SyncOptions};
|
use bdk::SignOptions;
|
||||||
|
use bdk::{FeeRate, KeychainKind, SyncOptions, Wallet};
|
||||||
|
use bitcoin::address::NetworkChecked;
|
||||||
|
use bitcoin::Address;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// The main function in this module is `combine_and_broadcast`, which combines the PSBTs
|
/// The main function in this module is `combine_and_broadcast`, which combines the PSBTs
|
||||||
/// from the maker and taker, finalizes the transaction, and broadcasts it on the blockchain.
|
/// from the maker and taker, finalizes the transaction, and broadcasts it on the blockchain.
|
||||||
pub async fn combine_and_broadcast() -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn combine_and_broadcast() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut base_psbt = PartiallySignedTransaction::from_str("TODO: insert the psbt created in step 3 here")?;
|
let mut base_psbt =
|
||||||
let signed_psbts = vec![
|
PartiallySignedTransaction::from_str("TODO: insert the psbt created in step 3 here")?;
|
||||||
// TODO: Paste each participant's PSBT here
|
let signed_psbts = vec![
|
||||||
"makers_psbt",
|
// TODO: Paste each participant's PSBT here
|
||||||
"takers_psbt",
|
"makers_psbt",
|
||||||
];
|
"takers_psbt",
|
||||||
|
];
|
||||||
|
|
||||||
for psbt in signed_psbts {
|
for psbt in signed_psbts {
|
||||||
let psbt = PartiallySignedTransaction::from_str(psbt)?;
|
let psbt = PartiallySignedTransaction::from_str(psbt)?;
|
||||||
base_psbt.combine(psbt)?;
|
base_psbt.combine(psbt)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let secp = Secp256k1::new();
|
let secp = Secp256k1::new();
|
||||||
let psbt = base_psbt.finalize(&secp).unwrap();
|
let psbt = base_psbt.finalize(&secp).unwrap();
|
||||||
let finalized_tx = psbt.extract_tx();
|
let finalized_tx = psbt.extract_tx();
|
||||||
dbg!(finalized_tx.txid());
|
dbg!(finalized_tx.txid());
|
||||||
|
|
||||||
let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20);
|
let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20);
|
||||||
dbg!(blockchain.broadcast(&finalized_tx));
|
dbg!(blockchain.broadcast(&finalized_tx));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/// Other functions include `create_script`, which creates a Taproot script descriptor from
|
/// Other functions include `create_script`, which creates a Taproot script descriptor from
|
||||||
async fn create_script(coordinator_key: &str, maker_key:&str, taker_key:&str ) -> Result<(bdk::descriptor::Descriptor<std::string::String>), Box<dyn std::error::Error>> {
|
async fn create_script(
|
||||||
|
coordinator_key: &str,
|
||||||
|
maker_key: &str,
|
||||||
|
taker_key: &str,
|
||||||
|
) -> Result<(bdk::descriptor::Descriptor<std::string::String>), Box<dyn std::error::Error>> {
|
||||||
|
// let maker_key = "020202020202020202020202020202020202020202020202020202020202020202";
|
||||||
|
// let taker_key = "03833be68fb7559c0e62ffdbb6d46cc44a58c19c6ba82e51144b583cff0519c791";
|
||||||
|
// let coordinator_key = "03b2f6e8abf3624f8e9b93f7b2567b158c15b0f20ab368f9fcb2d9251d6a788d09";
|
||||||
|
|
||||||
// let maker_key = "020202020202020202020202020202020202020202020202020202020202020202";
|
// Define policies based on the scripts provided
|
||||||
// let taker_key = "03833be68fb7559c0e62ffdbb6d46cc44a58c19c6ba82e51144b583cff0519c791";
|
let script_a = format!(
|
||||||
// let coordinator_key = "03b2f6e8abf3624f8e9b93f7b2567b158c15b0f20ab368f9fcb2d9251d6a788d09";
|
"and(and(after(escrow_timer),pk({})),pk({}))",
|
||||||
|
maker_key, coordinator_key
|
||||||
|
);
|
||||||
|
let script_b = format!(
|
||||||
|
"and_v(v:pk({}),and_v(v:pk({}),pk({})))",
|
||||||
|
maker_key, taker_key, coordinator_key
|
||||||
|
);
|
||||||
|
let script_c = format!("and(pk({}),pk({}))", maker_key, coordinator_key);
|
||||||
|
let script_d = format!("and(pk({}),pk({}))", taker_key, coordinator_key);
|
||||||
|
let script_e = format!("and(pk({}),after(very_long_timelock))", maker_key);
|
||||||
|
let script_f = format!(
|
||||||
|
"and_v(and_v(v:pk({}),v:pk({})),after(2048))",
|
||||||
|
maker_key, taker_key
|
||||||
|
);
|
||||||
|
|
||||||
// Define policies based on the scripts provided
|
// Compile the policies
|
||||||
let script_a = format!("and(and(after(escrow_timer),pk({})),pk({}))", maker_key, coordinator_key);
|
let compiled_a = Concrete::<String>::from_str(&script_a)?.compile()?;
|
||||||
let script_b = format!("and_v(v:pk({}),and_v(v:pk({}),pk({})))", maker_key, taker_key, coordinator_key);
|
let compiled_b = Concrete::<String>::from_str(&script_b)?.compile()?;
|
||||||
let script_c = format!("and(pk({}),pk({}))", maker_key, coordinator_key);
|
let compiled_c = Concrete::<String>::from_str(&script_c)?.compile()?;
|
||||||
let script_d = format!("and(pk({}),pk({}))", taker_key, coordinator_key);
|
let compiled_d = Concrete::<String>::from_str(&script_d)?.compile()?;
|
||||||
let script_e = format!("and(pk({}),after(very_long_timelock))", maker_key);
|
let compiled_e = Concrete::<String>::from_str(&script_e)?.compile()?;
|
||||||
let script_f = format!("and_v(and_v(v:pk({}),v:pk({})),after(2048))", maker_key, taker_key);
|
let compiled_f = Concrete::<String>::from_str(&script_f)?.compile()?;
|
||||||
|
|
||||||
// Compile the policies
|
// Create TapTree leaves
|
||||||
let compiled_a = Concrete::<String>::from_str(&script_a)?.compile()?;
|
let tap_leaf_a = TapTree::Leaf(Arc::new(compiled_a));
|
||||||
let compiled_b = Concrete::<String>::from_str(&script_b)?.compile()?;
|
let tap_leaf_b = TapTree::Leaf(Arc::new(compiled_b));
|
||||||
let compiled_c = Concrete::<String>::from_str(&script_c)?.compile()?;
|
let tap_leaf_c = TapTree::Leaf(Arc::new(compiled_c));
|
||||||
let compiled_d = Concrete::<String>::from_str(&script_d)?.compile()?;
|
let tap_leaf_d = TapTree::Leaf(Arc::new(compiled_d));
|
||||||
let compiled_e = Concrete::<String>::from_str(&script_e)?.compile()?;
|
let tap_leaf_e = TapTree::Leaf(Arc::new(compiled_e));
|
||||||
let compiled_f = Concrete::<String>::from_str(&script_f)?.compile()?;
|
let tap_leaf_f = TapTree::Leaf(Arc::new(compiled_f));
|
||||||
|
|
||||||
// Create TapTree leaves
|
// Create the TapTree (example combining leaves, adjust as necessary)
|
||||||
let tap_leaf_a = TapTree::Leaf(Arc::new(compiled_a));
|
let tap_tree = TapTree::Tree(Arc::new(tap_leaf_a), Arc::new(tap_leaf_b));
|
||||||
let tap_leaf_b = TapTree::Leaf(Arc::new(compiled_b));
|
|
||||||
let tap_leaf_c = TapTree::Leaf(Arc::new(compiled_c));
|
|
||||||
let tap_leaf_d = TapTree::Leaf(Arc::new(compiled_d));
|
|
||||||
let tap_leaf_e = TapTree::Leaf(Arc::new(compiled_e));
|
|
||||||
let tap_leaf_f = TapTree::Leaf(Arc::new(compiled_f));
|
|
||||||
|
|
||||||
// Create the TapTree (example combining leaves, adjust as necessary)
|
// Define a dummy internal key (replace with an actual key)
|
||||||
let tap_tree = TapTree::Tree(Arc::new(tap_leaf_a), Arc::new(tap_leaf_b));
|
let dummy_internal_key =
|
||||||
|
"020202020202020202020202020202020202020202020202020202020202020202".to_string();
|
||||||
|
|
||||||
// Define a dummy internal key (replace with an actual key)
|
// Create the descriptor
|
||||||
let dummy_internal_key = "020202020202020202020202020202020202020202020202020202020202020202".to_string();
|
let descriptor = Descriptor::new_tr(dummy_internal_key, Some(tap_tree))?;
|
||||||
|
println!("{}", descriptor);
|
||||||
// Create the descriptor
|
|
||||||
let descriptor = Descriptor::new_tr(dummy_internal_key, Some(tap_tree))?;
|
|
||||||
println!("{}", descriptor);
|
|
||||||
|
|
||||||
Ok(descriptor)
|
|
||||||
|
|
||||||
|
Ok(descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the provided keys, and `create_psbt`, which creates a PSBT from the descriptor
|
/// the provided keys, and `create_psbt`, which creates a PSBT from the descriptor
|
||||||
/// Figure out how to put UTXO's
|
/// Figure out how to put UTXO's
|
||||||
pub async fn create_psbt(descriptor: Descriptor<String>)-> Result<(PartiallySignedTransaction), Box<dyn std::error::Error>> {
|
pub async fn create_psbt(
|
||||||
// Step 1: Create a BDK wallet
|
descriptor: Descriptor<String>,
|
||||||
let wallet = Wallet::new(
|
) -> Result<(PartiallySignedTransaction), Box<dyn std::error::Error>> {
|
||||||
// TODO: insert your descriptor here
|
// Step 1: Create a BDK wallet
|
||||||
"tr(youshouldputyourdescriptorhere)",
|
let wallet = Wallet::new(
|
||||||
None,
|
// TODO: insert your descriptor here
|
||||||
bdk::bitcoin::Network::Testnet,
|
"tr(youshouldputyourdescriptorhere)",
|
||||||
MemoryDatabase::new()
|
None,
|
||||||
)?;
|
bdk::bitcoin::Network::Testnet,
|
||||||
|
MemoryDatabase::new(),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Step 2: Print the first address
|
// Step 2: Print the first address
|
||||||
println!("Deposit funds here: {:?}", wallet.get_address(AddressIndex::New)?);
|
println!(
|
||||||
|
"Deposit funds here: {:?}",
|
||||||
|
wallet.get_address(AddressIndex::New)?
|
||||||
|
);
|
||||||
|
|
||||||
// Step 3: Deposit funds
|
// Step 3: Deposit funds
|
||||||
// Use some testnet faucet, such as https://bitcoinfaucet.uo1.net/send.php
|
// Use some testnet faucet, such as https://bitcoinfaucet.uo1.net/send.php
|
||||||
|
// https://coinfaucet.eu/en/btc-testnet4/
|
||||||
|
|
||||||
// Step 4: Print balance
|
// Step 4: Print balance
|
||||||
let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20);
|
let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20);
|
||||||
wallet.sync(&blockchain, SyncOptions::default())?;
|
wallet.sync(&blockchain, SyncOptions::default())?;
|
||||||
println!("{:#?}", wallet.get_balance()?);
|
println!("{:#?}", wallet.get_balance()?);
|
||||||
|
|
||||||
let maker_utxos = vec![/* UTXO details here */];
|
let maker_utxos = vec![/* UTXO details here */];
|
||||||
let taker_utxos = vec![/* UTXO details here */];
|
let taker_utxos = vec![/* UTXO details here */];
|
||||||
|
|
||||||
//TODO: Change type to NetworkChecked
|
//TODO: Change type to NetworkChecked
|
||||||
// Recipient address (where funds will be sent)
|
// Recipient address (where funds will be sent)
|
||||||
let recipient_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?;
|
let recipient_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?;
|
||||||
|
|
||||||
|
// Build the PSBT
|
||||||
|
let mut tx_builder = wallet.build_tx();
|
||||||
|
tx_builder
|
||||||
|
.add_utxos(&maker_utxos)?
|
||||||
|
.add_utxos(&taker_utxos)?
|
||||||
|
.drain_wallet()
|
||||||
|
.drain_to(recipient_address.script_pubkey())
|
||||||
|
.fee_rate(FeeRate::from_sat_per_vb(3.0))
|
||||||
|
.policy_path(BTreeMap::new(), KeychainKind::External);
|
||||||
|
|
||||||
// Build the PSBT
|
let (psbt, tx_details) = tx_builder.finish()?;
|
||||||
let mut tx_builder = wallet.build_tx();
|
println!("PSBT: {:?}", psbt);
|
||||||
tx_builder
|
Ok(psbt)
|
||||||
.add_utxos(&maker_utxos)?
|
|
||||||
.add_utxos(&taker_utxos)?
|
|
||||||
.drain_wallet()
|
|
||||||
.drain_to(recipient_address.script_pubkey())
|
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(3.0))
|
|
||||||
.policy_path(BTreeMap::new(), KeychainKind::External);
|
|
||||||
|
|
||||||
let (psbt, tx_details) = tx_builder.finish()?;
|
|
||||||
println!("PSBT: {:?}", psbt);
|
|
||||||
Ok(psbt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The `taker_unresponsive` function handles the case when the taker is unresponsive and
|
/// The `taker_unresponsive` function handles the case when the taker is unresponsive and
|
||||||
/// the coordinator needs to sign the PSBT using an alternative path.
|
/// the coordinator needs to sign the PSBT using an alternative path.
|
||||||
// TODO: Figure out how to use UTXO's
|
// 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<NetworkChecked>) -> Result<(), Box<dyn std::error::Error>> {
|
fn taker_unresponsive(
|
||||||
// Maker signs the PSBT
|
psbt: PartiallySignedTransaction,
|
||||||
let maker_signed_psbt = wallet.sign(&mut psbt.clone(), SignOptions::default())?;
|
wallet: Wallet<MemoryDatabase>,
|
||||||
println!("Maker signed PSBT: {:?}", maker_signed_psbt);
|
maker_utxos: Vec<UTXO>,
|
||||||
|
taker_utxos: Vec<UTXO>,
|
||||||
|
recipient_address: Address<NetworkChecked>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Maker signs the PSBT
|
||||||
|
let maker_signed_psbt = wallet.sign(&mut psbt.clone(), SignOptions::default())?;
|
||||||
|
println!("Maker signed PSBT: {:?}", maker_signed_psbt);
|
||||||
|
|
||||||
// If taker is unresponsive, coordinator signs using alternative path
|
// If taker is unresponsive, coordinator signs using alternative path
|
||||||
let taker_responsive = false; // Assume taker is unresponsive
|
let taker_responsive = false; // Assume taker is unresponsive
|
||||||
if !taker_responsive {
|
if !taker_responsive {
|
||||||
let mut path = BTreeMap::new();
|
let mut path = BTreeMap::new();
|
||||||
path.insert(wallet.policies(KeychainKind::External)?.unwrap().id, vec![1]); // Path for coordinator and maker
|
path.insert(
|
||||||
|
wallet.policies(KeychainKind::External)?.unwrap().id,
|
||||||
|
vec![1],
|
||||||
|
); // Path for coordinator and maker
|
||||||
|
|
||||||
let mut coordinator_tx_builder = wallet.build_tx();
|
let mut coordinator_tx_builder = wallet.build_tx();
|
||||||
coordinator_tx_builder
|
coordinator_tx_builder
|
||||||
.add_utxos(&maker_utxos)?
|
.add_utxos(&maker_utxos)?
|
||||||
.add_utxos(&taker_utxos)?
|
.add_utxos(&taker_utxos)?
|
||||||
.drain_wallet()
|
.drain_wallet()
|
||||||
.drain_to(recipient_address.script_pubkey())
|
.drain_to(recipient_address.script_pubkey())
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(3.0))
|
.fee_rate(FeeRate::from_sat_per_vb(3.0))
|
||||||
.policy_path(path, KeychainKind::External);
|
.policy_path(path, KeychainKind::External);
|
||||||
|
|
||||||
let (coordinator_psbt, _details) = coordinator_tx_builder.finish()?;
|
let (coordinator_psbt, _details) = coordinator_tx_builder.finish()?;
|
||||||
let coordinator_signed_psbt = wallet.sign(&mut coordinator_psbt, SignOptions::default())?;
|
let coordinator_signed_psbt = wallet.sign(&mut coordinator_psbt, SignOptions::default())?;
|
||||||
println!("Coordinator signed PSBT: {:?}", coordinator_signed_psbt);
|
println!("Coordinator signed PSBT: {:?}", coordinator_signed_psbt);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user