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,8 +1,10 @@
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 mm2_rpc::data::legacy::ActivationRequest;

use super::init_activation_scheme::get_activation_scheme_path;
use crate::helpers::read_json_file;
use crate::logging::{error_anyhow, error_bail};
Expand All @@ -13,7 +15,15 @@ pub(crate) struct ActivationScheme {
}

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
10 changes: 6 additions & 4 deletions mm2src/adex_cli/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::time::Duration;
use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};

use mm2_rpc::data::legacy::ActivationRequest;

use crate::activation_scheme_db::{get_activation_scheme, init_activation_scheme};
use crate::adex_config::AdexConfigImpl;
use crate::adex_proc::ResponseHandlerImpl;
Expand Down Expand Up @@ -145,10 +147,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
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
101 changes: 97 additions & 4 deletions mm2src/mm2_rpc/src/data/legacy.rs
onur-ozkan marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use common::serde_derive::{Deserialize, Serialize};
use derive_more::Display;
use mm2_number::{construct_detailed, BigDecimal, BigRational, Fraction, MmNumber};
use rpc::v1::types::H256 as H256Json;
use std::collections::HashSet;
use std::ops::Deref;
use uuid::Uuid;

use common::serde_derive::{Deserialize, Serialize};
use common::{one_hundred, ten_f64, true_f};
use mm2_number::{construct_detailed, BigDecimal, BigRational, Fraction, MmNumber};

#[derive(Serialize, Deserialize)]
pub struct Mm2RpcResult<T> {
pub result: T,
Expand Down Expand Up @@ -139,7 +141,7 @@ pub struct SellBuyRequest {
pub rel_confs: Option<u64>,
pub rel_nota: Option<bool>,
pub min_volume: Option<MmNumber>,
#[serde(default = "get_true")]
#[serde(default = "true_f")]
pub save_in_history: bool,
}

Expand Down Expand Up @@ -254,4 +256,95 @@ pub struct MmVersionResponse {
pub datetime: String,
}

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

#[derive(Debug, Deserialize, Serialize)]
pub struct EnableRequest {
pub coin: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub urls: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub swap_contract_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fallback_swap_contract: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gas_station_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gas_station_decimals: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gas_station_policy: Option<GasStationPricePolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mm2: Option<u8>,
#[serde(default)]
pub tx_history: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub required_confirmations: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub requires_notarization: Option<bool>,
}

/// Using tagged representation to allow adding variants with coefficients, percentage, etc in the future.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[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 }
}

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

#[derive(Debug, Deserialize, Serialize)]
pub struct Server {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol: Option<Protocol>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_cert_verification: Option<bool>,
shamardy marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum Protocol {
Tcp,
Ssl,
}

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