Merge branch 'research' of https://github.com/RoboSats/taptrade-core into research

merge
This commit is contained in:
fbock
2024-08-26 14:26:31 +02:00
45 changed files with 143 additions and 20 deletions

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 436 KiB

After

Width:  |  Height:  |  Size: 436 KiB

View File

Before

Width:  |  Height:  |  Size: 436 KiB

After

Width:  |  Height:  |  Size: 436 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -34,3 +34,7 @@ chrono = "0.4.38"
lto = true
opt-level = 3
strip = true
[lib]
name = "coordinator"
path = "src/main.rs"

View File

@ -1,3 +1,77 @@
/// This module contains the API structures used for communication in the coordinator.
///
/// The `OfferRequest` struct represents a request to create an offer. It contains the following fields:
/// - `robohash_hex`: The identifier of the trader.
/// - `amount_satoshi`: The amount in satoshi to buy or sell.
/// - `is_buy_order`: A boolean indicating whether it is a buy order or a sell order.
/// - `bond_ratio`: The percentage of the trading amount to be used as a bond.
/// - `offer_duration_ts`: The unix timestamp indicating how long the offer should stay available.
///
/// The `BondRequirementResponse` struct represents the response containing bond requirements. It has the following fields:
/// - `bond_address`: The bond address.
/// - `locking_amount_sat`: The minimum amount of the bond output in satoshi.
///
/// The `BondSubmissionRequest` struct represents a request to submit a bond. It contains the following fields:
/// - `robohash_hex`: The identifier of the trader.
/// - `signed_bond_hex`: The signed bond transaction in hex format.
/// - `payout_address`: The payout address.
/// - `taproot_pubkey_hex`: The taproot public key in hex format.
/// - `musig_pub_nonce_hex`: The musig public nonce in hex format.
/// - `musig_pubkey_hex`: The musig public key in hex format.
/// - `bdk_psbt_inputs_hex_csv`: The bdk psbt inputs in hex format.
/// - `client_change_address`: The client change address.
///
/// The `OfferActivatedResponse` struct represents the response after successfully activating an offer. It has the following fields:
/// - `offer_id_hex`: The offer ID in hex format.
/// - `bond_locked_until_timestamp`: The unix timestamp until which the bond should not be touched unless the offer gets taken.
///
/// The `OffersRequest` struct represents a request to get offers. It contains the following fields:
/// - `buy_offers`: A boolean indicating whether to look for buy offers or sell offers.
/// - `amount_min_sat`: The minimum amount in satoshi.
/// - `amount_max_sat`: The maximum amount in satoshi.
///
/// The `PublicOffer` struct represents information about a public offer. It has the following fields:
/// - `amount_sat`: The amount in satoshi.
/// - `offer_id_hex`: The offer ID in hex format.
/// - `required_bond_amount_sat`: The required bond amount in satoshi.
/// - `bond_locking_address`: The bond locking address.
///
/// The `PublicOffers` struct represents a collection of public offers. It has the following field:
/// - `offers`: An optional vector of `PublicOffer` structs. This field is not included in the return JSON if no offers are available.
///
/// The `OfferTakenResponse` struct represents the response after taking an offer. It has the following fields:
/// - `escrow_psbt_hex`: The escrow PSBT in hex format.
/// - `escrow_output_descriptor`: The escrow output descriptor.
/// - `escrow_amount_maker_sat`: The escrow amount for the maker in satoshi.
/// - `escrow_amount_taker_sat`: The escrow amount for the taker in satoshi.
/// - `escrow_fee_sat_per_participant`: The escrow fee in satoshi per participant.
///
/// The `OfferPsbtRequest` struct represents a request to receive the escrow PSBT for a specified offer. It contains the following fields:
/// - `offer`: The `PublicOffer` struct representing the offer.
/// - `trade_data`: The `BondSubmissionRequest` struct representing the trade data.
///
/// The `OfferTakenRequest` struct represents a request to take an offer. It contains the following fields:
/// - `robohash_hex`: The identifier of the trader.
/// - `offer_id_hex`: The offer ID in hex format.
///
/// The `PsbtSubmissionRequest` struct represents a request to submit a PSBT. It contains the following fields:
/// - `signed_psbt_hex`: The signed PSBT in hex format.
/// - `offer_id_hex`: The offer ID in hex format.
/// - `robohash_hex`: The identifier of the trader.
///
/// The `PayoutResponse` struct represents the response after a payout. It has the following fields:
/// - `payout_psbt_hex`: The payout PSBT in hex format.
/// - `agg_musig_nonce_hex`: The aggregated musig nonce in hex format.
/// - `agg_musig_pubkey_ctx_hex`: The aggregated musig public key context in hex format.
///
/// The `TradeObligationsUnsatisfied` struct represents unsatisfied trade obligations. It has the following fields:
/// - `robohash_hex`: The identifier of the trader.
/// - `offer_id_hex`: The offer ID in hex format.
///
/// The `PayoutSignatureRequest` struct represents a request for a payout signature. It contains the following fields:
/// - `partial_sig_hex`: The partial signature in hex format.
/// - `offer_id_hex`: The offer ID in hex format.
/// - `robohash_hex`: The identifier of the trader.
use super::*;
#[derive(Deserialize, Serialize, Debug, Validate)]

