585: Configurable kraken websocket url via the ASB config r=thomaseizinger a=cimble-code

- Allows the ASB operator to configure a custom kraken websocket url via the ASB config. 
- Addresses the issue of price control first brought up [here](https://github.com/comit-network/xmr-btc-swap/discussions/571)

>  Gotya. 
There is a relatively easy to implement (but temporary) solution for that. We could let the user configure the kraken websocket url via the ASB config. That way you can plug in your own service. The only requirement is that your service publishes prices updates in the same format as [kraken](https://docs.kraken.com/websockets/), e.g. : 

_Originally posted by @bonomat in https://github.com/comit-network/xmr-btc-swap/discussions/571#discussioncomment-885535_

Co-authored-by: Your Name <you@example.com>
This commit is contained in:
bors[bot] 2021-06-28 12:05:00 +00:00 committed by GitHub
commit b708d5a4dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 23 additions and 6 deletions

View File

@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Printing the deposit address to the terminal as a QR code.
To not break automated scripts or integrations with other software, this behaviour is disabled if `--json` is passed to the application.
- Configuration setting for the websocket URL that the ASB connects to in order to receive price ticker updates. Can be configured manually by editing the config.toml file directly. It is expected that the server behind the url follows the same protocol as the [kraken websocket api](https://docs.kraken.com/websockets/)
### Fixed

View File

@ -27,6 +27,7 @@ pub struct Defaults {
listen_address_ws: Multiaddr,
electrum_rpc_url: Url,
monero_wallet_rpc_url: Url,
price_ticker_ws_url: Url,
bitcoin_confirmation_target: usize,
}
@ -41,6 +42,7 @@ impl GetDefaults for Testnet {
listen_address_ws: Multiaddr::from_str("/ip4/0.0.0.0/tcp/9940/ws")?,
electrum_rpc_url: Url::parse("ssl://electrum.blockstream.info:60002")?,
monero_wallet_rpc_url: Url::parse("http://127.0.0.1:38083/json_rpc")?,
price_ticker_ws_url: Url::parse("wss://ws.kraken.com")?,
bitcoin_confirmation_target: 1,
};
@ -59,6 +61,7 @@ impl GetDefaults for Mainnet {
listen_address_ws: Multiaddr::from_str("/ip4/0.0.0.0/tcp/9940/ws")?,
electrum_rpc_url: Url::parse("ssl://electrum.blockstream.info:50002")?,
monero_wallet_rpc_url: Url::parse("http://127.0.0.1:18083/json_rpc")?,
price_ticker_ws_url: Url::parse("wss://ws.kraken.com")?,
bitcoin_confirmation_target: 3,
};
@ -151,6 +154,7 @@ pub struct Maker {
#[serde(with = "::bitcoin::util::amount::serde::as_btc")]
pub max_buy_btc: bitcoin::Amount,
pub ask_spread: Decimal,
pub price_ticker_ws_url: Url,
}
impl Default for TorConf {
@ -307,6 +311,7 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> {
min_buy_btc: min_buy,
max_buy_btc: max_buy,
ask_spread,
price_ticker_ws_url: defaults.price_ticker_ws_url,
},
})
}
@ -347,6 +352,7 @@ mod tests {
min_buy_btc: bitcoin::Amount::from_btc(DEFAULT_MIN_BUY_AMOUNT).unwrap(),
max_buy_btc: bitcoin::Amount::from_btc(DEFAULT_MAX_BUY_AMOUNT).unwrap(),
ask_spread: Decimal::from_f64(DEFAULT_SPREAD).unwrap(),
price_ticker_ws_url: defaults.price_ticker_ws_url,
},
};
@ -387,6 +393,7 @@ mod tests {
min_buy_btc: bitcoin::Amount::from_btc(DEFAULT_MIN_BUY_AMOUNT).unwrap(),
max_buy_btc: bitcoin::Amount::from_btc(DEFAULT_MAX_BUY_AMOUNT).unwrap(),
ask_spread: Decimal::from_f64(DEFAULT_SPREAD).unwrap(),
price_ticker_ws_url: defaults.price_ticker_ws_url,
},
};

View File

@ -124,7 +124,7 @@ async fn main() -> Result<()> {
info!(%monero_balance, "Initialized Monero wallet");
}
let kraken_price_updates = kraken::connect()?;
let kraken_price_updates = kraken::connect(config.maker.price_ticker_ws_url)?;
// setup Tor hidden services
let tor_client =

View File

@ -1,4 +1,5 @@
use anyhow::{Context, Result};
use url::Url;
#[tokio::main]
async fn main() -> Result<()> {
@ -6,7 +7,9 @@ async fn main() -> Result<()> {
tracing_subscriber::fmt().with_env_filter("debug").finish(),
)?;
let mut ticker = swap::kraken::connect().context("Failed to connect to kraken")?;
let price_ticker_ws_url = Url::parse("wss://ws.kraken.com")?;
let mut ticker =
swap::kraken::connect(price_ticker_ws_url).context("Failed to connect to kraken")?;
loop {
match ticker.wait_for_next_update().await? {

View File

@ -5,11 +5,16 @@ use std::convert::{Infallible, TryFrom};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::watch;
use url::Url;
/// Connect to Kraken websocket API for a constant stream of rate updates.
///
/// If the connection fails, it will automatically be re-established.
pub fn connect() -> Result<PriceUpdates> {
///
/// price_ticker_ws_url must point to a websocket server that follows the kraken
/// price ticker protocol
/// See: https://docs.kraken.com/websockets/
pub fn connect(price_ticker_ws_url: Url) -> Result<PriceUpdates> {
let (price_update, price_update_receiver) = watch::channel(Err(Error::NotYetAvailable));
let price_update = Arc::new(price_update);
@ -26,8 +31,9 @@ pub fn connect() -> Result<PriceUpdates> {
backoff,
|| {
let price_update = price_update.clone();
let price_ticker_ws_url = price_ticker_ws_url.clone();
async move {
let mut stream = connection::new().await?;
let mut stream = connection::new(price_ticker_ws_url).await?;
while let Some(update) = stream.try_next().await.map_err(to_backoff)? {
let send_result = price_update.send(Ok(update));
@ -123,8 +129,8 @@ mod connection {
use futures::stream::BoxStream;
use tokio_tungstenite::tungstenite;
pub async fn new() -> Result<BoxStream<'static, Result<wire::PriceUpdate, Error>>> {
let (mut rate_stream, _) = tokio_tungstenite::connect_async("wss://ws.kraken.com")
pub async fn new(ws_url: Url) -> Result<BoxStream<'static, Result<wire::PriceUpdate, Error>>> {
let (mut rate_stream, _) = tokio_tungstenite::connect_async(ws_url)
.await
.context("Failed to connect to Kraken websocket API")?;