many more comments, small improvements (faster polling)

This commit is contained in:
fbock
2024-08-27 16:41:42 +02:00
parent 2687a7084c
commit 183eaf07f5
10 changed files with 112 additions and 77 deletions

View File

@ -6,6 +6,8 @@ pub mod tx_confirmation_monitoring;
use super::*;
/// Accepts the request to create a new offer, inserts it in the database and
/// returns the required bond information to the maker.
pub async fn process_order(
coordinator: Arc<Coordinator>,
offer: &OfferRequest,
@ -28,6 +30,8 @@ pub async fn process_order(
Ok(bond_requirements)
}
/// Accepts the signed bond transaction passed by the maker, validates it and inserts it in the database for further monitoring.
/// Moves the offer from the pending table to the active_maker_offers table ("the Orderbook").
pub async fn handle_maker_bond(
payload: &BondSubmissionRequest,
coordinator: Arc<Coordinator>,
@ -35,23 +39,30 @@ pub async fn handle_maker_bond(
let wallet = &coordinator.coordinator_wallet;
let database = &coordinator.coordinator_db;
// get the according bond requirements from the database to validate against them
let bond_requirements = database
.fetch_bond_requirements(&payload.robohash_hex)
.await
.map_err(|_| BondError::BondNotFound)?;
// validate the signed bond transaction
wallet
.validate_bond_tx_hex(&payload.signed_bond_hex, &bond_requirements)
.await
.map_err(|e| BondError::InvalidBond(e.to_string()))?;
debug!("\nBond validation successful");
// generates a random offer id to be able to identify the offer
let offer_id_hex: String = generate_random_order_id(16); // 16 bytes random offer id, maybe a different system makes more sense later on? (uuid or increasing counter...)
// create address for taker bond
// get new address for the taker bond to which the taker has to lock its bond when accepting this offer
let new_taker_bond_address = wallet
.get_new_address()
.await
.map_err(|e| BondError::CoordinatorError(e.to_string()))?;
// move the offer from the pending table to the active_maker_offers table in the database, returns the unix timestamp until the
// bond is being monitored (the inputs shouldn't be touched by the trader except for the following escrow transaction)
let bond_locked_until_timestamp = database
.move_offer_to_active(payload, &offer_id_hex, new_taker_bond_address)
.await
@ -63,6 +74,7 @@ pub async fn handle_maker_bond(
})
}
/// fetches all offers from the database that are suitable for the trade requested by the taker
pub async fn get_public_offers(
request: &OffersRequest,
coordinator: Arc<Coordinator>,
@ -80,6 +92,9 @@ pub async fn get_public_offers(
Ok(PublicOffers { offers })
}
/// Accepts the request of the taker to take an offer, validates the taker bond tx that is passed with the request,
/// creates the escrow locking transaction and moves all information to the taken offers db table. Returns the
/// information necessary for the taker to sign its input to the escrow locking psbt
pub async fn handle_taker_bond(
payload: &OfferPsbtRequest,
coordinator: Arc<Coordinator>,
@ -87,11 +102,13 @@ pub async fn handle_taker_bond(
let wallet = &coordinator.coordinator_wallet;
let database = &coordinator.coordinator_db;
// fetch the bond requirements for the taker bond from the database
let bond_requirements = database
.fetch_taker_bond_requirements(&payload.offer.offer_id_hex)
.await
.map_err(|_| BondError::BondNotFound)?;
// validate the signed taker bond transaction against the requirements
wallet
.validate_bond_tx_hex(&payload.trade_data.signed_bond_hex, &bond_requirements)
.await
@ -99,6 +116,7 @@ pub async fn handle_taker_bond(
debug!("\nTaker bond validation successful");
// create the escrow locking transaction
let escrow_output_data = wallet
.create_escrow_psbt(database, payload)
.await
@ -108,6 +126,7 @@ pub async fn handle_taker_bond(
escrow_output_data
);
// add the taker information to the database and move the offer to the taken_offers table
database
.add_taker_info_and_move_table(payload, &escrow_output_data)
.await
@ -123,6 +142,10 @@ pub async fn handle_taker_bond(
})
}
/// gets called by the polling endpoint the maker polls when waiting for an offer taker,
/// looks in the database if escrow output information is available for the offer id
/// which means the offer has been taken, returns the escrow locking tx information if
/// the offer has been taken so the maker can sign its input to it.
pub async fn get_offer_status_maker(
payload: &OfferTakenRequest,
coordinator: Arc<Coordinator>,
@ -157,6 +180,9 @@ pub async fn get_offer_status_maker(
})
}
/// gets polled by both traders so they can see if the exchange can safely begin.
/// checks the database for the confirmation flag of the escrow transaction which is
/// set by the concurrent confirmation monitoring task
pub async fn fetch_escrow_confirmation_status(
payload: &OfferTakenRequest,
coordinator: Arc<Coordinator>,
@ -178,6 +204,9 @@ pub async fn fetch_escrow_confirmation_status(
.map_err(|e| FetchEscrowConfirmationError::Database(e.to_string()))
}
/// handles the returned signed escrow locking psbt of both traders, if both are present in the db
/// it combines them and broadcasts the escrow transaction to the network
/// otherwise the tx will just get stored in the db.
pub async fn handle_signed_escrow_psbt(
payload: &PsbtSubmissionRequest,
coordinator: Arc<Coordinator>,
@ -223,6 +252,7 @@ pub async fn handle_signed_escrow_psbt(
Ok(())
}
/// sets the trader happy flag in the database which both traders have to either set true or false to continue with payout or escrow procedure
pub async fn handle_obligation_confirmation(
payload: &OfferTakenRequest,
coordinator: Arc<Coordinator>,
@ -237,6 +267,8 @@ pub async fn handle_obligation_confirmation(
Ok(())
}
/// if a trader requests escrow this function sets the trader happy flag to false in the db. Then a CLI for the coordinator should be opened
/// to decide which trader is correct
pub async fn initiate_escrow(
payload: &TradeObligationsUnsatisfied,
coordinator: Arc<Coordinator>,
@ -252,6 +284,8 @@ pub async fn initiate_escrow(
Ok(())
}
/// if both traders are happy this function will assemble the final keyspend payout transaction and return it to the traders
/// for them to be able to create the partial signatures
pub async fn handle_final_payout(
payload: &OfferTakenRequest,
coordinator: Arc<Coordinator>,
@ -263,6 +297,7 @@ pub async fn handle_final_payout(
.await
.map_err(|e| RequestError::Database(e.to_string()))?;
// both traders are happy, keyspend payout can begin
if trader_happiness.maker_happy.is_some_and(|x| x)
&& trader_happiness.taker_happy.is_some_and(|x| x)
{
@ -306,6 +341,7 @@ pub async fn handle_final_payout(
agg_musig_nonce_hex: escrow_payout_data.agg_musig_nonce.to_string(),
agg_musig_pubkey_ctx_hex: escrow_payout_data.aggregated_musig_pubkey_ctx_hex,
}));
// at least one trader has not yet submitted the satisfaction request, or a escrow is already ongoing
} else if (trader_happiness.maker_happy.is_none() || trader_happiness.taker_happy.is_none())
&& !trader_happiness.escrow_ongoing
{
@ -340,13 +376,13 @@ pub async fn handle_final_payout(
}
}
/// handles the returned partial signatures for the keyspend payout, if both are available it aggregates them,
/// inserts the signature in the payout tx and broadcasts it
pub async fn handle_payout_signature(
payload: &PayoutSignatureRequest,
coordinator: Arc<Coordinator>,
) -> Result<bool, RequestError> {
let database = &coordinator.coordinator_db;
// let _wallet = &coordinator.coordinator_wallet;
check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await?;
database

View File

@ -208,11 +208,14 @@ impl<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
})
}
/// placeholder to validate the returned, signed escrow psbt
pub async fn validate_escrow_init_psbt(&self, _escrow_init_psbt: &str) -> Result<()> {
warn!("Implement escrow psbt validation. For now, returning Ok");
Ok(())
}
/// combines the two signed, hex serialized escrow locking psbts returned by the traders, finalizes it
/// and broadcasts the escrow transaction
pub async fn combine_and_broadcast_escrow_psbt(
&self,
signed_maker_psbt_hex: &str,

View File

@ -4,6 +4,8 @@ use bdk::FeeRate;
use super::*;
use bitcoin;
/// get current feerate from blockchain backend and calculate absolute fees for the keyspend tx
/// depending on the feerate. Fallback to 40sat/vb if the feerate cannot be estimated (e.g. regtest backend).
fn get_tx_fees_abs_sat(blockchain_backend: &RpcBlockchain) -> Result<(u64, u64)> {
let feerate = match blockchain_backend.estimate_fee(6) {
Ok(feerate) => feerate,
@ -20,6 +22,7 @@ fn get_tx_fees_abs_sat(blockchain_backend: &RpcBlockchain) -> Result<(u64, u64)>
}
impl<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
/// loads the escrow descriptor in a temp wallet and return the escrow utxo (as Input and its Outpoint)
fn get_escrow_utxo(
&self,
descriptor: &Descriptor<XOnlyPublicKey>,
@ -45,6 +48,7 @@ impl<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
Ok((input, outpoint))
}
/// assembles the keyspend payout transaction as PSBT (without signatures)
pub async fn assemble_keyspend_payout_psbt(
&self,
payout_information: &PayoutData,
@ -77,33 +81,39 @@ impl<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
Ok(payout_psbt.serialize_hex())
}
/// Inserts the aggregated signature into the keyspend transaction and broadcasts it
pub async fn broadcast_keyspend_tx(
&self,
keyspend_ctx: &KeyspendContext,
) -> anyhow::Result<()> {
// we need a bitcoin 0.32 psbt to access the taproot_hash_ty() method
let bitcoin_032_psbt = bitcoin::Psbt::from_str(&keyspend_ctx.keyspend_psbt.to_string())?;
debug!("Payout psbt: {}", bitcoin_032_psbt.to_string());
// extract the unsigned transaction from the bitcoin 0.32 psbt
let mut bitcoin_032_tx: bitcoin::Transaction = bitcoin_032_psbt.clone().extract_tx()?;
// get a secp256k1::schnorr::Signature from the aggregated musig signature
let secp_signature =
bitcoin::secp256k1::schnorr::Signature::from_slice(&keyspend_ctx.agg_sig.to_bytes())?;
let sighash_type = bitcoin_032_psbt.inputs[0].taproot_hash_ty()?;
// assemble a rust bitcoin Signature from the secp signature and sighash type
let rust_bitcoin_sig = bitcoin::taproot::Signature {
signature: secp_signature,
sighash_type,
};
// let unsigned_tx_hex = bitcoin::consensus::encode::serialize_hex(&bitcoin_032_tx);
// create a p2tr key spend witness from the rust bitcoin signature
let witness = bitcoin::Witness::p2tr_key_spend(&rust_bitcoin_sig);
// let mut tx_clone = bitcoin_032_tx.clone();
// insert the witness into the transaction
let escrow_input: &mut bitcoin::TxIn = &mut bitcoin_032_tx.input[0];
escrow_input.witness = witness.clone();
let signed_hex_tx = bitcoin::consensus::encode::serialize_hex(&bitcoin_032_tx);
// convert the hex tx back into a bitcoin030 tx
// convert the hex tx back into a bitcoin030 tx to be able to broadcast it with the bdk backend
let bdk_bitcoin_030_tx: bdk::bitcoin::Transaction =
deserialize(&hex::decode(signed_hex_tx.clone())?)?;

View File

@ -45,6 +45,7 @@ impl BondRequirementResponse {
trace!("Fetching bond requirements from coordinator. (create-offer)");
let client = reqwest::blocking::Client::new();
let endpoint = format!("{}{}", trader_setup.coordinator_endpoint, "/create-offer");
// requests an offer from the coordinator according to the .env/cli input stored in trader_setup
let res = match client
.post(endpoint)
.json(&Self::_format_order_request(trader_setup))
@ -125,6 +126,7 @@ impl OfferTakenResponse {
}
impl PsbtSubmissionRequest {
// submits the signed escrow psbt to the coordinator
pub fn submit_escrow_psbt(
psbt: &PartiallySignedTransaction,
offer_id_hex: String,
@ -157,10 +159,10 @@ impl TradeObligationsSatisfied {
// if the trader is satisfied he can submit this to signal the coordinator readiness to close the trade
// if the other party also submits this the coordinator can initiate the closing transaction, otherwise
// escrow has to be initiated
pub fn submit(offer_id_hex: &String, trader_config: &TraderSettings) -> Result<()> {
pub fn submit(offer_id_hex: &str, trader_config: &TraderSettings) -> Result<()> {
let request = TradeObligationsSatisfied {
robohash_hex: trader_config.robosats_robohash_hex.clone(),
offer_id_hex: offer_id_hex.clone(),
offer_id_hex: offer_id_hex.to_string(),
};
let client = reqwest::blocking::Client::new();
@ -182,6 +184,7 @@ impl TradeObligationsSatisfied {
}
impl IsOfferReadyRequest {
/// polls until the escrow locking transaction is signaled as confirmed by the coordinator. This could also be implemented client side in theory
pub fn poll(taker_config: &TraderSettings, offer: &ActiveOffer) -> Result<()> {
let request = IsOfferReadyRequest {
robohash_hex: taker_config.robosats_robohash_hex.clone(),
@ -204,11 +207,13 @@ impl IsOfferReadyRequest {
res.status()
));
}
// Sleep for 10 sec and poll again
sleep(Duration::from_secs(10));
// Sleep for 2 sec and poll again
sleep(Duration::from_secs(2));
}
}
/// polls until the other trader also confirmed happiness, then the payout data required to
/// create the partial signature for the keyspend payout is returned
pub fn poll_payout(
trader_config: &TraderSettings,
offer: &ActiveOffer,
@ -221,8 +226,8 @@ impl IsOfferReadyRequest {
let mut res: reqwest::blocking::Response;
loop {
// Sleep for 10 sec and poll
sleep(Duration::from_secs(10));
// Sleep for 2 sec and poll
sleep(Duration::from_secs(2));
res = client
.post(format!(
@ -268,10 +273,11 @@ impl IsOfferReadyRequest {
}
impl TradeObligationsUnsatisfied {
pub fn request_escrow(offer_id_hex: &String, trader_config: &TraderSettings) -> Result<()> {
/// called to request escrow
pub fn request_escrow(offer_id_hex: &str, trader_config: &TraderSettings) -> Result<()> {
let request = TradeObligationsUnsatisfied {
robohash_hex: trader_config.robosats_robohash_hex.clone(),
offer_id_hex: offer_id_hex.clone(),
offer_id_hex: offer_id_hex.to_string(),
};
let client = reqwest::blocking::Client::new();

View File

@ -62,6 +62,7 @@ impl PublicOffers {
}
impl OfferPsbtRequest {
/// submits the taker bond to the coordinator and receives the escrow PSBT to sign in exchange if the bond was accepted
pub fn taker_request(
offer: &PublicOffer,
trade_data: BondSubmissionRequest,

View File

@ -8,6 +8,7 @@ use anyhow::{anyhow, Result};
use cli::CliSettings;
use log::{debug, error, info, trace, warn};
/// start the according trading mode depending on the CLI input or env variables/.env file [maker or taker]
fn start_trade_pipeline(cli_input: &CliSettings) -> Result<()> {
match cli_input {
CliSettings::Maker(maker_config) => trading::run_maker(maker_config),

View File

@ -11,11 +11,15 @@ impl ActiveOffer {
trading_wallet: &TradingWallet,
maker_config: &TraderSettings,
) -> Result<ActiveOffer> {
// fetches the bond requirements necessary to assemble the bond for the requested offer
let offer_conditions = BondRequirementResponse::fetch(maker_config)?;
debug!("Offer conditions fetched: {:#?}", &offer_conditions);
// assembles the bond required by the coordinator, also generates the musig data (keys, nonces) and a payout address
// which are being submitted to the coordinator for the further trade
let (bond, mut musig_data, payout_address) =
trading_wallet.trade_onchain_assembly(&offer_conditions, maker_config)?;
// get necessary data for the coordinator to assemble the escrow locking psbt (inputs owned by maker, change address)
let (psbt_inputs_hex_csv, escrow_change_address) =
trading_wallet.get_escrow_psbt_inputs(offer_conditions.locking_amount_sat as i64)?;
@ -34,6 +38,7 @@ impl ActiveOffer {
client_change_address: escrow_change_address.clone(),
};
// send the bond submission request to the coordinator, returns submission result with offer id and unix timestamp of bond lock
let submission_result = bond_submission_request.send_maker(maker_config)?;
Ok(ActiveOffer {
offer_id_hex: submission_result.offer_id_hex,

View File

@ -26,13 +26,16 @@ use communication::api::PayoutSignatureRequest;
use reqwest::header::ACCEPT_LANGUAGE;
use std::{str::FromStr, thread, time::Duration};
/// the main maker flow function
pub fn run_maker(maker_config: &TraderSettings) -> Result<()> {
// load a bdk wallet from passed xprv
let wallet = TradingWallet::load_wallet(maker_config)?; // initialize the wallet with xprv
// create an offer with the coordinator, offer is an offer that is in the coordinator orderbook (bond submitted, awaiting taker)
let offer = ActiveOffer::create(&wallet, maker_config)?;
info!("Maker offer created: {:#?}", &offer);
// waits until taker accepts offer, then gets the escrow psbt in return to sign the inputs
// waits until taker accepts offer (polling), then gets the escrow psbt in return to sign the inputs
let escrow_psbt_requirements = offer.wait_until_taken(maker_config)?;
let mut escrow_psbt =
PartiallySignedTransaction::from_str(escrow_psbt_requirements.escrow_psbt_hex.as_str())?;
@ -66,6 +69,7 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> {
)?;
// submit signed payout psbt back to coordinator
PayoutSignatureRequest::send(maker_config, &signature, &offer.offer_id_hex)?;
// now the coordinator will broadcast the payout transaction and the trade should be finished
} else {
warn!("Trader unsatisfied. Initiating escrow mode.");
TradeObligationsUnsatisfied::request_escrow(&offer.offer_id_hex, maker_config)?;
@ -74,26 +78,35 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> {
Ok(())
}
/// taker main trade function
pub fn run_taker(taker_config: &TraderSettings) -> Result<()> {
let wallet = TradingWallet::load_wallet(taker_config)?;
// fetches public offers of the coordinator (Orderbook)
let mut available_offers = PublicOffers::fetch(taker_config)?;
// polls until offers are available
while available_offers.offers.is_none() {
debug!("No offers available, fetching again in 10 sec.");
thread::sleep(Duration::from_secs(10));
debug!("No offers available, fetching again in 2 sec.");
thread::sleep(Duration::from_secs(2));
available_offers = PublicOffers::fetch(taker_config)?;
}
// ask for taker cli input to select a suitable offer
let selected_offer: &PublicOffer = available_offers.ask_user_to_select()?;
// take selected offer and wait for maker to sign his input to the ecrow transaction
let accepted_offer = ActiveOffer::take(&wallet, taker_config, selected_offer)?;
// polls the coordinator until the escrow locking transaction has been confirmed, this will take time until the maker signed his input
// and the transaction got confirmed onchain
accepted_offer.wait_on_trade_ready_confirmation(taker_config)?;
// ask the taker if he is satisfied or wants to go into escrow with cli input
if accepted_offer.fiat_confirmation_cli_input(taker_config)? {
// this represents the "confirm payment" / "confirm fiat recieved" button
TradeObligationsSatisfied::submit(&accepted_offer.offer_id_hex, taker_config)?;
debug!("Waiting for other party to confirm the trade.");
// pull for other parties confirmation, then receive the transaction to create MuSig signature for (keyspend) to payout address
// pull for other parties confirmation, then receive the transaction to create MuSig partial signature for (keyspend) to payout address
let (payout_keyspend_psbt, agg_pub_nonce, agg_pubk_ctx) =
IsOfferReadyRequest::poll_payout(taker_config, &accepted_offer)?;
@ -109,6 +122,7 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> {
// submit partial signature back to coordinator
PayoutSignatureRequest::send(taker_config, &signature, &accepted_offer.offer_id_hex)?;
// now the coordinator will broadcast the payout transaction and the trade should be finished
// here we need to handle if the other party is not cooperating
} else {
error!("Trader unsatisfied. Initiating escrow mode.");

View File

@ -21,8 +21,7 @@ impl ActiveOffer {
let (bond, mut musig_data, payout_address) =
trading_wallet.trade_onchain_assembly(&bond_requirements, taker_config)?;
// now we submit the signed bond transaction to the coordinator and receive the escrow PSBT we have to sign
// in exchange
// get inputs and a change address necessary for the coordinator to assemble the escrow locking psbt
let (bdk_psbt_inputs_hex_csv, client_change_address) =
trading_wallet.get_escrow_psbt_inputs(bond_requirements.locking_amount_sat as i64)?;
@ -36,11 +35,15 @@ impl ActiveOffer {
bdk_psbt_inputs_hex_csv: bdk_psbt_inputs_hex_csv.clone(),
client_change_address: client_change_address.clone(),
};
// now we submit the signed bond transaction to the coordinator and receive the escrow PSBT we have to sign
// in exchange
let escrow_contract_requirements =
OfferPsbtRequest::taker_request(offer, bond_submission_request, taker_config)?;
let mut escrow_psbt =
PartiallySignedTransaction::from_str(&escrow_contract_requirements.escrow_psbt_hex)?;
// now we have to verify, sign and submit the escrow psbt again
trading_wallet
.validate_escrow_psbt(&escrow_psbt)?
@ -53,6 +56,7 @@ impl ActiveOffer {
taker_config,
)?;
// offer is now active
Ok(ActiveOffer {
offer_id_hex: offer.offer_id_hex.clone(),
used_musig_config: musig_data,

View File

@ -98,54 +98,20 @@ impl TradingWallet {
trader_config: &TraderSettings,
) -> Result<(PartiallySignedTransaction, MuSigData, AddressInfo)> {
let trading_wallet = &self.wallet;
// assembles the bond according to the requirements
let bond = Bond::assemble(&self.wallet, offer_conditions, trader_config)?;
// get a new payout address from the trader wallet
let payout_address: AddressInfo =
trading_wallet.get_address(bdk::wallet::AddressIndex::New)?;
// generate new musig nonce and keys from the wallet xprv
let musig_data = MuSigData::create(&trader_config.wallet_xprv, trading_wallet.secp_ctx())?;
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())?;
// 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)
/// returns suitable inputs (binary encoded using bincode, hex serialized, csv formatted) 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();
@ -175,15 +141,7 @@ impl TradingWallet {
Ok((serialized_inputs, change_address))
}
// validate that the taker psbt references the correct inputs and amounts
// taker input should be the same as in the previous bond transaction.
// input amount should be the bond amount when buying,
// pub fn validate_taker_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> {
// error!("IMPLEMENT TAKER PSBT VALIDATION!");
// // tbd once the trade psbt is implemented on coordinator side
// Ok(self)
// }
/// signs the inputs of the passed psbt that are controlled by the bdk wallet of the trader
pub fn sign_escrow_psbt(&self, escrow_psbt: &mut PartiallySignedTransaction) -> Result<&Self> {
// we need to finalize here too to make finalizing on the coordinator side work
let sign_options = SignOptions {
@ -198,16 +156,14 @@ impl TradingWallet {
pub fn validate_escrow_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> {
warn!("IMPLEMENT MAKER PSBT VALIDATION for production use!");
// validate: change output address, amounts, fee
// tbd once the trade psbt is implemented on coordinator side
// tbd
Ok(self)
}
pub fn validate_payout_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> {
warn!("IMPLEMENT PAYOUT PSBT VALIDATION for production use!");
// validate: change output address, amounts, fee
// tbd once the trade psbt is implemented on coordinator side
// tbd
Ok(self)
}
@ -220,7 +176,6 @@ impl TradingWallet {
agg_pub_nonce: AggNonce,
local_musig_state: MuSigData,
) -> Result<String> {
// let tweak = validated_payout_psbt.spend_info()?;
let mut sig_hash_cache = SighashCache::new(&validated_payout_psbt.unsigned_tx);
let utxo = validated_payout_psbt
@ -235,11 +190,13 @@ impl TradingWallet {
.taproot_key_spend_signature_hash(0, &Prevouts::All(&[utxo]), sighash_type)
.context("Failed to create keyspend sighash")?;
let raw_sig_hash = binding.to_raw_hash();
// let keyspend_sig_hash_msg = binding.as_byte_array();
// get secret nonce from trader musig state
let secret_nonce = local_musig_state.nonce.get_sec_for_signing()?;
// get secret key from trade musig state
let seckey = local_musig_state.secret_key;
// create partial signature for the taproot keyspend signature hash of the payout psbt
let keyspend_sig: musig2::PartialSignature = musig2::sign_partial(
&key_agg_context,
seckey,
@ -250,9 +207,7 @@ impl TradingWallet {
match keyspend_sig {
MaybeScalar::Valid(s) => Ok(s.encode_hex()),
MaybeScalar::Zero => {
Err(anyhow!("keyspend sig maybe scalar is Zero"))
}
MaybeScalar::Zero => Err(anyhow!("keyspend sig maybe scalar is Zero")),
}
}
}