add client psbt input assembly/serialization

This commit is contained in:
f321x
2024-07-31 13:59:07 +02:00
parent 6900df89a8
commit ce24a95c4d
11 changed files with 102 additions and 60 deletions

View File

@ -30,6 +30,8 @@ pub struct BondSubmissionRequest {
pub taproot_pubkey_hex: String,
pub musig_pub_nonce_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

View File

@ -338,11 +338,7 @@ fn taker_unresponsive(
println!("here");
wallet.add_signer(
KeychainKind::External,
SignerOrdering(0),
Arc::new(signer)
);
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")?;
@ -445,11 +441,7 @@ fn coordinator_sign(
},
);
wallet.add_signer(
KeychainKind::External,
SignerOrdering(0),
Arc::new(signer)
);
wallet.add_signer(KeychainKind::External, SignerOrdering(0), Arc::new(signer));
// Print the PSBT before signing
println!("PSBT before signing: {:?}", psbt);
for (i, input) in (0_u32..).zip(psbt.inputs.iter()) {
@ -468,12 +460,11 @@ fn coordinator_sign(
Ok(_) => {
println!("Successfully signed PSBT.");
println!("Final PSBT: {:?}", psbt);
},
}
Err(e) => {
println!("Error signing PSBT: {:?}", e);
}
}
}
Err(e) => {
println!("Error creating wallet: {:?}", e);
@ -487,7 +478,7 @@ fn coordinator_sign(
mod tests {
use crate::coordinator;
use super::*;
use super::*;
use anyhow::{Context, Error};
// use bdk::blockchain::ElectrumBlockchain;
// use bdk::sled;

View File

@ -133,10 +133,7 @@ pub async fn handle_taker_bond(
}
debug!("\nTaker bond validation successful");
let escrow_output_data = match wallet
.get_escrow_psbt_outputs(database, &payload.offer.offer_id_hex)
.await
{
let escrow_output_data = match wallet.get_escrow_psbt(database, &payload).await {
Ok(escrow_output_data) => escrow_output_data,
Err(e) => {
return Err(BondError::CoordinatorError(e.to_string()));

View File

@ -100,6 +100,8 @@ impl CoordinatorDB {
bond_amount_sat INTEGER NOT NULL,
bond_tx_hex 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,
musig_pub_nonce_hex TEXT NOT NULL,
musig_pubkey_hex TEXT NOT NULL,
@ -241,8 +243,9 @@ impl CoordinatorDB {
);
sqlx::query(
"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)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
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,
change_address_maker, escrow_inputs_hex_maker_csv)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
)
.bind(offer_id)
.bind(hex::decode(&data.robohash_hex)?)
@ -258,6 +261,8 @@ impl CoordinatorDB {
.bind(data.musig_pub_nonce_hex.clone())
.bind(data.musig_pubkey_hex.clone())
.bind(taker_bond_address)
.bind(data.client_change_address.clone())
.bind(data.bdk_psbt_inputs_hex_csv.clone())
.execute(&*self.db_pool)
.await?;

View File

@ -25,6 +25,7 @@ use bdk::{
KeychainKind, SyncOptions, Wallet,
};
use coordinator::mempool_monitoring::MempoolHandler;
use core::panic;
use std::{collections::HashMap, str::FromStr};
use std::{fmt, ops::Deref};
use utils::*;
@ -236,11 +237,13 @@ impl<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
Ok(())
}
pub async fn get_escrow_psbt_outputs(
pub async fn get_escrow_psbt(
&self,
db: &Arc<CoordinatorDB>,
trade_id: &str,
taker_psbt_request: &OfferPsbtRequest,
) -> 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 coordinator_escrow_pk = self.get_coordinator_taproot_pk().await?;
let escrow_output_descriptor =

View File

@ -175,6 +175,15 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitcoin"
version = "0.30.2"
@ -1587,6 +1596,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"bdk",
"bincode",
"dotenvy",
"env_logger",
"hex",

View File

@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
anyhow = "1.0.86"
bdk = "0.29.0"
bincode = "1.3.3"
dotenvy = "0.15.0"
env_logger = "0.11.3"
hex = "0.4.3"

View File

@ -29,6 +29,8 @@ pub struct BondSubmissionRequest {
pub taproot_pubkey_hex: String,
pub musig_pub_nonce_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

View File

@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize};
use std::{f32::consts::E, str::FromStr, thread::sleep, time::Duration};
impl BondRequirementResponse {
fn _format_request(trader_setup: &TraderSettings) -> OrderRequest {
fn _format_order_request(trader_setup: &TraderSettings) -> OrderRequest {
let amount: u64;
let is_buy_order = match &trader_setup.trade_type {
OfferType::Buy(val) => {
@ -46,7 +46,7 @@ impl BondRequirementResponse {
let endpoint = format!("{}{}", trader_setup.coordinator_endpoint, "/create-offer");
let res = match client
.post(endpoint)
.json(&Self::_format_request(trader_setup))
.json(&Self::_format_order_request(trader_setup))
.send()
{
Ok(res) => res,

View File

@ -10,6 +10,10 @@ impl ActiveOffer {
debug!("Offer conditions fetched: {:#?}", &offer_conditions);
let (bond, mut musig_data, payout_address) =
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(
&maker_config.robosats_robohash_hex,
&bond,

View File

@ -13,7 +13,7 @@ use bdk::{
self,
bip32::ExtendedPrivKey,
key::{KeyPair, Secp256k1, XOnlyPublicKey},
psbt::PartiallySignedTransaction,
psbt::{serialize, Input, PartiallySignedTransaction},
Address, Network,
},
blockchain::ElectrumBlockchain,
@ -28,6 +28,7 @@ use bdk::{
use bond::Bond;
use cli::OfferType;
use musig2::MuSigData;
use serde::Serialize;
use std::str::FromStr;
use wallet_utils::get_seed;
@ -89,43 +90,69 @@ impl TradingWallet {
Ok((bond, musig_data, payout_address))
}
pub fn get_escrow_psbt(
&self,
escrow_psbt_requirements: OfferTakenResponse,
trader_config: &TraderSettings,
) -> Result<PartiallySignedTransaction> {
let fee_output = Address::from_str(&escrow_psbt_requirements.escrow_tx_fee_address)?
.assume_checked()
.script_pubkey();
let escrow_output = {
let temp_wallet = Wallet::new(
&escrow_psbt_requirements.escrow_output_descriptor,
None,
Network::Regtest,
MemoryDatabase::new(),
)?;
temp_wallet.get_address(AddressIndex::New)?.script_pubkey()
};
self.wallet.sync(&self.backend, SyncOptions::default())?;
// pub fn get_escrow_psbt(
// &self,
// escrow_psbt_requirements: OfferTakenResponse,
// trader_config: &TraderSettings,
// ) -> Result<PartiallySignedTransaction> {
// let fee_output = Address::from_str(&escrow_psbt_requirements.escrow_tx_fee_address)?
// .assume_checked()
// .script_pubkey();
// let escrow_output = {
// let temp_wallet = Wallet::new(
// &escrow_psbt_requirements.escrow_output_descriptor,
// None,
// Network::Regtest,
// MemoryDatabase::new(),
// )?;
// temp_wallet.get_address(AddressIndex::New)?.script_pubkey()
// };
// self.wallet.sync(&self.backend, SyncOptions::default())?;
let escrow_amount_sat = match trader_config.trade_type {
OfferType::Buy(_) => escrow_psbt_requirements.escrow_amount_taker_sat,
OfferType::Sell(_) => escrow_psbt_requirements.escrow_amount_maker_sat,
};
let (mut psbt, details) = {
let mut builder = self.wallet.build_tx();
builder
.add_recipient(escrow_output, escrow_amount_sat)
.add_recipient(
fee_output,
escrow_psbt_requirements.escrow_fee_sat_per_participant,
)
.fee_rate(FeeRate::from_sat_per_vb(10.0));
builder.finish()?
};
debug!("Signing escrow psbt.");
self.wallet.sign(&mut psbt, SignOptions::default())?;
Ok(psbt)
// let escrow_amount_sat = match trader_config.trade_type {
// OfferType::Buy(_) => escrow_psbt_requirements.escrow_amount_taker_sat,
// OfferType::Sell(_) => escrow_psbt_requirements.escrow_amount_maker_sat,
// };
// let (mut psbt, details) = {
// let mut builder = self.wallet.build_tx();
// builder
// .add_recipient(escrow_output, escrow_amount_sat)
// .add_recipient(
// fee_output,
// escrow_psbt_requirements.escrow_fee_sat_per_participant,
// )
// .fee_rate(FeeRate::from_sat_per_vb(10.0));
// builder.finish()?
// };
// debug!("Signing escrow psbt.");
// self.wallet.sign(&mut psbt, SignOptions::default())?;
// 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