Skip to content

Commit

Permalink
feat(adex-cli): provide activation request types to prevent malicious…
Browse files Browse the repository at this point in the history
… intents when activation scheme is being used
  • Loading branch information
rozhkovdmitrii committed Jul 12, 2023
1 parent 01bc10e commit 1b9ba98
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 50 deletions.
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
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 {
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")]
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,
#[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>,
}

#[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,
}

0 comments on commit 1b9ba98

Please sign in to comment.