Skip to content

Commit

Permalink
feat(adex-cli): activation request types (#1912)
Browse files Browse the repository at this point in the history
Activation types in cli have been introduced by this commit to ensure that if a malicious person substituted them in the activation scheme file it would not lead to any unexpected action.
  • Loading branch information
rozhkovdmitrii authored Jul 26, 2023
1 parent d4d5add commit 80f7e6f
Show file tree
Hide file tree
Showing 18 changed files with 430 additions and 325 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,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
88 changes: 88 additions & 0 deletions mm2src/adex_cli/src/rpc_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! 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::ser::SerializeSeq;
use serde::{Deserialize, Serialize, Serializer};

#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "method", rename_all = "lowercase")]
pub(crate) enum ActivationRequest {
Enable(EnableRequest),
Electrum(ElectrumRequest),
}

#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct EnableRequest {
coin: String,
#[serde(default, serialize_with = "serialize_urls", skip_serializing_if = "Vec::is_empty")]
urls: Vec<EnableUrl>,
#[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>,
}

fn serialize_urls<S>(urls: &Vec<EnableUrl>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s_seq = s.serialize_seq(None)?;
for url in urls {
s_seq.serialize_element(url.url.as_str())?;
}
s_seq.end()
}

#[derive(Debug, Deserialize)]
struct EnableUrl {
url: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct ElectrumRequest {
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 {
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, get_activation_scheme_p
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 @@ -146,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_ne!(kmd_scheme.get("servers").unwrap().as_array().unwrap().iter().count(), 0);
let Ok(ActivationRequest::Electrum(electrum)) = kmd_scheme else {
panic!("Failed to get electrum scheme")
};
assert_ne!(electrum.servers.len(), 0);
}

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

0 comments on commit 80f7e6f

Please sign in to comment.