Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(adex-cli): provide activation request types to prevent malicious… #1912

Merged
merged 8 commits into from
Jul 26, 2023
14 changes: 12 additions & 2 deletions mm2src/adex_cli/src/activation_scheme_db/activation_scheme_impl.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
use anyhow::{anyhow, bail, Result};
use log::{debug, error};
use serde_json::Value as Json;
use std::collections::HashMap;

use common::log::{debug, error};

use super::init_activation_scheme::get_activation_scheme_path;
use crate::helpers::read_json_file;
use crate::logging::{error_anyhow, error_bail};
use crate::rpc_data::ActivationRequest;

#[derive(Default)]
pub(crate) struct ActivationScheme {
scheme: HashMap<String, Json>,
}

impl ActivationScheme {
pub(crate) fn get_activation_method(&self, coin: &str) -> Option<&Json> { self.scheme.get(coin) }
pub(crate) fn get_activation_method(&self, coin: &str) -> Result<ActivationRequest> {
let method_json = self
.scheme
.get(coin)
.ok_or_else(|| error_anyhow!("Coin is not in activation scheme data: {}", coin))?;
let method: ActivationRequest = serde_json::from_value(method_json.clone())
.map_err(|error| error_anyhow!("Failed to deserialize json data: {:?}, error: {}", method_json, error))?;
Ok(method)
}

fn init(&mut self) -> Result<()> {
let mut scheme_source: Vec<Json> = Self::load_json_file()?;
Expand Down
6 changes: 2 additions & 4 deletions mm2src/adex_cli/src/adex_proc/adex_proc_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::OrderbookConfig;
use crate::activation_scheme_db::get_activation_scheme;
use crate::adex_config::AdexConfig;
use crate::transport::Transport;
use crate::{error_anyhow, error_bail, warn_anyhow, warn_bail};
use crate::{error_anyhow, error_bail, warn_anyhow};

pub(crate) struct AdexProc<'trp, 'hand, 'cfg, T: Transport, H: ResponseHandler, C: AdexConfig + ?Sized> {
pub(crate) transport: Option<&'trp T>,
Expand All @@ -37,9 +37,7 @@ impl<T: Transport, P: ResponseHandler, C: AdexConfig + 'static> AdexProc<'_, '_,
info!("Enabling asset: {asset}");

let activation_scheme = get_activation_scheme()?;
let Some(activation_method) = activation_scheme.get_activation_method(asset) else {
warn_bail!("Asset is not known: {asset}")
};
let activation_method = activation_scheme.get_activation_method(asset)?;

let enable = Command::builder()
.flatten_data(activation_method)
Expand Down
1 change: 1 addition & 0 deletions mm2src/adex_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#[cfg(not(target_arch = "wasm32"))] mod cli;
#[cfg(not(target_arch = "wasm32"))] mod helpers;
mod logging;
#[cfg(not(target_arch = "wasm32"))] mod rpc_data;
#[cfg(not(target_arch = "wasm32"))] mod scenarios;
#[cfg(all(not(target_arch = "wasm32"), test))] mod tests;
#[cfg(not(target_arch = "wasm32"))] mod transport;
Expand Down
71 changes: 71 additions & 0 deletions mm2src/adex_cli/src/rpc_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! Contains rpc data layer structures that are not ready to become a part of the mm2_rpc::data module
//!
//! *Note: it's expected that the following data types will be moved to mm2_rpc::data when mm2 is refactored to be able to handle them*
//!

use mm2_rpc::data::legacy::{ElectrumProtocol, GasStationPricePolicy, UtxoMergeParams};
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "method", rename_all = "lowercase")]
pub(crate) enum ActivationRequest {
shamardy marked this conversation as resolved.
Show resolved Hide resolved
Enable(EnableRequest),
Electrum(ElectrumRequest),
}

#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct EnableRequest {
shamardy marked this conversation as resolved.
Show resolved Hide resolved
coin: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
urls: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
swap_contract_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
fallback_swap_contract: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
gas_station_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
gas_station_decimals: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
gas_station_policy: Option<GasStationPricePolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
mm2: Option<u8>,
#[serde(default)]
tx_history: bool,
#[serde(skip_serializing_if = "Option::is_none")]
required_confirmations: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
requires_notarization: Option<bool>,
#[serde(default)]
contract_supports_watchers: Option<bool>,
}

