mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
feat: Include proof of reserves in quote
This commit is contained in:
parent
b18ba95e8c
commit
a810c0108b
@ -1,9 +1,8 @@
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use rust_decimal::Decimal;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[jsonrpc_client::api(version = "2.0")]
|
||||
pub trait MoneroWalletRpc {
|
||||
@ -36,6 +35,18 @@ pub trait MoneroWalletRpc {
|
||||
async fn refresh(&self) -> Refreshed;
|
||||
async fn sweep_all(&self, address: String) -> SweepAll;
|
||||
async fn get_version(&self) -> Version;
|
||||
async fn get_reserve_proof(
|
||||
&self,
|
||||
all: bool,
|
||||
amount: Option<u64>,
|
||||
message: Option<String>,
|
||||
) -> GetReserveProof;
|
||||
async fn check_reserve_proof(
|
||||
&self,
|
||||
address: String,
|
||||
message: Option<String>,
|
||||
signature: String,
|
||||
) -> CheckReserveProof;
|
||||
}
|
||||
|
||||
#[jsonrpc_client::implement(MoneroWalletRpc)]
|
||||
@ -216,6 +227,16 @@ pub struct SweepAll {
|
||||
pub tx_hash_list: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct GetReserveProof {
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct CheckReserveProof {
|
||||
pub good: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Deserialize)]
|
||||
pub struct Version {
|
||||
pub version: u32,
|
||||
|
@ -2,6 +2,7 @@ use crate::api::Context;
|
||||
use crate::bitcoin::{Amount, ExpiredTimelocks, TxLock};
|
||||
use crate::cli::{list_sellers, EventLoop, SellerStatus};
|
||||
use crate::libp2p_ext::MultiAddrExt;
|
||||
use crate::monero::ReserveProof;
|
||||
use crate::network::quote::{BidQuote, ZeroQuoteReceived};
|
||||
use crate::network::swarm;
|
||||
use crate::protocol::bob::{BobState, Swap};
|
||||
@ -419,6 +420,8 @@ impl Request {
|
||||
}
|
||||
};
|
||||
|
||||
let bid_quote_clone = bid_quote.clone();
|
||||
|
||||
context.tasks.clone().spawn(async move {
|
||||
tokio::select! {
|
||||
biased;
|
||||
@ -440,6 +443,9 @@ impl Request {
|
||||
swap_result = async {
|
||||
let max_givable = || bitcoin_wallet.max_giveable(TxLock::script_size());
|
||||
let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount);
|
||||
let check_reserve_proof = |reserve_proof: ReserveProof| {
|
||||
monero_wallet.check_reserve_proof(reserve_proof)
|
||||
};
|
||||
|
||||
let determine_amount = determine_btc_to_swap(
|
||||
context.config.json,
|
||||
@ -449,6 +455,7 @@ impl Request {
|
||||
max_givable,
|
||||
|| bitcoin_wallet.sync(),
|
||||
estimate_fee,
|
||||
check_reserve_proof,
|
||||
);
|
||||
|
||||
let (amount, fees) = match determine_amount.await {
|
||||
@ -501,7 +508,7 @@ impl Request {
|
||||
|
||||
Ok(json!({
|
||||
"swapId": swap_id.to_string(),
|
||||
"quote": bid_quote,
|
||||
"quote": bid_quote_clone,
|
||||
}))
|
||||
}
|
||||
Method::Resume { swap_id } => {
|
||||
@ -763,7 +770,7 @@ impl Request {
|
||||
.await?;
|
||||
|
||||
for seller in &sellers {
|
||||
match seller.status {
|
||||
match seller.status.clone() {
|
||||
SellerStatus::Online(quote) => {
|
||||
tracing::info!(
|
||||
price = %quote.price.to_string(),
|
||||
@ -858,7 +865,7 @@ fn qr_code(value: &impl ToString) -> Result<String> {
|
||||
Ok(qr_code)
|
||||
}
|
||||
|
||||
pub async fn determine_btc_to_swap<FB, TB, FMG, TMG, FS, TS, FFE, TFE>(
|
||||
pub async fn determine_btc_to_swap<FB, TB, FMG, TMG, FS, TS, FFE, TFE, FCRP, CRP>(
|
||||
json: bool,
|
||||
bid_quote: BidQuote,
|
||||
get_new_address: impl Future<Output = Result<bitcoin::Address>>,
|
||||
@ -866,6 +873,7 @@ pub async fn determine_btc_to_swap<FB, TB, FMG, TMG, FS, TS, FFE, TFE>(
|
||||
max_giveable_fn: FMG,
|
||||
sync: FS,
|
||||
estimate_fee: FFE,
|
||||
check_reserve_proof: FCRP,
|
||||
) -> Result<(Amount, Amount)>
|
||||
where
|
||||
TB: Future<Output = Result<Amount>>,
|
||||
@ -874,8 +882,10 @@ where
|
||||
FMG: Fn() -> TMG,
|
||||
TS: Future<Output = Result<()>>,
|
||||
FS: Fn() -> TS,
|
||||
FFE: Fn(Amount) -> TFE,
|
||||
TFE: Future<Output = Result<Amount>>,
|
||||
FFE: Fn(Amount) -> TFE,
|
||||
CRP: Future<Output = Result<bool>>,
|
||||
FCRP: Fn(ReserveProof) -> CRP,
|
||||
{
|
||||
if bid_quote.max_quantity == Amount::ZERO {
|
||||
bail!(ZeroQuoteReceived)
|
||||
@ -888,6 +898,15 @@ where
|
||||
"Received quote",
|
||||
);
|
||||
|
||||
if let Some(reserve_proof) = &bid_quote.reserve_proof {
|
||||
tracing::info!("Received reserve proof");
|
||||
if check_reserve_proof(reserve_proof.clone()).await? {
|
||||
tracing::info!("Reserve proof is valid");
|
||||
} else {
|
||||
bail!("Reserve proof is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
sync().await?;
|
||||
let mut max_giveable = max_giveable_fn().await?;
|
||||
|
||||
|
@ -406,9 +406,12 @@ where
|
||||
price: ask_price,
|
||||
min_quantity: bitcoin::Amount::ZERO,
|
||||
max_quantity: bitcoin::Amount::ZERO,
|
||||
reserve_proof: None,
|
||||
});
|
||||
}
|
||||
|
||||
let xmr_reserve_proof = self.monero_wallet.get_reserve_proof(None, None).await?;
|
||||
|
||||
if max_buy > max_bitcoin_for_monero {
|
||||
tracing::warn!(
|
||||
"Your Monero balance is too low to initiate a swap with the maximum swap amount {} that you have specified in your config. You can at most swap {}",
|
||||
@ -418,6 +421,7 @@ where
|
||||
price: ask_price,
|
||||
min_quantity: min_buy,
|
||||
max_quantity: max_bitcoin_for_monero,
|
||||
reserve_proof: Some(xmr_reserve_proof),
|
||||
});
|
||||
}
|
||||
|
||||
@ -425,6 +429,7 @@ where
|
||||
price: ask_price,
|
||||
min_quantity: min_buy,
|
||||
max_quantity: max_buy,
|
||||
reserve_proof: Some(xmr_reserve_proof),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ pub struct Seller {
|
||||
pub multiaddr: Multiaddr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd)]
|
||||
#[derive(Debug, Serialize, PartialEq, Eq, Hash, Clone, Ord, PartialOrd)]
|
||||
pub enum Status {
|
||||
Online(BidQuote),
|
||||
Unreachable,
|
||||
@ -284,7 +284,7 @@ impl EventLoop {
|
||||
|
||||
Ok(Seller {
|
||||
multiaddr: address.clone(),
|
||||
status: Status::Online(*quote),
|
||||
status: Status::Online(quote.clone()),
|
||||
})
|
||||
}
|
||||
QuoteStatus::Received(Status::Unreachable) => {
|
||||
|
@ -199,6 +199,13 @@ pub struct TransferProof {
|
||||
tx_key: PrivateKey,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ReserveProof {
|
||||
address: String,
|
||||
signature: String,
|
||||
message: Option<String>,
|
||||
}
|
||||
|
||||
impl TransferProof {
|
||||
pub fn new(tx_hash: TxHash, tx_key: PrivateKey) -> Self {
|
||||
Self { tx_hash, tx_key }
|
||||
|
@ -13,6 +13,8 @@ use tokio::sync::Mutex;
|
||||
use tokio::time::Interval;
|
||||
use url::Url;
|
||||
|
||||
use super::ReserveProof;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Wallet {
|
||||
inner: Mutex<wallet::Client>,
|
||||
@ -314,6 +316,38 @@ impl Wallet {
|
||||
}
|
||||
unreachable!("Loop should have returned by now");
|
||||
}
|
||||
|
||||
pub async fn get_reserve_proof(
|
||||
&self,
|
||||
amount: Option<u64>,
|
||||
message: Option<String>,
|
||||
) -> Result<ReserveProof> {
|
||||
let signature = self
|
||||
.inner
|
||||
.lock()
|
||||
.await
|
||||
.get_reserve_proof(amount.is_none(), amount, message.clone())
|
||||
.await?
|
||||
.signature;
|
||||
|
||||
let address = self.inner.lock().await.get_address(0).await?.address;
|
||||
|
||||
Ok(ReserveProof {
|
||||
address,
|
||||
signature,
|
||||
message,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn check_reserve_proof(&self, proof: ReserveProof) -> Result<bool> {
|
||||
Ok(self
|
||||
.inner
|
||||
.lock()
|
||||
.await
|
||||
.check_reserve_proof(proof.address, proof.message, proof.signature)
|
||||
.await?
|
||||
.good)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::monero::ReserveProof;
|
||||
use crate::network::json_pull_codec::JsonPullCodec;
|
||||
use crate::{asb, bitcoin, cli};
|
||||
use libp2p::core::ProtocolName;
|
||||
@ -24,7 +25,7 @@ impl ProtocolName for BidQuoteProtocol {
|
||||
}
|
||||
|
||||
/// Represents a quote for buying XMR.
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct BidQuote {
|
||||
/// The price at which the maker is willing to buy at.
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
@ -35,6 +36,7 @@ pub struct BidQuote {
|
||||
/// The maximum quantity the maker is willing to buy.
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
pub max_quantity: bitcoin::Amount,
|
||||
pub reserve_proof: Option<ReserveProof>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, thiserror::Error)]
|
||||
|
Loading…
Reference in New Issue
Block a user