Skip to content

Commit

Permalink
Merge pull request #1729 from rozhkovdmitrii/feature-1682-introduce-adex
Browse files Browse the repository at this point in the history
adex tool was introduced
  • Loading branch information
ca333 authored Apr 10, 2023
2 parents 59b15da + 37a1d2a commit a0ee6b6
Show file tree
Hide file tree
Showing 19 changed files with 1,116 additions and 117 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## {inc-release}

**Features:**
- `adex-cli` command line utility was introduced that supplies commands: `init`, `start`, `stop`, `status` [#1729](https://github.com/KomodoPlatform/atomicDEX-API/pull/1729)

**Enhancements/Fixes:**
- CI/CD workflow logics are improved [#1736](https://github.com/KomodoPlatform/atomicDEX-API/pull/1736)
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ members = [
"mm2src/trezor",
]

exclude = [
"mm2src/adex_cli"
]

# https://doc.rust-lang.org/beta/cargo/reference/features.html#feature-resolver-version-2
resolver = "2"

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

The AtomicDEX API core is open-source [atomic-swap](https://komodoplatform.com/en/academy/atomic-swaps/) software for seamless, decentralised, peer to peer trading between almost every blockchain asset in existence. This software works with propagation of orderbooks and swap states through the [libp2p](https://libp2p.io/) protocol and uses [Hash Time Lock Contracts (HTLCs)](https://en.bitcoinwiki.org/wiki/Hashed_Timelock_Contracts) for ensuring that the two parties in a swap either mutually complete a trade, or funds return to thier original owner.

There is no 3rd party intermediatary, no proxy tokens, and at all times users remain in sole possession of their private keys.
There is no 3rd party intermediary, no proxy tokens, and at all times users remain in sole possession of their private keys.

A [well documented API](https://developers.komodoplatform.com/basic-docs/atomicdex/introduction-to-atomicdex.html) offers simple access to the underlying services using simple language agnostic JSON structured methods and parameters such that users can communicate with the core in a variety of methods such as [curl](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/buy.html) in CLI, or fully functioning [desktop and mobile applications](https://atomicdex.io/) like [AtomicDEX Desktop](https://github.com/KomodoPlatform/atomicDEX-Desktop).

Expand Down Expand Up @@ -115,6 +115,7 @@ For example:

The coins file contains information about the coins and tokens you want to trade. A regularly updated version is maintained in the [Komodo Platform coins repository](https://github.com/KomodoPlatform/coins/blob/master/coins). Pull Requests to add any coins not yet included are welcome.

To facilitate interoperability with the `mm2` service, there is the `adex-cli` command line utility. It provides a questionnaire initialization mode to set up the configuration and obtain the proper coin set through the internet. It can also be used to start or stop the service.

## Usage

Expand Down
26 changes: 26 additions & 0 deletions mm2src/adex_cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "adex-cli"
version = "0.1.0"
edition = "2021"
authors = ["Rozhkov Dmitrii <rozhkov@komodoplatform.com>"]
description = "Provides a CLI interface and facilitates interoperating to komodo atomic dex through the mm2 service"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
clap = "2.33.3"
common = { path = "../common" }
derive_more = "0.99"
env_logger = "0.7.1"
gstuff = { version = "=0.7.4" , features = [ "nightly" ]}
inquire = "0.6"
log = "0.4"
mm2_net = { path = "../mm2_net" }
passwords = "3.1"
serde = "1.0"
serde_json = { version = "1", features = ["preserve_order", "raw_value"] }
sysinfo = "0.28"
tiny-bip39 = "0.8.0"
tokio = { version = "1.20", features = [ "macros" ] }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.3", features = ["processthreadsapi", "winnt"] }
113 changes: 113 additions & 0 deletions mm2src/adex_cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use clap::{App, Arg, SubCommand};
use log::error;
use std::env;

use crate::scenarios::{get_status, init, start_process, stop_process};

enum Command {
Init {
mm_coins_path: String,
mm_conf_path: String,
},
Start {
mm_conf_path: Option<String>,
mm_coins_path: Option<String>,
mm_log: Option<String>,
},
Stop,
Status,
}

pub fn process_cli() {
let mut app = App::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS"))
.about(env!("CARGO_PKG_DESCRIPTION"))
.subcommand(
SubCommand::with_name("init")
.about("Initialize predefined mm2 coin set and configuration")
.arg(
Arg::with_name("mm-coins-path")
.long("mm-coins-path")
.value_name("FILE")
.help("coin set file path")
.default_value("coins"),
)
.arg(
Arg::with_name("mm-conf-path")
.long("mm-conf-path")
.value_name("FILE")
.help("mm2 configuration file path")
.default_value("MM2.json"),
),
)
.subcommand(
SubCommand::with_name("start")
.about("Start mm2 service")
.arg(
Arg::with_name("mm-conf-path")
.long("mm-conf-path")
.value_name("FILE")
.help("mm2 configuration file path"),
)
.arg(
Arg::with_name("mm-coins-path")
.long("mm-coins-path")
.value_name("FILE")
.help("coin set file path"),
)
.arg(
Arg::with_name("mm-log")
.long("mm-log")
.value_name("FILE")
.help("log file path"),
),
)
.subcommand(SubCommand::with_name("stop").about("Stop mm2 instance"))
.subcommand(SubCommand::with_name("status").about("Get mm2 running status"));

let matches = app.clone().get_matches();

let command = match matches.subcommand() {
("init", Some(init_matches)) => {
let mm_coins_path = init_matches.value_of("mm-coins-path").unwrap_or("coins").to_owned();
let mm_conf_path = init_matches.value_of("mm-conf-path").unwrap_or("MM2.json").to_owned();
Command::Init {
mm_coins_path,
mm_conf_path,
}
},
("start", Some(start_matches)) => {
let mm_conf_path = start_matches.value_of("mm-conf-path").map(|s| s.to_owned());
let mm_coins_path = start_matches.value_of("mm-coins-path").map(|s| s.to_owned());
let mm_log = start_matches.value_of("mm-log").map(|s| s.to_owned());
Command::Start {
mm_conf_path,
mm_coins_path,
mm_log,
}
},
("stop", _) => Command::Stop,
("status", _) => Command::Status,
_ => {
let _ = app
.print_long_help()
.map_err(|error| error!("Failed to print_long_help: {error}"));
return;
},
};

match command {
Command::Init {
mm_coins_path: coins_file,
mm_conf_path: mm2_cfg_file,
} => init(&mm2_cfg_file, &coins_file),
Command::Start {
mm_conf_path: mm2_cfg_file,
mm_coins_path: coins_file,
mm_log: log_file,
} => start_process(&mm2_cfg_file, &coins_file, &log_file),
Command::Stop => stop_process(),
Command::Status => get_status(),
}
}
13 changes: 13 additions & 0 deletions mm2src/adex_cli/src/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use log::LevelFilter;
use std::io::Write;

pub fn init_logging() {
let mut builder = env_logger::builder();
let level = std::env::var("RUST_LOG")
.map(|s| s.parse().expect("Failed to parse RUST_LOG"))
.unwrap_or(LevelFilter::Info);
builder
.filter_level(level)
.format(|buf, record| writeln!(buf, "{}", record.args()));
builder.init();
}
12 changes: 12 additions & 0 deletions mm2src/adex_cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[cfg(not(target_arch = "wasm32"))] mod cli;
#[cfg(not(target_arch = "wasm32"))] mod log;
#[cfg(not(target_arch = "wasm32"))] mod scenarios;

#[cfg(target_arch = "wasm32")]
fn main() {}

#[cfg(not(target_arch = "wasm32"))]
fn main() {
log::init_logging();
cli::process_cli();
}
34 changes: 34 additions & 0 deletions mm2src/adex_cli/src/scenarios/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use common::log::error;
use serde::Serialize;
use std::fs::OpenOptions;
use std::io::Write;
use std::ops::Deref;

pub fn rewrite_data_file<T>(data: T, file: &str) -> Result<(), ()>
where
T: Deref<Target = [u8]>,
{
let mut writer = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(file)
.map_err(|error| {
error!("Failed to open {file}: {error}");
})?;

writer.write(&data).map_err(|error| {
error!("Failed to write data into {file}: {error}");
})?;
Ok(())
}

pub fn rewrite_json_file<T>(value: &T, file: &str) -> Result<(), ()>
where
T: Serialize,
{
let data = serde_json::to_vec_pretty(value).map_err(|error| {
error!("Failed to serialize data {error}");
})?;
rewrite_data_file(data, file)
}
45 changes: 45 additions & 0 deletions mm2src/adex_cli/src/scenarios/init_coins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use common::log::{error, info};
use derive_more::Display;
use mm2_net::transport::slurp_url;

use super::helpers::rewrite_data_file;

#[derive(Clone, Copy, Debug, Display)]
pub enum CoinSet {
Empty,
Full,
}

#[tokio::main(flavor = "current_thread")]
pub async fn init_coins(coins_file: &str) -> Result<(), ()> {
const FULL_COIN_SET_ADDRESS: &str = "https://raw.githubusercontent.com/KomodoPlatform/coins/master/coins";
const EMPTY_COIN_SET_DATA: &[u8] = b"[]\n";
let coin_set = inquire_coin_set(coins_file)?;
info!("Start getting mm2 coins");
let coins_data = match coin_set {
CoinSet::Empty => Vec::<u8>::from(EMPTY_COIN_SET_DATA),
CoinSet::Full => {
info!("Getting coin set from: {FULL_COIN_SET_ADDRESS}");
let (_status_code, _headers, data) = slurp_url(FULL_COIN_SET_ADDRESS).await.map_err(|error| {
error!("Failed to get coin set from: {FULL_COIN_SET_ADDRESS}, error: {error}");
})?;
data
},
};

rewrite_data_file(coins_data, coins_file)?;
info!("Got coins data, written into: {coins_file}");
Ok(())
}

fn inquire_coin_set(coins_file: &str) -> Result<CoinSet, ()> {
inquire::Select::new(
format!("Select one of predefined coin sets to save into: {coins_file}").as_str(),
vec![CoinSet::Empty, CoinSet::Full],
)
.with_help_message("Information about the currencies: their ticker symbols, names, ports, addresses, etc.")
.prompt()
.map_err(|error| {
error!("Failed to select coin_set: {error}");
})
}
Loading

0 comments on commit a0ee6b6

Please sign in to comment.