mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Merge #1195
1195: fix(asb): use unlocked monero balance for quotes r=binarybaron a=delta1 Related issue #1192 - changes the ASB to use the unlocked (spendable) Monero balance when providing a quote - upgrades monero docker images and downloads to v0.18.1.2 - includes "immediately fetch transaction status upon subscription" from 0.11.x branch #1188 This will also be backported to [0.11.x](https://github.com/comit-network/xmr-btc-swap/tree/0.11.x) branch for a new release Co-authored-by: Byron Hambly <bhambly@blockstream.com> Co-authored-by: binarybaron <86064887+binarybaron@users.noreply.github.com>
This commit is contained in:
commit
b1fe7b0d2f
@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed ASB to quote on Monero unlocked balance instead of total balance
|
||||
|
||||
### Added
|
||||
|
||||
## [0.11.0] - 2022-08-11
|
||||
|
||||
### Changed
|
||||
@ -320,7 +326,7 @@ It is possible to migrate critical data from the old db to the sqlite but there
|
||||
- Fixed an issue where Alice would not verify if Bob's Bitcoin lock transaction is semantically correct, i.e. pays the agreed upon amount to an output owned by both of them.
|
||||
Fixing this required a **breaking change** on the network layer and hence old versions are not compatible with this version.
|
||||
|
||||
[Unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...HEAD
|
||||
[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.11.0...HEAD
|
||||
[0.11.0]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.2...0.11.0
|
||||
[0.10.2]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.1...0.10.2
|
||||
[0.10.1]: https://github.com/comit-network/xmr-btc-swap/compare/0.10.0...0.10.1
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2326,6 +2326,7 @@ dependencies = [
|
||||
"monero-epee-bin-serde",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
|
@ -25,7 +25,7 @@ impl Image for Monerod {
|
||||
type EntryPoint = str;
|
||||
|
||||
fn descriptor(&self) -> String {
|
||||
"rinocommunity/monero:v0.18.0.0".to_owned()
|
||||
"rinocommunity/monero:v0.18.1.2".to_owned()
|
||||
}
|
||||
|
||||
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
|
||||
@ -70,7 +70,7 @@ impl Image for MoneroWalletRpc {
|
||||
type EntryPoint = str;
|
||||
|
||||
fn descriptor(&self) -> String {
|
||||
"rinocommunity/monero:v0.18.0.0".to_owned()
|
||||
"rinocommunity/monero:v0.18.1.2".to_owned()
|
||||
}
|
||||
|
||||
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
|
||||
@ -229,6 +229,7 @@ impl IntoIterator for MoneroWalletRpcArgs {
|
||||
format!("--daemon-address={}", self.daemon_address),
|
||||
format!("--rpc-bind-port={}", RPC_PORT),
|
||||
"--log-level=4".to_string(),
|
||||
"--allow-mismatched-daemon-version".to_string(), /* https://github.com/monero-project/monero/issues/8600 */
|
||||
];
|
||||
|
||||
if self.disable_rpc_login {
|
||||
|
@ -138,15 +138,30 @@ impl<'c> Monero {
|
||||
let wallet = self.wallet(name)?;
|
||||
let address = wallet.address().await?.address;
|
||||
|
||||
let mut expected_total = 0;
|
||||
let mut expected_unlocked = 0;
|
||||
let mut unlocked = 0;
|
||||
for amount in amount_in_outputs {
|
||||
if amount > 0 {
|
||||
miner_wallet.transfer(&address, amount).await?;
|
||||
expected_total += amount;
|
||||
tracing::info!("Funded {} wallet with {}", wallet.name, amount);
|
||||
|
||||
// sanity checks for total/unlocked balance
|
||||
let total = wallet.balance().await?;
|
||||
assert_eq!(total, expected_total);
|
||||
assert_eq!(unlocked, expected_unlocked);
|
||||
|
||||
monerod
|
||||
.client()
|
||||
.generateblocks(10, miner_address.clone())
|
||||
.await?;
|
||||
wallet.refresh().await?;
|
||||
expected_unlocked += amount;
|
||||
|
||||
unlocked = wallet.unlocked_balance().await?;
|
||||
assert_eq!(unlocked, expected_unlocked);
|
||||
assert_eq!(total, expected_total);
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,10 +325,18 @@ impl<'c> MoneroWalletRpc {
|
||||
Ok(balance)
|
||||
}
|
||||
|
||||
pub async fn unlocked_balance(&self) -> Result<u64> {
|
||||
self.client().refresh().await?;
|
||||
let balance = self.client().get_balance(0).await?.unlocked_balance;
|
||||
|
||||
Ok(balance)
|
||||
}
|
||||
|
||||
pub async fn refresh(&self) -> Result<Refreshed> {
|
||||
Ok(self.client().refresh().await?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mine a block ever BLOCK_TIME_SECS seconds.
|
||||
async fn mine(monerod: monerod::Client, reward_address: String) -> Result<()> {
|
||||
loop {
|
||||
|
@ -13,6 +13,7 @@ monero = "0.12"
|
||||
monero-epee-bin-serde = "1"
|
||||
rand = "0.7"
|
||||
reqwest = { version = "0.11", default-features = false, features = [ "json" ] }
|
||||
rust_decimal = { version = "1", features = [ "serde-float" ] }
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
serde_json = "1.0"
|
||||
tracing = "0.1"
|
||||
|
@ -1,4 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use rust_decimal::Decimal;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
@ -86,10 +89,30 @@ pub struct GetAddress {
|
||||
#[derive(Deserialize, Debug, Clone, Copy)]
|
||||
pub struct GetBalance {
|
||||
pub balance: u64,
|
||||
pub blocks_to_unlock: u32,
|
||||
pub multisig_import_needed: bool,
|
||||
pub time_to_unlock: u32,
|
||||
pub unlocked_balance: u64,
|
||||
pub multisig_import_needed: bool,
|
||||
pub blocks_to_unlock: u32,
|
||||
pub time_to_unlock: u32,
|
||||
}
|
||||
|
||||
impl fmt::Display for GetBalance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut total = Decimal::from(self.balance);
|
||||
total
|
||||
.set_scale(12)
|
||||
.expect("12 is smaller than max precision of 28");
|
||||
|
||||
let mut unlocked = Decimal::from(self.unlocked_balance);
|
||||
unlocked
|
||||
.set_scale(12)
|
||||
.expect("12 is smaller than max precision of 28");
|
||||
|
||||
write!(
|
||||
f,
|
||||
"total balance: {}, unlocked balance: {}",
|
||||
total, unlocked
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::asb::{Behaviour, OutEvent, Rate};
|
||||
use crate::monero::Amount;
|
||||
use crate::network::quote::BidQuote;
|
||||
use crate::network::swap_setup::alice::WalletSnapshot;
|
||||
use crate::network::transfer_proof;
|
||||
@ -326,7 +327,10 @@ where
|
||||
.ask()
|
||||
.context("Failed to compute asking price")?;
|
||||
|
||||
let xmr = self.monero_wallet.get_balance().await?;
|
||||
let balance = self.monero_wallet.get_balance().await?;
|
||||
|
||||
// use unlocked monero balance for quote
|
||||
let xmr = Amount::from_piconero(balance.unlocked_balance);
|
||||
|
||||
let max_bitcoin_for_monero = xmr.max_bitcoin_for_price(ask_price).ok_or_else(|| {
|
||||
anyhow::anyhow!("Bitcoin price ({}) x Monero ({}) overflow", ask_price, xmr)
|
||||
|
@ -31,7 +31,6 @@ use swap::asb::config::{
|
||||
use swap::asb::{cancel, punish, redeem, refund, safely_abort, EventLoop, Finality, KrakenRate};
|
||||
use swap::common::check_latest_version;
|
||||
use swap::database::open_db;
|
||||
use swap::monero::Amount;
|
||||
use swap::network::rendezvous::XmrBtcNamespace;
|
||||
use swap::network::swarm;
|
||||
use swap::protocol::alice::{run, AliceState};
|
||||
@ -103,23 +102,34 @@ async fn main() -> Result<()> {
|
||||
|
||||
match cmd {
|
||||
Command::Start { resume_only } => {
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
|
||||
let monero_wallet = init_monero_wallet(&config, env_config).await?;
|
||||
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
tracing::info!(%bitcoin_balance, "Initialized Bitcoin wallet");
|
||||
|
||||
let monero_balance = monero_wallet.get_balance().await?;
|
||||
if monero_balance == Amount::ZERO {
|
||||
let monero_address = monero_wallet.get_main_address();
|
||||
tracing::info!(%monero_address, "Monero wallet address");
|
||||
let monero = monero_wallet.get_balance().await?;
|
||||
match (monero.balance, monero.unlocked_balance) {
|
||||
(0, _) => {
|
||||
tracing::warn!(
|
||||
%monero_address,
|
||||
"The Monero balance is 0, make sure to deposit funds at",
|
||||
)
|
||||
} else {
|
||||
tracing::info!(%monero_balance, "Initialized Monero wallet");
|
||||
}
|
||||
(total, 0) => {
|
||||
let total = monero::Amount::from_piconero(total);
|
||||
tracing::warn!(
|
||||
%total,
|
||||
"Unlocked Monero balance is 0, total balance is",
|
||||
)
|
||||
}
|
||||
(total, unlocked) => {
|
||||
let total = monero::Amount::from_piconero(total);
|
||||
let unlocked = monero::Amount::from_piconero(unlocked);
|
||||
tracing::info!(%total, %unlocked, "Monero wallet balance");
|
||||
}
|
||||
}
|
||||
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
tracing::info!(%bitcoin_balance, "Bitcoin wallet balance");
|
||||
|
||||
let kraken_price_updates = kraken::connect(config.maker.price_ticker_ws_url.clone())?;
|
||||
|
||||
@ -236,16 +246,14 @@ async fn main() -> Result<()> {
|
||||
bitcoin_wallet.broadcast(signed_tx, "withdraw").await?;
|
||||
}
|
||||
Command::Balance => {
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
let monero_wallet = init_monero_wallet(&config, env_config).await?;
|
||||
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
let monero_balance = monero_wallet.get_balance().await?;
|
||||
tracing::info!(%monero_balance);
|
||||
|
||||
tracing::info!(
|
||||
%bitcoin_balance,
|
||||
%monero_balance,
|
||||
"Current balance");
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
tracing::info!(%bitcoin_balance);
|
||||
tracing::info!(%bitcoin_balance, %monero_balance, "Current balance");
|
||||
}
|
||||
Command::Cancel { swap_id } => {
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
|
@ -169,8 +169,6 @@ impl Wallet {
|
||||
let mut last_status = None;
|
||||
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
|
||||
let new_status = match client.lock().await.status_of_script(&tx) {
|
||||
Ok(new_status) => new_status,
|
||||
Err(error) => {
|
||||
@ -191,6 +189,8 @@ impl Wallet {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
});
|
||||
|
||||
@ -743,7 +743,7 @@ impl Client {
|
||||
electrum,
|
||||
blockchain,
|
||||
latest_block_height: BlockHeight::try_from(latest_block)?,
|
||||
last_sync: Instant::now(),
|
||||
last_sync: Instant::now() - interval,
|
||||
sync_interval: interval,
|
||||
script_history: Default::default(),
|
||||
subscriptions: Default::default(),
|
||||
|
@ -47,10 +47,10 @@ mod test {
|
||||
#[tokio::test]
|
||||
#[ignore = "For local testing, makes http requests to github."]
|
||||
async fn it_compares_with_github() {
|
||||
let result = check_latest_version("0.10.1").await.unwrap();
|
||||
let result = check_latest_version("0.11.0").await.unwrap();
|
||||
assert_eq!(result, Version::Available);
|
||||
|
||||
let result = check_latest_version("0.10.2").await.unwrap();
|
||||
let result = check_latest_version("0.11.1").await.unwrap();
|
||||
assert_eq!(result, Version::Current);
|
||||
}
|
||||
}
|
||||
|
@ -253,10 +253,8 @@ impl Wallet {
|
||||
}
|
||||
|
||||
/// Get the balance of the primary account.
|
||||
pub async fn get_balance(&self) -> Result<Amount> {
|
||||
let amount = self.inner.lock().await.get_balance(0).await?.balance;
|
||||
|
||||
Ok(Amount::from_piconero(amount))
|
||||
pub async fn get_balance(&self) -> Result<wallet::GetBalance> {
|
||||
Ok(self.inner.lock().await.get_balance(0).await?)
|
||||
}
|
||||
|
||||
pub async fn block_height(&self) -> Result<BlockHeight> {
|
||||
|
@ -18,20 +18,20 @@ use tokio_util::io::StreamReader;
|
||||
compile_error!("unsupported operating system");
|
||||
|
||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-x64-v0.18.0.0.tar.bz2";
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-x64-v0.18.1.2.tar.bz2";
|
||||
|
||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-armv8-v0.18.0.0.tar.bz2";
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.0.0.tar.bz2";
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.1.2.tar.bz2";
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "arm"))]
|
||||
const DOWNLOAD_URL: &str =
|
||||
"https://downloads.getmonero.org/cli/monero-linux-armv7-v0.18.0.0.tar.bz2";
|
||||
"https://downloads.getmonero.org/cli/monero-linux-armv7-v0.18.1.2.tar.bz2";
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-win-x64-v0.18.0.0.zip";
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-win-x64-v0.18.1.2.zip";
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
const PACKED_FILE: &str = "monero-wallet-rpc";
|
||||
@ -39,7 +39,7 @@ const PACKED_FILE: &str = "monero-wallet-rpc";
|
||||
#[cfg(target_os = "windows")]
|
||||
const PACKED_FILE: &str = "monero-wallet-rpc.exe";
|
||||
|
||||
const CODENAME: &str = "Fluorine Fermi";
|
||||
const WALLET_RPC_VERSION: &str = "v0.18.1.2";
|
||||
|
||||
#[derive(Debug, Clone, Copy, thiserror::Error)]
|
||||
#[error("monero wallet rpc executable not found in downloaded archive")]
|
||||
@ -86,7 +86,7 @@ impl WalletRpc {
|
||||
let version = String::from_utf8_lossy(&output.stdout);
|
||||
tracing::debug!("RPC version output: {}", version);
|
||||
|
||||
if !version.contains(CODENAME) {
|
||||
if !version.contains(WALLET_RPC_VERSION) {
|
||||
tracing::info!("Removing old version of monero-wallet-rpc");
|
||||
tokio::fs::remove_file(exec_path).await?;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::asb::LatestRate;
|
||||
use crate::monero::Amount;
|
||||
use crate::network::swap_setup;
|
||||
use crate::network::swap_setup::{
|
||||
protocol, BlockchainNetwork, SpotPriceError, SpotPriceRequest, SpotPriceResponse,
|
||||
@ -42,7 +43,7 @@ pub enum OutEvent {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WalletSnapshot {
|
||||
balance: monero::Amount,
|
||||
balance: monero_rpc::wallet::GetBalance,
|
||||
lock_fee: monero::Amount,
|
||||
|
||||
// TODO: Consider using the same address for punish and redeem (they are mutually exclusive, so
|
||||
@ -323,7 +324,8 @@ where
|
||||
.sell_quote(btc)
|
||||
.map_err(Error::SellQuoteCalculationFailed)?;
|
||||
|
||||
if wallet_snapshot.balance < xmr + wallet_snapshot.lock_fee {
|
||||
let unlocked = Amount::from_piconero(wallet_snapshot.balance.unlocked_balance);
|
||||
if unlocked < xmr + wallet_snapshot.lock_fee {
|
||||
return Err(Error::BalanceTooLow {
|
||||
balance: wallet_snapshot.balance,
|
||||
buy: btc,
|
||||
@ -479,9 +481,9 @@ pub enum Error {
|
||||
max: bitcoin::Amount,
|
||||
buy: bitcoin::Amount,
|
||||
},
|
||||
#[error("Balance {balance} too low to fulfill swapping {buy}")]
|
||||
#[error("Unlocked balance ({balance}) too low to fulfill swapping {buy}")]
|
||||
BalanceTooLow {
|
||||
balance: monero::Amount,
|
||||
balance: monero_rpc::wallet::GetBalance,
|
||||
buy: bitcoin::Amount,
|
||||
},
|
||||
#[error("Failed to fetch latest rate")]
|
||||
|
@ -867,7 +867,9 @@ impl Wallet for monero::Wallet {
|
||||
}
|
||||
|
||||
async fn get_balance(&self) -> Result<Self::Amount> {
|
||||
self.get_balance().await
|
||||
let total = self.get_balance().await?;
|
||||
let balance = Self::Amount::from_piconero(total.balance);
|
||||
Ok(balance)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user