View File

@ -1,5 +1,51 @@
use super::*;
/// Validates the timestamp of an offer duration.
///
/// This function takes an offer duration timestamp as input and validates it against the current time.
/// It checks if the offer duration is within a valid range, which is between the current time plus 3 hours
/// and the current time plus 7 days. If the offer duration is too short or too long, it returns a validation error.
///
/// # Arguments
///
/// * `offer_duration_ts` - The offer duration timestamp to validate.
///
/// # Returns
///
/// * `Result<(), ValidationError>` - An empty result if the offer duration is valid, or a validation error if it is not.
///
/// # Example
///
/// ```
/// use coordinator::communication::communication_utils::validate_timestamp;
/// use std::time::{SystemTime, UNIX_EPOCH};
///
/// // Get the current time
/// let now = SystemTime::now();
/// // Convert the current time to a UNIX timestamp
/// let unix_timestamp = now
/// .duration_since(UNIX_EPOCH)
/// .expect("Time went backwards")
/// .as_secs();
///
/// // Use a timestamp that is within the valid range (current time + 4 hours)
/// let offer_duration_ts = unix_timestamp + 4 * 3600;
/// let result = validate_timestamp(offer_duration_ts);
/// assert!(result.is_ok());
/// ```
///
/// # Errors
///
/// This function can return the following errors:
///
/// * `ValidationError` - If the offer duration is too short or too long.
///
/// # Panics
///
/// This function may panic if the system time goes backwards during the calculation of the current time.
///
/// # Safety
///
/// This function is safe to use.
pub fn validate_timestamp(offer_duration_ts: u64) -> Result<(), ValidationError> {
// Get the current time
let now = SystemTime::now();

View File

@ -372,7 +372,7 @@ async fn test_fetch_taker_bond_requirements() -> Result<()> {
// Call the fetch_taker_bond_requirements function
let result = database
.fetch_taker_bond_requirements(&offer_id_hex.to_string())
.fetch_taker_bond_requirements(offer_id_hex)
.await?;
// Verify the result

View File

@ -1,7 +1,7 @@
mod communication;
mod coordinator;
mod database;
mod wallet;
pub mod communication;
pub mod coordinator;
pub mod database;
pub mod wallet;
use anyhow::{anyhow, Context, Result};
use axum::{

View File

@ -12,7 +12,6 @@ use bdk::{
Wallet,
};
use bitcoin;
use bitcoin::consensus::Decodable;
fn get_backend() -> RpcBlockchain {
dotenv().ok();
@ -152,7 +151,7 @@ async fn test_transaction_without_signature() {
};
let result = test_wallet
.validate_bond_tx_hex(&bond_without_signature, &requirements)
.validate_bond_tx_hex(bond_without_signature, &requirements)
.await;
assert!(result.is_err());
test_wallet.shutdown().await;
@ -170,7 +169,7 @@ async fn test_transaction_with_invalid_signature() {
};
let result = test_wallet
.validate_bond_tx_hex(&bond_with_invalid_signature, &requirements)
.validate_bond_tx_hex(bond_with_invalid_signature, &requirements)
.await;
assert!(result.is_err());
test_wallet.shutdown().await;
@ -187,7 +186,7 @@ async fn test_bond_with_spent_input() {
};
let result = test_wallet
.validate_bond_tx_hex(&bond_with_spent_input, &requirements)
.validate_bond_tx_hex(bond_with_spent_input, &requirements)
.await;
assert!(result.is_err());
test_wallet.shutdown().await;
@ -203,7 +202,7 @@ async fn test_valid_bond_tx() {
bond_address: "tb1p5yh969z6fgatg0mvcyvggd08fujnat8890vcdud277q06rr9xgmqwfdkcx".to_string(),
};
let result = test_wallet.validate_bond_tx_hex(&bond, &requirements).await;
let result = test_wallet.validate_bond_tx_hex(bond, &requirements).await;
assert!(result.is_ok());
test_wallet.shutdown().await;
}
@ -218,7 +217,7 @@ async fn test_invalid_bond_tx_low_input_sum() {
bond_address: "tb1p5yh969z6fgatg0mvcyvggd08fujnat8890vcdud277q06rr9xgmqwfdkcx".to_string(),
};
let result = test_wallet.validate_bond_tx_hex(&bond, &requirements).await;
let result = test_wallet.validate_bond_tx_hex(bond, &requirements).await;
assert!(result.is_err());
assert!(result
.unwrap_err()
@ -237,7 +236,7 @@ async fn test_invalid_bond_tx_low_output_sum() {
bond_address: "tb1p5yh969z6fgatg0mvcyvggd08fujnat8890vcdud277q06rr9xgmqwfdkcx".to_string(),
};
let result = test_wallet.validate_bond_tx_hex(&bond, &requirements).await;
let result = test_wallet.validate_bond_tx_hex(bond, &requirements).await;
test_wallet.shutdown().await;
assert!(result.is_err());
assert!(result
@ -256,7 +255,7 @@ async fn test_invalid_bond_tx_low_fee_rate() {
bond_address: "tb1p5yh969z6fgatg0mvcyvggd08fujnat8890vcdud277q06rr9xgmqwfdkcx".to_string(),
};
let result = test_wallet.validate_bond_tx_hex(&bond, &requirements).await;
let result = test_wallet.validate_bond_tx_hex(bond, &requirements).await;
test_wallet.shutdown().await;
assert!(result.is_err());
assert!(result
@ -383,7 +382,7 @@ fn test_create_escrow_spending_psbt() {
let escrow_utxo = escrow_output_wallet.list_unspent().unwrap();
dbg!(&escrow_utxo);
assert!(escrow_utxo.len() > 0);
assert!(!escrow_utxo.is_empty());
}
#[test]

View File

@ -28,7 +28,7 @@ impl PublicOffers {
}
};
if res.status() == 204 {
return Ok(PublicOffers { offers: None });
Ok(PublicOffers { offers: None })
} else {
match res.json::<PublicOffers>() {
Ok(offers) => {

View File

@ -29,7 +29,7 @@ impl ActiveOffer {
payout_address: payout_address.address.to_string(),
musig_pub_nonce_hex: hex::encode(musig_data.nonce.get_pub_for_sharing()?.serialize()),
musig_pubkey_hex: hex::encode(musig_data.public_key.serialize()),
taproot_pubkey_hex: hex::encode(&trading_wallet.taproot_pubkey.serialize()),
taproot_pubkey_hex: hex::encode(trading_wallet.taproot_pubkey.serialize()),
bdk_psbt_inputs_hex_csv: psbt_inputs_hex_csv.clone(),
client_change_address: escrow_change_address.clone(),
};

View File

@ -65,7 +65,7 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> {
offer.used_musig_config,
)?;
// submit signed payout psbt back to coordinator
PayoutSignatureRequest::send(&maker_config, &signature, &offer.offer_id_hex)?;
PayoutSignatureRequest::send(maker_config, &signature, &offer.offer_id_hex)?;
} else {
warn!("Trader unsatisfied. Initiating escrow mode.");
TradeObligationsUnsatisfied::request_escrow(&offer.offer_id_hex, maker_config)?;
@ -108,7 +108,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)?;
PayoutSignatureRequest::send(taker_config, &signature, &accepted_offer.offer_id_hex)?;
// here we need to handle if the other party is not cooperating
} else {
error!("Trader unsatisfied. Initiating escrow mode.");

View File

@ -251,7 +251,7 @@ impl TradingWallet {
match keyspend_sig {
MaybeScalar::Valid(s) => Ok(s.encode_hex()),
MaybeScalar::Zero => {
return Err(anyhow!("keyspend sig maybe scalar is Zero"));
Err(anyhow!("keyspend sig maybe scalar is Zero"))
}
}
}