mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-07-18 08:43:17 +00:00
fix input amounts trough additional api request field in BondRequirementsResponse
This commit is contained in:
@ -35,6 +35,6 @@ lto = true
|
||||
opt-level = 3
|
||||
strip = true
|
||||
|
||||
[lib]
|
||||
name = "coordinator"
|
||||
path = "src/main.rs"
|
||||
# [lib]
|
||||
# name = "coordinator"
|
||||
# path = "src/main.rs"
|
||||
|
@ -86,10 +86,11 @@ pub struct OfferRequest {
|
||||
pub offer_duration_ts: u64, // unix timestamp how long the offer should stay available
|
||||
}
|
||||
|
||||
#[derive(Serialize, PartialEq, Debug, Validate)]
|
||||
#[derive(Serialize, PartialEq, Debug, Validate, Deserialize)]
|
||||
pub struct BondRequirementResponse {
|
||||
pub bond_address: String,
|
||||
pub locking_amount_sat: u64, // min amount of the bond output in sat
|
||||
pub escrow_locking_input_amount_without_trade_sum: u64,
|
||||
}
|
||||
|
||||
// maker step 2
|
||||
@ -124,8 +125,7 @@ pub struct OffersRequest {
|
||||
pub struct PublicOffer {
|
||||
pub amount_sat: u64,
|
||||
pub offer_id_hex: String,
|
||||
pub required_bond_amount_sat: u64,
|
||||
pub bond_locking_address: String,
|
||||
pub bond_requirements: BondRequirementResponse,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
|
@ -15,31 +15,23 @@ pub async fn process_order(
|
||||
let wallet = &coordinator.coordinator_wallet;
|
||||
let database = &coordinator.coordinator_db;
|
||||
|
||||
// the client also uses this amount to select inputs for the escrow psbt, this could be separated
|
||||
// to make the required bond amount lower without causing the client to return too small inputs
|
||||
// 5000 is the abs tx fee used in the escrow psbt per trader
|
||||
panic!("This is borked");
|
||||
let coordinator_feerate = (coordinator.coordinator_wallet.coordinator_feerate
|
||||
* offer.amount_satoshi as f64) as u64
|
||||
/ 100;
|
||||
let locking_amount_sat = match offer.is_buy_order {
|
||||
true => {
|
||||
5000 + coordinator_feerate + offer.amount_satoshi * u64::from(offer.bond_ratio) / 100
|
||||
}
|
||||
false => {
|
||||
(offer.amount_satoshi * u64::from(offer.bond_ratio) / 100)
|
||||
+ offer.amount_satoshi
|
||||
+ 5000 + coordinator_feerate
|
||||
}
|
||||
};
|
||||
let bond_amount = (offer.bond_ratio as u64 * offer.amount_satoshi) / 100;
|
||||
let coordinator_fee = ((coordinator.coordinator_wallet.coordinator_feerate
|
||||
* offer.amount_satoshi as f64)
|
||||
/ 100.0) as u64;
|
||||
let absolute_tx_fee = 5000;
|
||||
// 5000 is a buffer
|
||||
let escrow_locking_input_amount_without_trade_sum =
|
||||
bond_amount + coordinator_fee + absolute_tx_fee + 5000;
|
||||
trace!(
|
||||
"Offer amount: {}, Locking amount: {}",
|
||||
offer.amount_satoshi,
|
||||
locking_amount_sat
|
||||
bond_amount
|
||||
);
|
||||
let bond_requirements = BondRequirementResponse {
|
||||
bond_address: wallet.get_new_address().await?,
|
||||
locking_amount_sat,
|
||||
locking_amount_sat: bond_amount,
|
||||
escrow_locking_input_amount_without_trade_sum,
|
||||
};
|
||||
|
||||
database
|
||||
|
@ -18,6 +18,7 @@ struct AwaitingBondOffer {
|
||||
offer_duration_ts: u64,
|
||||
bond_address: String,
|
||||
bond_amount_sat: u64,
|
||||
escrow_locking_input_amount_without_trade_sum: u64,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
@ -78,7 +79,8 @@ impl CoordinatorDB {
|
||||
bond_ratio INTEGER NOT NULL,
|
||||
offer_duration_ts INTEGER NOT NULL,
|
||||
bond_address TEXT NOT NULL,
|
||||
bond_amount_sat INTEGER NOT NULL
|
||||
bond_amount_sat INTEGER NOT NULL,
|
||||
escrow_locking_input_amount_without_trade_sum INTEGER NOT NULL
|
||||
)",
|
||||
)
|
||||
.execute(&db_pool)
|
||||
@ -96,6 +98,7 @@ impl CoordinatorDB {
|
||||
offer_duration_ts INTEGER NOT NULL,
|
||||
bond_address TEXT NOT NULL,
|
||||
bond_amount_sat INTEGER NOT NULL,
|
||||
escrow_locking_input_amount_without_trade_sum INTEGER,
|
||||
bond_tx_hex TEXT NOT NULL,
|
||||
payout_address TEXT NOT NULL,
|
||||
change_address_maker TEXT NOT NULL,
|
||||
@ -169,8 +172,8 @@ impl CoordinatorDB {
|
||||
) -> Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT OR REPLACE INTO maker_requests (robohash, is_buy_order, amount_sat,
|
||||
bond_ratio, offer_duration_ts, bond_address, bond_amount_sat)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, escrow_locking_input_amount_without_trade_sum)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
)
|
||||
.bind(hex::decode(&order.robohash_hex)?)
|
||||
.bind(bool_to_sql_int(order.is_buy_order))
|
||||
@ -179,6 +182,7 @@ impl CoordinatorDB {
|
||||
.bind(order.offer_duration_ts as i64)
|
||||
.bind(bond_requirements.bond_address.clone())
|
||||
.bind(bond_requirements.locking_amount_sat as i64)
|
||||
.bind(bond_requirements.escrow_locking_input_amount_without_trade_sum as i64)
|
||||
.execute(&*self.db_pool)
|
||||
.await?;
|
||||
|
||||
@ -207,7 +211,7 @@ impl CoordinatorDB {
|
||||
robohash_hex: &str,
|
||||
) -> Result<AwaitingBondOffer> {
|
||||
let fetched_values = sqlx::query_as::<_, (Vec<u8>, bool, i64, u8, i64, String, i64)> (
|
||||
"SELECT robohash, is_buy_order, amount_sat, bond_ratio, offer_duration_ts, bond_address, bond_amount_sat FROM maker_requests WHERE robohash = ?",
|
||||
"SELECT robohash, is_buy_order, amount_sat, bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, escrow_locking_input_amount_without_trade_sum FROM maker_requests WHERE robohash = ?",
|
||||
)
|
||||
.bind(hex::decode(robohash_hex)?)
|
||||
.fetch_one(&*self.db_pool)
|
||||
@ -226,6 +230,7 @@ impl CoordinatorDB {
|
||||
offer_duration_ts: fetched_values.4 as u64,
|
||||
bond_address: fetched_values.5,
|
||||
bond_amount_sat: fetched_values.6 as u64,
|
||||
escrow_locking_input_amount_without_trade_sum: fetched_values.6 as u64,
|
||||
};
|
||||
debug!(
|
||||
"Deleted offer from maker_requests table. Fetched offer: {:#?}",
|
||||
@ -252,8 +257,8 @@ 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,
|
||||
change_address_maker, escrow_inputs_hex_maker_csv)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
change_address_maker, escrow_inputs_hex_maker_csv, escrow_locking_input_amount_without_trade_sum)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
)
|
||||
.bind(offer_id)
|
||||
.bind(hex::decode(&data.robohash_hex)?)
|
||||
@ -271,6 +276,7 @@ impl CoordinatorDB {
|
||||
.bind(taker_bond_address)
|
||||
.bind(data.client_change_address.clone())
|
||||
.bind(data.bdk_psbt_inputs_hex_csv.clone())
|
||||
.bind(remaining_offer_information.escrow_locking_input_amount_without_trade_sum as i64)
|
||||
.execute(&*self.db_pool)
|
||||
.await?;
|
||||
|
||||
@ -287,8 +293,8 @@ impl CoordinatorDB {
|
||||
"Fetching suitable offers from db. Specification: {:#?}",
|
||||
requested_offer
|
||||
);
|
||||
let fetched_offers = sqlx::query_as::<_, (String, i64, i64, String)> (
|
||||
"SELECT offer_id, amount_sat, bond_amount_sat, taker_bond_address FROM active_maker_offers WHERE is_buy_order = ? AND amount_sat BETWEEN ? AND ?",
|
||||
let fetched_offers = sqlx::query_as::<_, (String, i64, i64, String, i64)> (
|
||||
"SELECT offer_id, amount_sat, bond_amount_sat, taker_bond_address, escrow_locking_input_amount_without_trade_sum FROM active_maker_offers WHERE is_buy_order = ? AND amount_sat BETWEEN ? AND ?",
|
||||
)
|
||||
.bind(requested_offer.buy_offers)
|
||||
.bind(requested_offer.amount_min_sat as i64)
|
||||
@ -299,11 +305,16 @@ impl CoordinatorDB {
|
||||
let available_offers: Vec<PublicOffer> = fetched_offers
|
||||
.into_iter()
|
||||
.map(
|
||||
|(offer_id_hex, amount_sat, bond_amount_sat, bond_address_taker)| PublicOffer {
|
||||
|(offer_id_hex, amount_sat, bond_amount_sat, bond_address_taker, min_inputs)| {
|
||||
PublicOffer {
|
||||
offer_id_hex,
|
||||
amount_sat: amount_sat as u64,
|
||||
required_bond_amount_sat: bond_amount_sat as u64,
|
||||
bond_locking_address: bond_address_taker,
|
||||
bond_requirements: BondRequirementResponse {
|
||||
bond_address: bond_address_taker,
|
||||
locking_amount_sat: bond_amount_sat as u64,
|
||||
escrow_locking_input_amount_without_trade_sum: min_inputs as u64,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
@ -400,7 +411,7 @@ impl CoordinatorDB {
|
||||
.bind(public_offer.bond_ratio)
|
||||
.bind(public_offer.offer_duration_ts)
|
||||
.bind(public_offer.bond_address_maker)
|
||||
.bind(trade_and_taker_info.offer.bond_locking_address.clone())
|
||||
.bind(trade_and_taker_info.offer.bond_requirements.bond_address.clone())
|
||||
.bind(public_offer.bond_amount_sat)
|
||||
.bind(public_offer.bond_tx_hex_maker)
|
||||
.bind(trade_and_taker_info.trade_data.signed_bond_hex.clone())
|
||||
|
@ -13,8 +13,8 @@ hex = "0.4.3"
|
||||
log = "0.4.21"
|
||||
musig2 = "0.0.11"
|
||||
rand_core = "0.6.4"
|
||||
reqwest = { version = "0.12.4", features = ["blocking", "json"] }
|
||||
serde = "1.0.203"
|
||||
reqwest = { version = "0.12", features = ["blocking", "json"] }
|
||||
serde = "1.0"
|
||||
sha2 = "0.10.8"
|
||||
|
||||
[profile.release]
|
||||
|
@ -13,10 +13,11 @@ pub struct OrderRequest {
|
||||
|
||||
// coordinator answer to maker step 1
|
||||
// direct Json answer to step 1 (same request)
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct BondRequirementResponse {
|
||||
pub bond_address: String, // address the bond ha/workspaces/taptrade-core/taptrade-cli-demo/trader/src/communications to be locked to
|
||||
pub locking_amount_sat: u64, // min amount of the bond output in sat
|
||||
pub escrow_locking_input_amount_without_trade_sum: u64, // minimum required amount of input to the escrow tx
|
||||
}
|
||||
|
||||
// maker step 2
|
||||
@ -76,8 +77,7 @@ pub struct PublicOffers {
|
||||
pub struct PublicOffer {
|
||||
pub amount_sat: u64,
|
||||
pub offer_id_hex: String,
|
||||
pub required_bond_amount_sat: u64,
|
||||
pub bond_locking_address: String, // its probably bad privacy to make the locking address static
|
||||
pub bond_requirements: BondRequirementResponse,
|
||||
}
|
||||
|
||||
// request to receive the escrow psbt to sign for the specified offer to take it
|
||||
|
@ -20,8 +20,14 @@ impl ActiveOffer {
|
||||
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 input_amount = if maker_config.trade_type.is_buy_order() {
|
||||
offer_conditions.escrow_locking_input_amount_without_trade_sum
|
||||
} else {
|
||||
offer_conditions.escrow_locking_input_amount_without_trade_sum
|
||||
+ maker_config.trade_type.value()
|
||||
};
|
||||
let (psbt_inputs_hex_csv, escrow_change_address) =
|
||||
trading_wallet.get_escrow_psbt_inputs(offer_conditions.locking_amount_sat as i64)?;
|
||||
trading_wallet.get_escrow_psbt_inputs(input_amount)?;
|
||||
|
||||
debug!(
|
||||
"Submitting maker bond: {:#?}",
|
||||
|
@ -69,7 +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
|
||||
debug!("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)?;
|
||||
@ -122,7 +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
|
||||
debug!("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.");
|
||||
|
@ -12,18 +12,23 @@ impl ActiveOffer {
|
||||
taker_config: &TraderSettings,
|
||||
offer: &PublicOffer,
|
||||
) -> Result<ActiveOffer> {
|
||||
let bond_requirements = BondRequirementResponse {
|
||||
bond_address: offer.bond_locking_address.clone(),
|
||||
locking_amount_sat: offer.required_bond_amount_sat,
|
||||
};
|
||||
|
||||
// assembly of the Bond transaction and generation of MuSig data and payout address
|
||||
let (bond, mut musig_data, payout_address) =
|
||||
trading_wallet.trade_onchain_assembly(&bond_requirements, taker_config)?;
|
||||
trading_wallet.trade_onchain_assembly(&offer.bond_requirements, taker_config)?;
|
||||
|
||||
// get inputs and a change address necessary for the coordinator to assemble the escrow locking psbt
|
||||
let input_amount = if taker_config.trade_type.is_buy_order() {
|
||||
offer
|
||||
.bond_requirements
|
||||
.escrow_locking_input_amount_without_trade_sum
|
||||
} else {
|
||||
offer
|
||||
.bond_requirements
|
||||
.escrow_locking_input_amount_without_trade_sum
|
||||
+ taker_config.trade_type.value()
|
||||
};
|
||||
let (bdk_psbt_inputs_hex_csv, client_change_address) =
|
||||
trading_wallet.get_escrow_psbt_inputs(bond_requirements.locking_amount_sat as i64)?;
|
||||
trading_wallet.get_escrow_psbt_inputs(input_amount)?;
|
||||
|
||||
let bond_submission_request = BondSubmissionRequest {
|
||||
robohash_hex: taker_config.robosats_robohash_hex.clone(),
|
||||
|
@ -99,6 +99,7 @@ mod tests {
|
||||
let bond_target = BondRequirementResponse {
|
||||
bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(),
|
||||
locking_amount_sat: 10000,
|
||||
escrow_locking_input_amount_without_trade_sum: 10000,
|
||||
};
|
||||
let trader_input = TraderSettings {
|
||||
electrum_endpoint: "ssl://mempool.space:40002".to_string(),
|
||||
@ -123,6 +124,7 @@ mod tests {
|
||||
let bond_target = BondRequirementResponse {
|
||||
bond_address: "invalid_address".to_string(),
|
||||
locking_amount_sat: 10000,
|
||||
escrow_locking_input_amount_without_trade_sum: 10000,
|
||||
};
|
||||
let trader_input = TraderSettings {
|
||||
electrum_endpoint: "ssl://mempool.space:40002".to_string(),
|
||||
@ -148,6 +150,7 @@ mod tests {
|
||||
bond_address: "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297"
|
||||
.to_string(),
|
||||
locking_amount_sat: 10000,
|
||||
escrow_locking_input_amount_without_trade_sum: 10000,
|
||||
};
|
||||
let trader_input = TraderSettings {
|
||||
electrum_endpoint: "ssl://mempool.space:40002".to_string(),
|
||||
@ -172,6 +175,7 @@ mod tests {
|
||||
let bond_target = BondRequirementResponse {
|
||||
bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(),
|
||||
locking_amount_sat: 10000000000, // Very high amount
|
||||
escrow_locking_input_amount_without_trade_sum: 10000,
|
||||
};
|
||||
let trader_input = TraderSettings {
|
||||
electrum_endpoint: "ssl://mempool.space:40002".to_string(),
|
||||
@ -196,6 +200,7 @@ mod tests {
|
||||
let bond_target = BondRequirementResponse {
|
||||
bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(),
|
||||
locking_amount_sat: 0,
|
||||
escrow_locking_input_amount_without_trade_sum: 10000,
|
||||
};
|
||||
let trader_input = TraderSettings {
|
||||
electrum_endpoint: "ssl://mempool.space:40002".to_string(),
|
||||
|
@ -112,7 +112,8 @@ impl TradingWallet {
|
||||
}
|
||||
|
||||
/// 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)> {
|
||||
pub fn get_escrow_psbt_inputs(&self, amount_sat: u64) -> Result<(String, String)> {
|
||||
let mut amount_sat = amount_sat as i64; // convert to signed int for subtraction
|
||||
let mut inputs: Vec<String> = Vec::new();
|
||||
|
||||
self.wallet.sync(&self.backend, SyncOptions::default())?;
|
||||
|
@ -7,7 +7,7 @@
|
||||
/// nonce, as well as flags indicating whether it has been accessed for signing or sharing.
|
||||
///
|
||||
/// The `generate` function in the `MusigNonce` implementation generates a new `MusigNonce` with a
|
||||
/// secret nonce based on the current timestamp.
|
||||
/// secret nonce based on the os rng salted with the current timestamp.
|
||||
///
|
||||
/// The `get_sec_for_signing` function in the `MusigNonce` implementation returns the secret nonce
|
||||
/// for signing, ensuring that it has not been accessed for signing before.
|
||||
@ -39,7 +39,7 @@ pub struct MuSigData {
|
||||
pub secret_key: MusigSecretKey,
|
||||
}
|
||||
|
||||
// secret nonce has to be used only one time!
|
||||
/// nonce must not be used more than once
|
||||
#[derive(Debug)]
|
||||
pub struct MusigNonce {
|
||||
secret_nonce: SecNonce,
|
||||
|
Reference in New Issue
Block a user