#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct ElectrumRequest {
shamardy marked this conversation as resolved.
Show resolved Hide resolved
coin: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(super) servers: Vec<Server>,
#[serde(skip_serializing_if = "Option::is_none")]
mm2: Option<u8>,
#[serde(default)]
tx_history: bool,
#[serde(skip_serializing_if = "Option::is_none")]
required_confirmations: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
requires_notarization: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
swap_contract_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
fallback_swap_contract: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
utxo_merge_params: Option<UtxoMergeParams>,
}

#[derive(Debug, Deserialize, Serialize)]
pub(super) struct Server {
shamardy marked this conversation as resolved.
Show resolved Hide resolved
url: String,
#[serde(default)]
protocol: ElectrumProtocol,
#[serde(default)]
disable_cert_verification: bool,
}
9 changes: 5 additions & 4 deletions mm2src/adex_cli/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::activation_scheme_db::{get_activation_scheme, init_activation_scheme}
use crate::adex_config::AdexConfigImpl;
use crate::adex_proc::ResponseHandlerImpl;
use crate::cli::Cli;
use crate::rpc_data::ActivationRequest;

const FAKE_SERVER_COOLDOWN_TIMEOUT_MS: u64 = 10;
const FAKE_SERVER_WARMUP_TIMEOUT_MS: u64 = 100;
Expand Down Expand Up @@ -145,10 +146,10 @@ async fn test_activation_scheme() {
init_activation_scheme().await.unwrap();
let scheme = get_activation_scheme().unwrap();
let kmd_scheme = scheme.get_activation_method("KMD");
assert!(kmd_scheme.is_some());
let kmd_scheme = kmd_scheme.unwrap();
assert_eq!(kmd_scheme.get("method").unwrap().as_str().unwrap(), "electrum");
assert_eq!(kmd_scheme.get("servers").unwrap().as_array().unwrap().iter().count(), 3);
let Ok(ActivationRequest::Electrum(electrum)) = kmd_scheme else {
panic!("Failed to get electrum scheme")
};
assert_eq!(electrum.servers.len(), 3);
}

