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 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

View File

@ -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;

View File

@ -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()));

View File

@ -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?;

View File

@ -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 =

View File

@ -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",

View File

@ -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"

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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