Merge branch 'rpc-server' of github.com:yamabiiko/xmr-btc-swap into rpc-server

This commit is contained in:
Lorenzo Tucci 2023-09-22 23:29:35 +03:00
commit 959a309d0e
No known key found for this signature in database
GPG key ID: D98C4FA2CDF590A0
6 changed files with 106 additions and 25 deletions

View file

@ -4,9 +4,8 @@ use crate::cli::{list_sellers, EventLoop, SellerStatus};
use crate::libp2p_ext::MultiAddrExt;
use crate::network::quote::{BidQuote, ZeroQuoteReceived};
use crate::network::swarm;
use crate::protocol::bob;
use crate::protocol::bob::swap::is_complete;
use crate::protocol::bob::{BobState, Swap};
use crate::protocol::{bob, State};
use crate::{bitcoin, cli, monero, rpc};
use anyhow::{bail, Context as AnyContext, Result};
use libp2p::core::Multiaddr;
@ -195,8 +194,6 @@ impl Request {
.as_ref()
.context("Could not get Bitcoin wallet")?;
let swap_state: BobState = context.db.get_state(swap_id).await?.try_into()?;
let peerId = context
.db
.get_peer_id(swap_id)
@ -209,12 +206,53 @@ impl Request {
.await
.with_context(|| "Could not get addressess")?;
let is_completed = is_complete(&swap_state);
let state = context.db.get_state(swap_id).await?;
let is_completed = state.swap_finished();
let start_date = context.db.get_swap_start_date(swap_id).await?;
let swap_state: BobState = state.try_into()?;
let state_name = format!("{}", swap_state);
let (
xmr_amount,
btc_amount,
tx_lock_id,
tx_cancel_fee,
tx_refund_fee,
btc_refund_address,
cancel_timelock,
punish_timelock,
) = context
.db
.get_states(swap_id)
.await?
.iter()
.find_map(|state| {
if let State::Bob(BobState::SwapSetupCompleted(state2)) = state {
let xmr_amount = state2.xmr;
let btc_amount = state2.tx_lock.lock_amount().to_sat();
let tx_cancel_fee = state2.tx_cancel_fee.to_sat();
let tx_refund_fee = state2.tx_refund_fee.to_sat();
let tx_lock_id = state2.tx_lock.txid();
let btc_refund_address = state2.refund_address.to_string();
return Some((
xmr_amount,
btc_amount,
tx_lock_id,
tx_cancel_fee,
tx_refund_fee,
btc_refund_address,
state2.cancel_timelock,
state2.punish_timelock,
));
} else {
None
}
})
.with_context(|| "Did not find SwapSetupCompleted state for swap")?;
let timelock = match swap_state {
BobState::Started { .. }
| BobState::SafelyAborted
@ -230,7 +268,6 @@ impl Request {
Some(state.expired_timelock(bitcoin_wallet).await)
}
BobState::BtcPunished { .. } => Some(Ok(ExpiredTimelocks::Punish)),
// swap is already finished
BobState::BtcRefunded(_)
| BobState::BtcRedeemed(_)
| BobState::XmrRedeemed { .. } => None,
@ -245,10 +282,18 @@ impl Request {
},
"completed": is_completed,
"startDate": start_date,
// If none return null, if some unwrap and return as json
"timelock": timelock.map(|tl| tl.map(|tl| json!(tl)).unwrap_or(json!(null))).unwrap_or(json!(null)),
// Use display to get the string representation of the state
"stateName": state_name,
"xmrAmount": xmr_amount,
"btcAmount": btc_amount,
"txLockId": tx_lock_id,
"txCancelFee": tx_cancel_fee,
"txRefundFee": tx_refund_fee,
"btcRefundAddress": btc_refund_address.to_string(),
"cancelTimelock": cancel_timelock,
"punishTimelock": punish_timelock,
// If the timelock is None, it means that the swap is in a state where the timelock is not accessible to us.
// If that is the case, we return null. Otherwise, we return the timelock.
"timelock": timelock.map(|tl| tl.map(|tl| json!(tl)).unwrap_or(json!(null))).unwrap_or(json!(null)),
}))
}
Method::BuyXmr {
@ -263,7 +308,7 @@ impl Request {
tokio::select! {
biased;
_ = context.swap_lock.listen_for_swap_force_suspension() => {
tracing::info!("Shutdown signal received, exiting");
tracing::debug!("Shutdown signal received, exiting");
()
},
_ = async {
@ -477,7 +522,7 @@ impl Request {
()
},
_ = context.swap_lock.listen_for_swap_force_suspension() => {
tracing::info!("Shutdown signal received, exiting");
tracing::debug!("Shutdown signal received, exiting");
()
}
}

View file

@ -762,9 +762,10 @@ impl Client {
self.blockchain.get_tx(txid)
}
fn update_state(&mut self) -> Result<()> {
fn update_state(&mut self, force_sync: bool) -> Result<()> {
let now = Instant::now();
if now < self.last_sync + self.sync_interval {
if !force_sync && now < self.last_sync + self.sync_interval {
return Ok(());
}
@ -784,10 +785,11 @@ impl Client {
if !self.script_history.contains_key(&script) {
self.script_history.insert(script.clone(), vec![]);
self.update_state(true)?;
}else {
self.update_state(false)?;
}
self.update_state()?;
let history = self.script_history.entry(script).or_default();
let history_of_tx = history

View file

@ -5,7 +5,7 @@ use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use libp2p::{Multiaddr, PeerId};
use sqlx::sqlite::Sqlite;
use sqlx::{Pool, SqlitePool};
use sqlx::{Pool, Row, SqlitePool};
use std::collections::HashMap;
use std::path::Path;
use std::str::FromStr;
@ -270,6 +270,39 @@ impl Database for SqliteDatabase {
result
}
async fn get_states(&self, swap_id: Uuid) -> Result<Vec<State>> {
let mut conn = self.pool.acquire().await?;
let swap_id = swap_id.to_string();
// TODO: We should use query! instead of query here to allow for at-compile-time validation
// I didn't manage to generate the mappings for the query! macro because of problems with sqlx-cli
let rows = sqlx::query(
r#"
SELECT state
FROM swap_states
WHERE swap_id = ?
"#,
)
.bind(swap_id)
.fetch_all(&mut conn)
.await?;
let result = rows
.iter()
.map(|row| {
let state_str: &str = row.try_get("state")?;
let state = match serde_json::from_str::<Swap>(state_str) {
Ok(a) => Ok(State::from(a)),
Err(e) => Err(e),
}?;
Ok(state)
})
.collect::<Result<Vec<State>>>();
result
}
async fn raw_all(&self) -> Result<HashMap<Uuid, Vec<serde_json::Value>>> {
let mut conn = self.pool.acquire().await?;
let rows = sqlx::query!(

View file

@ -143,6 +143,7 @@ pub trait Database {
async fn get_swap_start_date(&self, swap_id: Uuid) -> Result<String>;
async fn insert_latest_state(&self, swap_id: Uuid, state: State) -> Result<()>;
async fn get_state(&self, swap_id: Uuid) -> Result<State>;
async fn get_states(&self, swap_id: Uuid) -> Result<Vec<State>>;
async fn all(&self) -> Result<Vec<(Uuid, State)>>;
async fn raw_all(&self) -> Result<HashMap<Uuid, Vec<serde_json::Value>>>;
}

View file

@ -288,13 +288,13 @@ pub struct State2 {
S_a_monero: monero::PublicKey,
S_a_bitcoin: bitcoin::PublicKey,
v: monero::PrivateViewKey,
xmr: monero::Amount,
cancel_timelock: CancelTimelock,
punish_timelock: PunishTimelock,
refund_address: bitcoin::Address,
pub xmr: monero::Amount,
pub cancel_timelock: CancelTimelock,
pub punish_timelock: PunishTimelock,
pub refund_address: bitcoin::Address,
redeem_address: bitcoin::Address,
punish_address: bitcoin::Address,
tx_lock: bitcoin::TxLock,
pub tx_lock: bitcoin::TxLock,
tx_cancel_sig_a: Signature,
tx_refund_encsig: bitcoin::EncryptedSignature,
min_monero_confirmations: u64,
@ -303,9 +303,9 @@ pub struct State2 {
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_punish_fee: bitcoin::Amount,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_refund_fee: bitcoin::Amount,
pub tx_refund_fee: bitcoin::Amount,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_cancel_fee: bitcoin::Amount,
pub tx_cancel_fee: bitcoin::Amount,
}
impl State2 {

View file

@ -51,7 +51,7 @@ pub fn register_modules(context: Arc<Context>) -> RpcModule<Arc<Context>> {
.unwrap();
module
.register_async_method("get_raw_history", |params, context| async move {
.register_async_method("get_raw_states", |params, context| async move {
execute_request(params, Method::GetRawStates, &context).await
})
.unwrap();