Skip to content

Commit

Permalink
No login mode #1318 (#1341)
Browse files Browse the repository at this point in the history
* Make mm2 work without a passphrase

* Fix no-login tests

* Add is_my_order function
  • Loading branch information
caglaryucekaya authored Jul 18, 2022
1 parent 68bbddc commit 3cce5af
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 26 deletions.
4 changes: 4 additions & 0 deletions mm2src/mm2_core/src/mm_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ impl MmCtx {
}
}

/// Get a reference to the secp256k1 key pair as option.
/// Can be used in no-login functions to check if the passphrase is set
pub fn secp256k1_key_pair_as_option(&self) -> Option<&KeyPair> { self.secp256k1_key_pair.as_option() }

/// This is our public ID, allowing us to be different from other peers.
/// This should also be our public key which we'd use for message verification.
pub fn public_id(&self) -> Result<bits256, String> {
Expand Down
26 changes: 14 additions & 12 deletions mm2src/mm2_main/src/lp_native_dex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@ fn migrate_db(ctx: &MmArc) -> MmInitResult<()> {
fn migration_1(_ctx: &MmArc) {}

pub async fn lp_init_continue(ctx: MmArc) -> MmInitResult<()> {
init_ordermatch_context(&ctx)?;
init_p2p(ctx.clone()).await?;

if ctx.secp256k1_key_pair_as_option().is_none() {
return Ok(());
}

#[cfg(not(target_arch = "wasm32"))]
{
fix_directories(&ctx)?;
Expand All @@ -360,9 +367,7 @@ pub async fn lp_init_continue(ctx: MmArc) -> MmInitResult<()> {
migrate_db(&ctx)?;
}

init_ordermatch_context(&ctx)?;
init_message_service(&ctx).await?;
init_p2p(ctx.clone()).await?;

let balance_update_ordermatch_handler = BalanceUpdateOrdermatchHandler::new(ctx.clone());
register_balance_update_handler(ctx.clone(), Box::new(balance_update_ordermatch_handler)).await;
Expand All @@ -386,18 +391,15 @@ pub async fn lp_init_continue(ctx: MmArc) -> MmInitResult<()> {
pub async fn lp_init(ctx: MmArc) -> MmInitResult<()> {
info!("Version: {} DT {}", MM_VERSION, MM_DATETIME);

if ctx.conf["passphrase"].is_null() && ctx.conf["hw_wallet"].is_null() {
return MmError::err(MmInitError::FieldNotFoundInConfig {
field: "passphrase".to_owned(),
});
if !ctx.conf["passphrase"].is_null() {
let passphrase: String =
json::from_value(ctx.conf["passphrase"].clone()).map_to_mm(|e| MmInitError::ErrorDeserializingConfig {
field: "passphrase".to_owned(),
error: e.to_string(),
})?;
CryptoCtx::init_with_iguana_passphrase(ctx.clone(), &passphrase)?;
}

let passphrase: String =
json::from_value(ctx.conf["passphrase"].clone()).map_to_mm(|e| MmInitError::ErrorDeserializingConfig {
field: "passphrase".to_owned(),
error: e.to_string(),
})?;
CryptoCtx::init_with_iguana_passphrase(ctx.clone(), &passphrase)?;
lp_init_continue(ctx.clone()).await?;

let ctx_id = ctx.ffi_handle().map_to_mm(MmInitError::Internal)?;
Expand Down
8 changes: 5 additions & 3 deletions mm2src/mm2_main/src/lp_ordermatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ async fn request_and_fill_orderbook(ctx: &MmArc, base: &str, rel: &str) -> Resul
let ordermatch_ctx = OrdermatchContext::from_ctx(ctx).unwrap();
let mut orderbook = ordermatch_ctx.orderbook.lock();

let my_pubkey = ctx.secp256k1_key_pair().public();
let keypair = ctx.secp256k1_key_pair_as_option();
let alb_pair = alb_ordered_pair(base, rel);
for (pubkey, GetOrderbookPubkeyItem { orders, .. }) in pubkey_orders {
let pubkey_bytes = match hex::decode(&pubkey) {
Expand All @@ -351,8 +351,10 @@ async fn request_and_fill_orderbook(ctx: &MmArc, base: &str, rel: &str) -> Resul
continue;
},
};
if pubkey_bytes.as_slice() == my_pubkey.as_ref() {
continue;
if let Some(keypair) = keypair {
if pubkey_bytes.as_slice() == keypair.public().as_ref() {
continue;
}
}

if is_pubkey_banned(ctx, &pubkey_bytes[1..].into()) {
Expand Down
26 changes: 18 additions & 8 deletions mm2src/mm2_main/src/lp_ordermatch/orderbook_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ fn build_aggregated_entries_v2(
(aggregated, total_base.into(), total_rel.into())
}

pub fn is_my_order(my_pub: &Option<String>, order_pubkey: &str) -> bool {
my_pub.as_ref().map(|my| my == order_pubkey).unwrap_or(false)
}

pub async fn orderbook_rpc(ctx: MmArc, req: Json) -> Result<Response<Vec<u8>>, String> {
let req: OrderbookReq = try_s!(json::from_value(req));
if req.base == req.rel {
Expand Down Expand Up @@ -136,7 +140,11 @@ pub async fn orderbook_rpc(ctx: MmArc, req: Json) -> Result<Response<Vec<u8>>, S

try_s!(subscribe_to_orderbook_topic(&ctx, &base_ticker, &rel_ticker, request_orderbook).await);
let orderbook = ordermatch_ctx.orderbook.lock();
let my_pubsecp = try_s!(CryptoCtx::from_ctx(&ctx)).secp256k1_pubkey_hex();
let my_pubsecp = ctx.secp256k1_key_pair_as_option().map(|_| {
CryptoCtx::from_ctx(&ctx)
.expect("ctx is available")
.secp256k1_pubkey_hex()
});

let mut asks = match orderbook.unordered.get(&(base_ticker.clone(), rel_ticker.clone())) {
Some(uuids) => {
Expand All @@ -154,7 +162,7 @@ pub async fn orderbook_rpc(ctx: MmArc, req: Json) -> Result<Response<Vec<u8>>, S
&ask.pubkey,
address_format,
));
let is_mine = my_pubsecp == ask.pubkey;
let is_mine = is_my_order(&my_pubsecp, &ask.pubkey);
orderbook_entries.push(ask.as_rpc_entry_ask(address, is_mine));
}
orderbook_entries
Expand All @@ -181,7 +189,7 @@ pub async fn orderbook_rpc(ctx: MmArc, req: Json) -> Result<Response<Vec<u8>>, S
&bid.pubkey,
address_format,
));
let is_mine = my_pubsecp == bid.pubkey;
let is_mine = is_my_order(&my_pubsecp, &bid.pubkey);
orderbook_entries.push(bid.as_rpc_entry_bid(address, is_mine));
}
orderbook_entries
Expand Down Expand Up @@ -298,9 +306,11 @@ pub async fn orderbook_rpc_v2(
.map_to_mm(OrderbookRpcError::P2PSubscribeError)?;

let orderbook = ordermatch_ctx.orderbook.lock();
let my_pubsecp = CryptoCtx::from_ctx(&ctx)
.expect("ctx is available")
.secp256k1_pubkey_hex();
let my_pubsecp = ctx.secp256k1_key_pair_as_option().map(|_| {
CryptoCtx::from_ctx(&ctx)
.expect("ctx is available")
.secp256k1_pubkey_hex()
});

let mut asks = match orderbook.unordered.get(&(base_ticker.clone(), rel_ticker.clone())) {
Some(uuids) => {
Expand All @@ -321,7 +331,7 @@ pub async fn orderbook_rpc_v2(
continue;
},
};
let is_mine = my_pubsecp == ask.pubkey;
let is_mine = is_my_order(&my_pubsecp, &ask.pubkey);
orderbook_entries.push(ask.as_rpc_v2_entry_ask(address, is_mine));
}
orderbook_entries
Expand Down Expand Up @@ -351,7 +361,7 @@ pub async fn orderbook_rpc_v2(
continue;
},
};
let is_mine = my_pubsecp == bid.pubkey;
let is_mine = is_my_order(&my_pubsecp, &bid.pubkey);
orderbook_entries.push(bid.as_rpc_v2_entry_bid(address, is_mine));
}
orderbook_entries
Expand Down
138 changes: 135 additions & 3 deletions mm2src/mm2_main/src/mm2_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use http::{HeaderMap, StatusCode};
use mm2_number::{BigDecimal, BigRational, Fraction, MmNumber};
use mm2_test_helpers::for_tests::{check_my_swap_status, check_recent_swaps, check_stats_swap_status,
enable_native as enable_native_impl, enable_qrc20, find_metrics_in_json,
from_env_file, init_z_coin_light, init_z_coin_status, mm_spat, sign_message,
verify_message, wait_till_history_has_records, LocalStart, MarketMakerIt, RaiiDump,
MAKER_ERROR_EVENTS, MAKER_SUCCESS_EVENTS, TAKER_ERROR_EVENTS, TAKER_SUCCESS_EVENTS};
from_env_file, init_z_coin_light, init_z_coin_status, mm_spat, morty_conf,
rick_conf, sign_message, verify_message, wait_till_history_has_records, LocalStart,
MarketMakerIt, Mm2TestConf, RaiiDump, MAKER_ERROR_EVENTS, MAKER_SUCCESS_EVENTS,
MORTY, RICK, TAKER_ERROR_EVENTS, TAKER_SUCCESS_EVENTS};
use serde_json::{self as json, Value as Json};
use std::collections::HashMap;
use std::convert::{identity, TryFrom};
Expand Down Expand Up @@ -7364,3 +7365,134 @@ fn test_sign_verify_message_eth() {

assert!(response.is_valid);
}

#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_no_login() {
let coins = json!([rick_conf(), morty_conf()]);
let seednode_passphrase = get_passphrase(&".env.seed", "BOB_PASSPHRASE").unwrap();
let seednode_conf = Mm2TestConf::seednode(&seednode_passphrase, &coins);
let seednode = MarketMakerIt::start(seednode_conf.conf, seednode_conf.rpc_password, seednode_conf.local).unwrap();
let (_dump_log, _dump_dashboard) = seednode.mm_dump();
log!("log path: {}", seednode.log_path.display());

let no_login_conf = Mm2TestConf::no_login_node(&coins, &[&seednode.ip.to_string()]);
let no_login_node =
MarketMakerIt::start(no_login_conf.conf, no_login_conf.rpc_password, no_login_conf.local).unwrap();
let (_dump_log, _dump_dashboard) = no_login_node.mm_dump();
log!("log path: {}", no_login_node.log_path.display());

block_on(enable_electrum_json(&seednode, RICK, false, rick_electrums()));
block_on(enable_electrum_json(&seednode, MORTY, false, morty_electrums()));

let orders = [
// (base, rel, price, volume, min_volume)
("RICK", "MORTY", "0.9", "0.9", None),
("RICK", "MORTY", "0.8", "0.9", None),
("RICK", "MORTY", "0.7", "0.9", Some("0.9")),
("MORTY", "RICK", "0.8", "0.9", None),
("MORTY", "RICK", "0.9", "0.9", None),
];

for (base, rel, price, volume, min_volume) in orders.iter() {
let rc = block_on(seednode.rpc(&json! ({
"userpass": seednode.userpass,
"method": "setprice",
"base": base,
"rel": rel,
"price": price,
"volume": volume,
"min_volume": min_volume.unwrap_or("0.00777"),
"cancel_previous": false,
})))
.unwrap();
assert!(rc.0.is_success(), "!setprice: {}", rc.1);
}

let orderbook = block_on(no_login_node.rpc(&json! ({
"userpass": no_login_node.userpass,
"method": "orderbook",
"base": "RICK",
"rel": "MORTY",
})))
.unwrap();
assert!(orderbook.0.is_success(), "!orderbook: {}", orderbook.1);
let orderbook: OrderbookResponse = json::from_str(&orderbook.1).unwrap();
assert_eq!(orderbook.asks.len(), 3);
assert_eq!(orderbook.bids.len(), 2);

let orderbook_v2 = block_on(no_login_node.rpc(&json! ({
"userpass": no_login_node.userpass,
"mmrpc": "2.0",
"method": "orderbook",
"params": {
"base": "RICK",
"rel": "MORTY",
},
})))
.unwrap();
assert!(orderbook_v2.0.is_success(), "!orderbook: {}", orderbook_v2.1);
let orderbook_v2: RpcV2Response<OrderbookV2Response> = json::from_str(&orderbook_v2.1).unwrap();
let orderbook_v2 = orderbook_v2.result;
assert_eq!(orderbook_v2.asks.len(), 3);
assert_eq!(orderbook_v2.bids.len(), 2);

let best_orders = block_on(no_login_node.rpc(&json! ({
"userpass": no_login_node.userpass,
"method": "best_orders",
"coin": "RICK",
"action": "buy",
"volume": "0.1",
})))
.unwrap();
assert!(best_orders.0.is_success(), "!best_orders: {}", best_orders.1);
let best_orders: BestOrdersResponse = json::from_str(&best_orders.1).unwrap();
let best_morty_orders = best_orders.result.get("MORTY").unwrap();
assert_eq!(1, best_morty_orders.len());
let expected_price: BigDecimal = "0.8".parse().unwrap();
assert_eq!(expected_price, best_morty_orders[0].price);

let best_orders_v2 = block_on(no_login_node.rpc(&json! ({
"userpass": no_login_node.userpass,
"mmrpc": "2.0",
"method": "best_orders",
"params": {
"coin": "RICK",
"action": "buy",
"request_by": {
"type": "number",
"value": 1
}
},
})))
.unwrap();
assert!(best_orders_v2.0.is_success(), "!best_orders: {}", best_orders_v2.1);
let best_orders_v2: RpcV2Response<BestOrdersV2Response> = json::from_str(&best_orders_v2.1).unwrap();
let best_morty_orders = best_orders_v2.result.orders.get(MORTY).unwrap();
assert_eq!(1, best_morty_orders.len());
let expected_price: BigDecimal = "0.7".parse().unwrap();
assert_eq!(expected_price, best_morty_orders[0].price.decimal);

let orderbook_depth = block_on(no_login_node.rpc(&json! ({
"userpass": no_login_node.userpass,
"method": "orderbook_depth",
"pairs":[["RICK","MORTY"]]
})))
.unwrap();
assert!(
orderbook_depth.0.is_success(),
"!orderbook_depth: {}",
orderbook_depth.1
);
let orderbook_depth: OrderbookDepthResponse = json::from_str(&orderbook_depth.1).unwrap();
let orderbook_depth = orderbook_depth.result;
assert_eq!(orderbook_depth[0].depth.asks, 3);
assert_eq!(orderbook_depth[0].depth.bids, 2);

let version = block_on(no_login_node.rpc(&json! ({
"userpass": no_login_node.userpass,
"method": "version",
})))
.unwrap();
assert!(version.0.is_success(), "!version: {}", version.1);
}
28 changes: 28 additions & 0 deletions mm2src/mm2_test_helpers/src/for_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub const TAKER_ERROR_EVENTS: [&str; 13] = [
];

pub const RICK: &str = "RICK";
pub const MORTY: &str = "MORTY";
pub const ZOMBIE_TICKER: &str = "ZOMBIE";
pub const ZOMBIE_ELECTRUMS: &[&str] = &["zombie.sirseven.me:10033"];
pub const ZOMBIE_LIGHTWALLETD_URLS: &[&str] = &["http://zombie.sirseven.me:443"];
Expand Down Expand Up @@ -142,6 +143,20 @@ impl Mm2TestConf {
local: None,
}
}

pub fn no_login_node(coins: &Json, seednodes: &[&str]) -> Self {
Mm2TestConf {
conf: json!({
"gui": "nogui",
"netid": 9998,
"coins": coins,
"rpc_password": DEFAULT_RPC_PASSWORD,
"seednodes": seednodes,
}),
rpc_password: DEFAULT_RPC_PASSWORD.into(),
local: None,
}
}
}

pub fn zombie_conf() -> Json {
Expand Down Expand Up @@ -186,6 +201,19 @@ pub fn rick_conf() -> Json {
})
}

pub fn morty_conf() -> Json {
json!({
"coin":"MORTY",
"asset":"MORTY",
"required_confirmations":0,
"txversion":4,
"overwintered":1,
"protocol":{
"type":"UTXO"
}
})
}

#[cfg(target_arch = "wasm32")]
pub fn mm_ctx_with_custom_db() -> MmArc { MmCtxBuilder::new().with_test_db_namespace().into_mm_arc() }

Expand Down

0 comments on commit 3cce5af

Please sign in to comment.