mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-07-20 01:33:19 +00:00
add client psbt input assembly/serialization
This commit is contained in:
@ -30,6 +30,8 @@ pub struct BondSubmissionRequest {
|
|||||||
pub taproot_pubkey_hex: String,
|
pub taproot_pubkey_hex: String,
|
||||||
pub musig_pub_nonce_hex: String,
|
pub musig_pub_nonce_hex: String,
|
||||||
pub musig_pubkey_hex: String,
|
pub musig_pubkey_hex: String,
|
||||||
|
pub bdk_psbt_inputs_hex_csv: String,
|
||||||
|
pub client_change_address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response after step2 if offer creation was successful and the offer is now online in the orderbook
|
// Response after step2 if offer creation was successful and the offer is now online in the orderbook
|
||||||
|
@ -338,11 +338,7 @@ fn taker_unresponsive(
|
|||||||
|
|
||||||
println!("here");
|
println!("here");
|
||||||
|
|
||||||
wallet.add_signer(
|
wallet.add_signer(KeychainKind::External, SignerOrdering(0), Arc::new(signer));
|
||||||
KeychainKind::External,
|
|
||||||
SignerOrdering(0),
|
|
||||||
Arc::new(signer)
|
|
||||||
);
|
|
||||||
|
|
||||||
// // Step 3: Sign the transaction
|
// // Step 3: Sign the transaction
|
||||||
// let mut psbt = PartiallySignedTransaction::from_str("TODO: paste the PSBT obtained in step 3 here")?;
|
// let mut psbt = PartiallySignedTransaction::from_str("TODO: paste the PSBT obtained in step 3 here")?;
|
||||||
@ -445,11 +441,7 @@ fn coordinator_sign(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
wallet.add_signer(
|
wallet.add_signer(KeychainKind::External, SignerOrdering(0), Arc::new(signer));
|
||||||
KeychainKind::External,
|
|
||||||
SignerOrdering(0),
|
|
||||||
Arc::new(signer)
|
|
||||||
);
|
|
||||||
// Print the PSBT before signing
|
// Print the PSBT before signing
|
||||||
println!("PSBT before signing: {:?}", psbt);
|
println!("PSBT before signing: {:?}", psbt);
|
||||||
for (i, input) in (0_u32..).zip(psbt.inputs.iter()) {
|
for (i, input) in (0_u32..).zip(psbt.inputs.iter()) {
|
||||||
@ -468,12 +460,11 @@ fn coordinator_sign(
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
println!("Successfully signed PSBT.");
|
println!("Successfully signed PSBT.");
|
||||||
println!("Final PSBT: {:?}", psbt);
|
println!("Final PSBT: {:?}", psbt);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error signing PSBT: {:?}", e);
|
println!("Error signing PSBT: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error creating wallet: {:?}", e);
|
println!("Error creating wallet: {:?}", e);
|
||||||
@ -487,7 +478,7 @@ fn coordinator_sign(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::coordinator;
|
use crate::coordinator;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
// use bdk::blockchain::ElectrumBlockchain;
|
// use bdk::blockchain::ElectrumBlockchain;
|
||||||
// use bdk::sled;
|
// use bdk::sled;
|
||||||
|
@ -133,10 +133,7 @@ pub async fn handle_taker_bond(
|
|||||||
}
|
}
|
||||||
debug!("\nTaker bond validation successful");
|
debug!("\nTaker bond validation successful");
|
||||||
|
|
||||||
let escrow_output_data = match wallet
|
let escrow_output_data = match wallet.get_escrow_psbt(database, &payload).await {
|
||||||
.get_escrow_psbt_outputs(database, &payload.offer.offer_id_hex)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(escrow_output_data) => escrow_output_data,
|
Ok(escrow_output_data) => escrow_output_data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(BondError::CoordinatorError(e.to_string()));
|
return Err(BondError::CoordinatorError(e.to_string()));
|
||||||
|
@ -100,6 +100,8 @@ impl CoordinatorDB {
|
|||||||
bond_amount_sat INTEGER NOT NULL,
|
bond_amount_sat INTEGER NOT NULL,
|
||||||
bond_tx_hex TEXT NOT NULL,
|
bond_tx_hex TEXT NOT NULL,
|
||||||
payout_address TEXT NOT NULL,
|
payout_address TEXT NOT NULL,
|
||||||
|
change_address_maker TEXT NOT NULL,
|
||||||
|
escrow_inputs_hex_maker_csv TEXT NOT NULL,
|
||||||
taproot_pubkey_hex_maker TEXT NOT NULL,
|
taproot_pubkey_hex_maker TEXT NOT NULL,
|
||||||
musig_pub_nonce_hex TEXT NOT NULL,
|
musig_pub_nonce_hex TEXT NOT NULL,
|
||||||
musig_pubkey_hex TEXT NOT NULL,
|
musig_pubkey_hex TEXT NOT NULL,
|
||||||
@ -241,8 +243,9 @@ impl CoordinatorDB {
|
|||||||
);
|
);
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"INSERT OR REPLACE INTO active_maker_offers (offer_id, robohash, is_buy_order, amount_sat,
|
"INSERT OR REPLACE INTO active_maker_offers (offer_id, robohash, is_buy_order, amount_sat,
|
||||||
bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, bond_tx_hex, payout_address, taproot_pubkey_hex_maker, musig_pub_nonce_hex, musig_pubkey_hex, taker_bond_address)
|
bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, bond_tx_hex, payout_address, taproot_pubkey_hex_maker, musig_pub_nonce_hex, musig_pubkey_hex, taker_bond_address,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
change_address_maker, escrow_inputs_hex_maker_csv)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
)
|
)
|
||||||
.bind(offer_id)
|
.bind(offer_id)
|
||||||
.bind(hex::decode(&data.robohash_hex)?)
|
.bind(hex::decode(&data.robohash_hex)?)
|
||||||
@ -258,6 +261,8 @@ impl CoordinatorDB {
|
|||||||
.bind(data.musig_pub_nonce_hex.clone())
|
.bind(data.musig_pub_nonce_hex.clone())
|
||||||
.bind(data.musig_pubkey_hex.clone())
|
.bind(data.musig_pubkey_hex.clone())
|
||||||
.bind(taker_bond_address)
|
.bind(taker_bond_address)
|
||||||
|
.bind(data.client_change_address.clone())
|
||||||
|
.bind(data.bdk_psbt_inputs_hex_csv.clone())
|
||||||
.execute(&*self.db_pool)
|
.execute(&*self.db_pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ use bdk::{
|
|||||||
KeychainKind, SyncOptions, Wallet,
|
KeychainKind, SyncOptions, Wallet,
|
||||||
};
|
};
|
||||||
use coordinator::mempool_monitoring::MempoolHandler;
|
use coordinator::mempool_monitoring::MempoolHandler;
|
||||||
|
use core::panic;
|
||||||
use std::{collections::HashMap, str::FromStr};
|
use std::{collections::HashMap, str::FromStr};
|
||||||
use std::{fmt, ops::Deref};
|
use std::{fmt, ops::Deref};
|
||||||
use utils::*;
|
use utils::*;
|
||||||
@ -236,11 +237,13 @@ impl<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_escrow_psbt_outputs(
|
pub async fn get_escrow_psbt(
|
||||||
&self,
|
&self,
|
||||||
db: &Arc<CoordinatorDB>,
|
db: &Arc<CoordinatorDB>,
|
||||||
trade_id: &str,
|
taker_psbt_request: &OfferPsbtRequest,
|
||||||
) -> Result<EscrowPsbt> {
|
) -> Result<EscrowPsbt> {
|
||||||
|
let trade_id = &taker_psbt_request.offer.offer_id_hex;
|
||||||
|
panic!("adjust");
|
||||||
let escrow_pubkeys = db.fetch_escrow_tx_payout_data(trade_id).await?;
|
let escrow_pubkeys = db.fetch_escrow_tx_payout_data(trade_id).await?;
|
||||||
let coordinator_escrow_pk = self.get_coordinator_taproot_pk().await?;
|
let coordinator_escrow_pk = self.get_coordinator_taproot_pk().await?;
|
||||||
let escrow_output_descriptor =
|
let escrow_output_descriptor =
|
||||||
|
10
taptrade-cli-demo/trader/Cargo.lock
generated
10
taptrade-cli-demo/trader/Cargo.lock
generated
@ -175,6 +175,15 @@ version = "0.9.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
|
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin"
|
name = "bitcoin"
|
||||||
version = "0.30.2"
|
version = "0.30.2"
|
||||||
@ -1587,6 +1596,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bdk",
|
"bdk",
|
||||||
|
"bincode",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"hex",
|
"hex",
|
||||||
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
bdk = "0.29.0"
|
bdk = "0.29.0"
|
||||||
|
bincode = "1.3.3"
|
||||||
dotenvy = "0.15.0"
|
dotenvy = "0.15.0"
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
@ -29,6 +29,8 @@ pub struct BondSubmissionRequest {
|
|||||||
pub taproot_pubkey_hex: String,
|
pub taproot_pubkey_hex: String,
|
||||||
pub musig_pub_nonce_hex: String,
|
pub musig_pub_nonce_hex: String,
|
||||||
pub musig_pubkey_hex: String,
|
pub musig_pubkey_hex: String,
|
||||||
|
pub bdk_psbt_inputs_hex_csv: String,
|
||||||
|
pub client_change_address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response after step2 if offer creation was successful and the offer is now online in the orderbook
|
// Response after step2 if offer creation was successful and the offer is now online in the orderbook
|
||||||
|
@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::{f32::consts::E, str::FromStr, thread::sleep, time::Duration};
|
use std::{f32::consts::E, str::FromStr, thread::sleep, time::Duration};
|
||||||
|
|
||||||
impl BondRequirementResponse {
|
impl BondRequirementResponse {
|
||||||
fn _format_request(trader_setup: &TraderSettings) -> OrderRequest {
|
fn _format_order_request(trader_setup: &TraderSettings) -> OrderRequest {
|
||||||
let amount: u64;
|
let amount: u64;
|
||||||
let is_buy_order = match &trader_setup.trade_type {
|
let is_buy_order = match &trader_setup.trade_type {
|
||||||
OfferType::Buy(val) => {
|
OfferType::Buy(val) => {
|
||||||
@ -46,7 +46,7 @@ impl BondRequirementResponse {
|
|||||||
let endpoint = format!("{}{}", trader_setup.coordinator_endpoint, "/create-offer");
|
let endpoint = format!("{}{}", trader_setup.coordinator_endpoint, "/create-offer");
|
||||||
let res = match client
|
let res = match client
|
||||||
.post(endpoint)
|
.post(endpoint)
|
||||||
.json(&Self::_format_request(trader_setup))
|
.json(&Self::_format_order_request(trader_setup))
|
||||||
.send()
|
.send()
|
||||||
{
|
{
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
|
@ -10,6 +10,10 @@ impl ActiveOffer {
|
|||||||
debug!("Offer conditions fetched: {:#?}", &offer_conditions);
|
debug!("Offer conditions fetched: {:#?}", &offer_conditions);
|
||||||
let (bond, mut musig_data, payout_address) =
|
let (bond, mut musig_data, payout_address) =
|
||||||
trading_wallet.trade_onchain_assembly(&offer_conditions, maker_config)?;
|
trading_wallet.trade_onchain_assembly(&offer_conditions, maker_config)?;
|
||||||
|
|
||||||
|
let (psbt_inputs_hex_csv, escrow_change_address) =
|
||||||
|
trading_wallet.get_escrow_psbt_inputs()?;
|
||||||
|
|
||||||
let submission_result = BondSubmissionRequest::send_maker(
|
let submission_result = BondSubmissionRequest::send_maker(
|
||||||
&maker_config.robosats_robohash_hex,
|
&maker_config.robosats_robohash_hex,
|
||||||
&bond,
|
&bond,
|
||||||
|
@ -13,7 +13,7 @@ use bdk::{
|
|||||||
self,
|
self,
|
||||||
bip32::ExtendedPrivKey,
|
bip32::ExtendedPrivKey,
|
||||||
key::{KeyPair, Secp256k1, XOnlyPublicKey},
|
key::{KeyPair, Secp256k1, XOnlyPublicKey},
|
||||||
psbt::PartiallySignedTransaction,
|
psbt::{serialize, Input, PartiallySignedTransaction},
|
||||||
Address, Network,
|
Address, Network,
|
||||||
},
|
},
|
||||||
blockchain::ElectrumBlockchain,
|
blockchain::ElectrumBlockchain,
|
||||||
@ -28,6 +28,7 @@ use bdk::{
|
|||||||
use bond::Bond;
|
use bond::Bond;
|
||||||
use cli::OfferType;
|
use cli::OfferType;
|
||||||
use musig2::MuSigData;
|
use musig2::MuSigData;
|
||||||
|
use serde::Serialize;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use wallet_utils::get_seed;
|
use wallet_utils::get_seed;
|
||||||
|
|
||||||
@ -89,43 +90,69 @@ impl TradingWallet {
|
|||||||
Ok((bond, musig_data, payout_address))
|
Ok((bond, musig_data, payout_address))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_escrow_psbt(
|
// pub fn get_escrow_psbt(
|
||||||
&self,
|
// &self,
|
||||||
escrow_psbt_requirements: OfferTakenResponse,
|
// escrow_psbt_requirements: OfferTakenResponse,
|
||||||
trader_config: &TraderSettings,
|
// trader_config: &TraderSettings,
|
||||||
) -> Result<PartiallySignedTransaction> {
|
// ) -> Result<PartiallySignedTransaction> {
|
||||||
let fee_output = Address::from_str(&escrow_psbt_requirements.escrow_tx_fee_address)?
|
// let fee_output = Address::from_str(&escrow_psbt_requirements.escrow_tx_fee_address)?
|
||||||
.assume_checked()
|
// .assume_checked()
|
||||||
.script_pubkey();
|
// .script_pubkey();
|
||||||
let escrow_output = {
|
// let escrow_output = {
|
||||||
let temp_wallet = Wallet::new(
|
// let temp_wallet = Wallet::new(
|
||||||
&escrow_psbt_requirements.escrow_output_descriptor,
|
// &escrow_psbt_requirements.escrow_output_descriptor,
|
||||||
None,
|
// None,
|
||||||
Network::Regtest,
|
// Network::Regtest,
|
||||||
MemoryDatabase::new(),
|
// MemoryDatabase::new(),
|
||||||
)?;
|
// )?;
|
||||||
temp_wallet.get_address(AddressIndex::New)?.script_pubkey()
|
// temp_wallet.get_address(AddressIndex::New)?.script_pubkey()
|
||||||
};
|
// };
|
||||||
self.wallet.sync(&self.backend, SyncOptions::default())?;
|
// self.wallet.sync(&self.backend, SyncOptions::default())?;
|
||||||
|
|
||||||
let escrow_amount_sat = match trader_config.trade_type {
|
// let escrow_amount_sat = match trader_config.trade_type {
|
||||||
OfferType::Buy(_) => escrow_psbt_requirements.escrow_amount_taker_sat,
|
// OfferType::Buy(_) => escrow_psbt_requirements.escrow_amount_taker_sat,
|
||||||
OfferType::Sell(_) => escrow_psbt_requirements.escrow_amount_maker_sat,
|
// OfferType::Sell(_) => escrow_psbt_requirements.escrow_amount_maker_sat,
|
||||||
};
|
// };
|
||||||
let (mut psbt, details) = {
|
// let (mut psbt, details) = {
|
||||||
let mut builder = self.wallet.build_tx();
|
// let mut builder = self.wallet.build_tx();
|
||||||
builder
|
// builder
|
||||||
.add_recipient(escrow_output, escrow_amount_sat)
|
// .add_recipient(escrow_output, escrow_amount_sat)
|
||||||
.add_recipient(
|
// .add_recipient(
|
||||||
fee_output,
|
// fee_output,
|
||||||
escrow_psbt_requirements.escrow_fee_sat_per_participant,
|
// escrow_psbt_requirements.escrow_fee_sat_per_participant,
|
||||||
)
|
// )
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(10.0));
|
// .fee_rate(FeeRate::from_sat_per_vb(10.0));
|
||||||
builder.finish()?
|
// builder.finish()?
|
||||||
};
|
// };
|
||||||
debug!("Signing escrow psbt.");
|
// debug!("Signing escrow psbt.");
|
||||||
self.wallet.sign(&mut psbt, SignOptions::default())?;
|
// self.wallet.sign(&mut psbt, SignOptions::default())?;
|
||||||
Ok(psbt)
|
// Ok(psbt)
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// returns suitable inputs (hex, csv serialized) and a change address for the assembly of the escrow psbt (coordinator side)
|
||||||
|
pub fn get_escrow_psbt_inputs(&self, mut amount_sat: i64) -> Result<(String, String)> {
|
||||||
|
let mut inputs: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
self.wallet.sync(&self.backend, SyncOptions::default())?;
|
||||||
|
let available_utxos = self.wallet.list_unspent()?;
|
||||||
|
|
||||||
|
// could use more advanced coin selection if neccessary
|
||||||
|
for utxo in available_utxos {
|
||||||
|
let psbt_input = self.wallet.get_psbt_input(utxo, None, false)?;
|
||||||
|
inputs.push(hex::encode(bincode::serialize(&psbt_input)?));
|
||||||
|
amount_sat -= utxo.txout.value as i64;
|
||||||
|
if amount_sat <= 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let serialized_inputs = inputs.join(",");
|
||||||
|
|
||||||
|
let change_address = self
|
||||||
|
.wallet
|
||||||
|
.get_address(AddressIndex::New)?
|
||||||
|
.address
|
||||||
|
.to_string();
|
||||||
|
Ok((serialized_inputs, change_address))
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate that the taker psbt references the correct inputs and amounts
|
// validate that the taker psbt references the correct inputs and amounts
|
||||||
|
Reference in New Issue
Block a user