#[tokio::test]
Expand Down
18 changes: 2 additions & 16 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use mm2_err_handle::prelude::*;
use mm2_net::transport::{slurp_url, GuiAuthValidation, GuiAuthValidationGenerator, SlurpError};
use mm2_number::bigdecimal_custom::CheckedDivision;
use mm2_number::{BigDecimal, MmNumber};
use mm2_rpc::data::legacy::GasStationPricePolicy;
#[cfg(test)] use mocktopus::macros::*;
use rand::seq::SliceRandom;
use rpc::v1::types::Bytes as BytesJson;
Expand Down Expand Up @@ -4247,7 +4248,7 @@ impl EthCoin {
// TODO refactor to error_log_passthrough once simple maker bot is merged
let gas_station_price = match &coin.gas_station_url {
Some(url) => {
match GasStationData::get_gas_price(url, coin.gas_station_decimals, coin.gas_station_policy)
match GasStationData::get_gas_price(url, coin.gas_station_decimals, coin.gas_station_policy.clone())
.compat()
.await
{
Expand Down Expand Up @@ -5000,21 +5001,6 @@ pub struct GasStationData {
fast: MmNumber,
}

/// Using tagged representation to allow adding variants with coefficients, percentage, etc in the future.
#[derive(Clone, Copy, Debug, Deserialize)]
#[serde(tag = "policy", content = "additional_data")]
pub enum GasStationPricePolicy {
/// Use mean between average and fast values, default and recommended to use on ETH mainnet due to
/// gas price big spikes.
MeanAverageFast,
/// Use average value only. Useful for non-heavily congested networks (Matic, etc.)
Average,
}

impl Default for GasStationPricePolicy {
fn default() -> Self { GasStationPricePolicy::MeanAverageFast }
}

impl GasStationData {
fn average_gwei(&self, decimals: u8, gas_price_policy: GasStationPricePolicy) -> NumConversResult<U256> {
let gas_price = match gas_price_policy {
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/eth/v2_activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl EthCoin {
ticker,
gas_station_url: self.gas_station_url.clone(),
gas_station_decimals: self.gas_station_decimals,
gas_station_policy: self.gas_station_policy,
gas_station_policy: self.gas_station_policy.clone(),
web3,
web3_instances,
history_sync_state: Mutex::new(self.history_sync_state.lock().unwrap().clone()),
Expand Down
10 changes: 1 addition & 9 deletions mm2src/coins/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ use mm2_core::mm_ctx::MmArc;
use mm2_err_handle::prelude::*;
use mm2_metrics::MetricsArc;
use mm2_number::BigDecimal;
use mm2_rpc::data::legacy::UtxoMergeParams;
#[cfg(test)] use mocktopus::macros::*;
use num_traits::ToPrimitive;
use primitives::hash::{H160, H256, H264};
Expand Down Expand Up @@ -1341,15 +1342,6 @@ impl RpcTransportEventHandler for ElectrumProtoVerifier {
}
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct UtxoMergeParams {
pub merge_at: usize,
#[serde(default = "common::ten_f64")]
pub check_every: f64,
#[serde(default = "common::one_hundred")]
pub max_merge_at_once: usize,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct UtxoActivationParams {
pub mode: UtxoRpcMode,
Expand Down
25 changes: 1 addition & 24 deletions mm2src/coins/utxo/rpc_clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use keys::hash::H256;
use keys::{Address, Type as ScriptType};
use mm2_err_handle::prelude::*;
use mm2_number::{BigDecimal, BigInt, MmNumber};
use mm2_rpc::data::legacy::ElectrumProtocol;
#[cfg(test)] use mocktopus::macros::*;
use rpc::v1::types::{Bytes as BytesJson, Transaction as RpcTransaction, H256 as H256Json};
use serde_json::{self as json, Value as Json};
Expand Down Expand Up @@ -1384,30 +1385,6 @@ pub fn electrum_script_hash(script: &[u8]) -> Vec<u8> {
result
}

#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug, Deserialize, Serialize)]
/// Deserializable Electrum protocol representation for RPC
pub enum ElectrumProtocol {
/// TCP
TCP,
/// SSL/TLS
SSL,
/// Insecure WebSocket.
WS,
/// Secure WebSocket.
WSS,
}

#[cfg(not(target_arch = "wasm32"))]
impl Default for ElectrumProtocol {
fn default() -> Self { ElectrumProtocol::TCP }
}

#[cfg(target_arch = "wasm32")]
impl Default for ElectrumProtocol {
fn default() -> Self { ElectrumProtocol::WS }
}

#[derive(Debug, Deserialize, Serialize)]
/// Deserializable Electrum protocol version representation for RPC
/// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html#server.version
Expand Down
10 changes: 0 additions & 10 deletions mm2src/coins/utxo/utxo_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ use chain::{OutPoint, TransactionOutput};
use common::executor::Timer;
use common::jsonrpc_client::JsonRpcErrorType;
use common::log::{error, warn};
use common::{one_hundred, ten_f64};
use crypto::{Bip32DerPathOps, Bip44Chain, RpcDerivationPath, StandardHDPath, StandardHDPathError};
use futures::compat::Future01CompatExt;
use futures::future::{FutureExt, TryFutureExt};
Expand Down Expand Up @@ -86,15 +85,6 @@ lazy_static! {

pub const HISTORY_TOO_LARGE_ERR_CODE: i64 = -1;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct UtxoMergeParams {
merge_at: usize,
#[serde(default = "ten_f64")]
check_every: f64,
#[serde(default = "one_hundred")]
max_merge_at_once: usize,
}

pub async fn get_tx_fee(coin: &UtxoCoinFields) -> UtxoRpcResult<ActualTxFee> {
let conf = &coin.conf;
match &coin.tx_fee {
Expand Down
Loading