diff --git a/mm2src/mm2_main/src/database.rs b/mm2src/mm2_main/src/database.rs index 8f278d3adc..6945204643 100644 --- a/mm2src/mm2_main/src/database.rs +++ b/mm2src/mm2_main/src/database.rs @@ -119,6 +119,13 @@ fn migration_12() -> Vec<(&'static str, Vec)> { ] } +fn migration_13() -> Vec<(&'static str, Vec)> { + vec![ + (my_swaps::ADD_SWAP_VERSION_FIELD, vec![]), // Step 1: Add new column + (my_swaps::SET_LEGACY_SWAP_VERSION, vec![]), // Step 2: Update old rows + ] +} + async fn statements_for_migration(ctx: &MmArc, current_migration: i64) -> Option)>> { match current_migration { 1 => Some(migration_1(ctx).await), @@ -133,6 +140,7 @@ async fn statements_for_migration(ctx: &MmArc, current_migration: i64) -> Option 10 => Some(migration_10(ctx).await), 11 => Some(migration_11()), 12 => Some(migration_12()), + 13 => Some(migration_13()), _ => None, } } diff --git a/mm2src/mm2_main/src/database/my_swaps.rs b/mm2src/mm2_main/src/database/my_swaps.rs index 2fe1a85890..04b2ac11b5 100644 --- a/mm2src/mm2_main/src/database/my_swaps.rs +++ b/mm2src/mm2_main/src/database/my_swaps.rs @@ -52,8 +52,12 @@ pub const TRADING_PROTO_UPGRADE_MIGRATION: &[&str] = &[ "ALTER TABLE my_swaps ADD COLUMN taker_coin_nota BOOLEAN;", ]; +/// Adds Swap Protocol version column to `my_swaps` table +pub const ADD_SWAP_VERSION_FIELD: &str = "ALTER TABLE my_swaps ADD COLUMN swap_version INTEGER;"; +/// Sets default value for `swap_version` to `1` for existing rows +pub const SET_LEGACY_SWAP_VERSION: &str = "UPDATE my_swaps SET swap_version = 1 WHERE swap_version IS NULL;"; pub const ADD_OTHER_P2P_PUBKEY_FIELD: &str = "ALTER TABLE my_swaps ADD COLUMN other_p2p_pub BLOB;"; -// Storing rational numbers as text to maintain precision +/// Storing rational numbers as text to maintain precision pub const ADD_DEX_FEE_BURN_FIELD: &str = "ALTER TABLE my_swaps ADD COLUMN dex_fee_burn TEXT;"; /// The query to insert swap on migration 1, during this migration swap_type column doesn't exist @@ -97,7 +101,8 @@ const INSERT_MY_SWAP_V2: &str = r#"INSERT INTO my_swaps ( maker_coin_nota, taker_coin_confs, taker_coin_nota, - other_p2p_pub + other_p2p_pub, + swap_version ) VALUES ( :my_coin, :other_coin, @@ -118,7 +123,8 @@ const INSERT_MY_SWAP_V2: &str = r#"INSERT INTO my_swaps ( :maker_coin_nota, :taker_coin_confs, :taker_coin_nota, - :other_p2p_pub + :other_p2p_pub, + :swap_version );"#; pub fn insert_new_swap_v2(ctx: &MmArc, params: &[(&str, &dyn ToSql)]) -> SqlResult<()> { @@ -311,7 +317,8 @@ pub const SELECT_MY_SWAP_V2_FOR_RPC_BY_UUID: &str = r#"SELECT maker_coin_confs, maker_coin_nota, taker_coin_confs, - taker_coin_nota + taker_coin_nota, + swap_version FROM my_swaps WHERE uuid = :uuid; "#; @@ -337,7 +344,8 @@ pub const SELECT_MY_SWAP_V2_BY_UUID: &str = r#"SELECT taker_coin_confs, taker_coin_nota, p2p_privkey, - other_p2p_pub + other_p2p_pub, + swap_version FROM my_swaps WHERE uuid = :uuid; "#; diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index ab00fb30da..3a61efb868 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -84,6 +84,7 @@ use crate::lp_swap::{calc_max_maker_vol, check_balance_for_maker_swap, check_bal run_taker_swap, swap_v2_topic, AtomicLocktimeVersion, CheckBalanceError, CheckBalanceResult, CoinVolumeInfo, MakerSwap, RunMakerSwapInput, RunTakerSwapInput, SwapConfirmationsSettings, TakerSwap, LEGACY_SWAP_TYPE}; +use crate::swap_versioning::{legacy_swap_version, SwapVersion}; #[cfg(any(test, feature = "run-docker-tests"))] use crate::lp_swap::taker_swap::FailAt; @@ -139,6 +140,8 @@ const TRIE_STATE_HISTORY_TIMEOUT: u64 = 3; const TRIE_ORDER_HISTORY_TIMEOUT: u64 = 300; #[cfg(test)] const TRIE_ORDER_HISTORY_TIMEOUT: u64 = 3; +/// Current swap protocol version +const SWAP_VERSION_DEFAULT: u8 = 2; pub type OrderbookP2PHandlerResult = Result<(), MmError>; @@ -1178,12 +1181,12 @@ pub struct TakerRequest { #[serde(default)] match_by: MatchBy, conf_settings: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub base_protocol_info: Option>, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default, skip_serializing_if = "SwapVersion::is_legacy")] + pub swap_version: SwapVersion, } impl TakerRequest { @@ -1204,6 +1207,7 @@ impl TakerRequest { conf_settings: Some(message.conf_settings), base_protocol_info: message.base_protocol_info, rel_protocol_info: message.rel_protocol_info, + swap_version: message.swap_version, } } @@ -1249,6 +1253,7 @@ impl From for new_protocol::OrdermatchMessage { conf_settings: taker_order.request.conf_settings.unwrap(), base_protocol_info: taker_order.request.base_protocol_info, rel_protocol_info: taker_order.request.rel_protocol_info, + swap_version: taker_order.request.swap_version, }) } } @@ -1274,6 +1279,7 @@ pub struct TakerOrderBuilder<'a> { min_volume: Option, timeout: u64, save_in_history: bool, + swap_version: u8, } pub enum TakerOrderBuildError { @@ -1353,6 +1359,7 @@ impl<'a> TakerOrderBuilder<'a> { order_type: OrderType::GoodTillCancelled, timeout: TAKER_ORDER_TIMEOUT, save_in_history: true, + swap_version: SWAP_VERSION_DEFAULT, } } @@ -1416,6 +1423,12 @@ impl<'a> TakerOrderBuilder<'a> { self } + /// When a new [TakerOrderBuilder::new] is created, it sets [SWAP_VERSION_DEFAULT]. + /// However, if user has not specified in the config to use TPU V2, + /// the TakerOrderBuilder's swap_version is changed to legacy. + /// In the future alls users will be using TPU V2 by default without "use_trading_proto_v2" configuration. + pub fn set_legacy_swap_v(&mut self) { self.swap_version = legacy_swap_version() } + /// Validate fields and build #[allow(clippy::result_large_err)] pub fn build(self) -> Result { @@ -1504,6 +1517,7 @@ impl<'a> TakerOrderBuilder<'a> { conf_settings: self.conf_settings, base_protocol_info: Some(base_protocol_info), rel_protocol_info: Some(rel_protocol_info), + swap_version: SwapVersion::from(self.swap_version), }, matches: Default::default(), min_volume, @@ -1544,6 +1558,7 @@ impl<'a> TakerOrderBuilder<'a> { conf_settings: self.conf_settings, base_protocol_info: Some(base_protocol_info), rel_protocol_info: Some(rel_protocol_info), + swap_version: SwapVersion::from(self.swap_version), }, matches: HashMap::new(), min_volume: Default::default(), @@ -1705,6 +1720,8 @@ pub struct MakerOrder { /// A custom priv key for more privacy to prevent linking orders of the same node between each other /// Commonly used with privacy coins (ARRR, ZCash, etc.) p2p_privkey: Option, + #[serde(default, skip_serializing_if = "SwapVersion::is_legacy")] + pub swap_version: SwapVersion, } pub struct MakerOrderBuilder<'a> { @@ -1717,6 +1734,7 @@ pub struct MakerOrderBuilder<'a> { rel_orderbook_ticker: Option, conf_settings: Option, save_in_history: bool, + swap_version: u8, } pub enum MakerOrderBuildError { @@ -1866,6 +1884,7 @@ impl<'a> MakerOrderBuilder<'a> { price: 0.into(), conf_settings: None, save_in_history: true, + swap_version: SWAP_VERSION_DEFAULT, } } @@ -1904,6 +1923,12 @@ impl<'a> MakerOrderBuilder<'a> { self } + /// When a new [MakerOrderBuilder::new] is created, it sets [SWAP_VERSION_DEFAULT]. + /// However, if user has not specified in the config to use TPU V2, + /// the MakerOrderBuilder's swap_version is changed to legacy. + /// In the future alls users will be using TPU V2 by default without "use_trading_proto_v2" configuration. + pub fn set_legacy_swap_v(&mut self) { self.swap_version = legacy_swap_version() } + /// Build MakerOrder #[allow(clippy::result_large_err)] pub fn build(self) -> Result { @@ -1960,6 +1985,7 @@ impl<'a> MakerOrderBuilder<'a> { base_orderbook_ticker: self.base_orderbook_ticker, rel_orderbook_ticker: self.rel_orderbook_ticker, p2p_privkey, + swap_version: SwapVersion::from(self.swap_version), }) } @@ -1984,6 +2010,7 @@ impl<'a> MakerOrderBuilder<'a> { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::from(self.swap_version), } } } @@ -2114,6 +2141,7 @@ impl From for MakerOrder { base_orderbook_ticker: taker_order.base_orderbook_ticker, rel_orderbook_ticker: taker_order.rel_orderbook_ticker, p2p_privkey: taker_order.p2p_privkey, + swap_version: taker_order.request.swap_version, }, // The "buy" taker order is recreated with reversed pair as Maker order is always considered as "sell" TakerAction::Buy => { @@ -2136,6 +2164,7 @@ impl From for MakerOrder { base_orderbook_ticker: taker_order.rel_orderbook_ticker, rel_orderbook_ticker: taker_order.base_orderbook_ticker, p2p_privkey: taker_order.p2p_privkey, + swap_version: taker_order.request.swap_version, } }, } @@ -2182,12 +2211,12 @@ pub struct MakerReserved { sender_pubkey: H256Json, dest_pub_key: H256Json, conf_settings: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub base_protocol_info: Option>, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default, skip_serializing_if = "SwapVersion::is_legacy")] + pub swap_version: SwapVersion, } impl MakerReserved { @@ -2215,6 +2244,7 @@ impl MakerReserved { conf_settings: Some(message.conf_settings), base_protocol_info: message.base_protocol_info, rel_protocol_info: message.rel_protocol_info, + swap_version: message.swap_version, } } } @@ -2231,6 +2261,7 @@ impl From for new_protocol::OrdermatchMessage { conf_settings: maker_reserved.conf_settings.unwrap(), base_protocol_info: maker_reserved.base_protocol_info, rel_protocol_info: maker_reserved.rel_protocol_info, + swap_version: maker_reserved.swap_version, }) } } @@ -3052,8 +3083,11 @@ fn lp_connect_start_bob(ctx: MmArc, maker_match: MakerMatch, maker_order: MakerO }, }; - // TODO add alice swap protocol version check once protocol versioning is implemented - if !ctx.use_trading_proto_v2() { + let alice_swap_v = maker_match.request.swap_version; + let bob_swap_v = maker_order.swap_version; + + // Start a legacy swap if either the taker or maker uses the legacy swap protocol (version 1) + if alice_swap_v.is_legacy() || bob_swap_v.is_legacy() { let params = LegacySwapParams { maker_coin: &maker_coin, taker_coin: &taker_coin, @@ -3188,6 +3222,7 @@ async fn start_maker_swap_state_machine< lock_duration: *params.locktime, taker_p2p_pubkey: *taker_p2p_pubkey, require_taker_payment_spend_confirm: true, + swap_version: maker_order.swap_version.version, }; #[allow(clippy::box_default)] maker_swap_state_machine @@ -3273,8 +3308,11 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat uuid ); - // TODO add bob swap protocol version check once protocol versioning is implemented - if !ctx.use_trading_proto_v2() { + let bob_swap_v = taker_match.reserved.swap_version; + let alice_swap_v = taker_order.request.swap_version; + + // Start a legacy swap if either the maker or taker uses the legacy swap protocol (version 1) + if bob_swap_v.is_legacy() || alice_swap_v.is_legacy() { let params = LegacySwapParams { maker_coin: &maker_coin, taker_coin: &taker_coin, @@ -3425,6 +3463,7 @@ async fn start_taker_swap_state_machine< maker_p2p_pubkey: *maker_p2p_pubkey, require_maker_payment_confirm_before_funding_spend: true, require_maker_payment_spend_confirm: true, + swap_version: taker_order.request.swap_version.version, }; #[allow(clippy::box_default)] taker_swap_state_machine @@ -3945,6 +3984,7 @@ async fn process_taker_request(ctx: MmArc, from_pubkey: H256Json, taker_request: }), base_protocol_info: Some(base_coin.coin_protocol_info(None)), rel_protocol_info: Some(rel_coin.coin_protocol_info(Some(rel_amount.clone()))), + swap_version: order.swap_version, }; let topic = order.orderbook_topic(); log::debug!("Request matched sending reserved {:?}", reserved); @@ -4192,6 +4232,10 @@ pub async fn lp_auto_buy( .with_save_in_history(input.save_in_history) .with_base_orderbook_ticker(ordermatch_ctx.orderbook_ticker(base_coin.ticker())) .with_rel_orderbook_ticker(ordermatch_ctx.orderbook_ticker(rel_coin.ticker())); + if !ctx.use_trading_proto_v2() { + order_builder.set_legacy_swap_v(); + } + if let Some(timeout) = input.timeout { order_builder = order_builder.with_timeout(timeout); } @@ -4929,7 +4973,7 @@ pub async fn create_maker_order(ctx: &MmArc, req: SetPriceReq) -> Result Result for OrdermatchMessage { /// MsgPack compact representation does not work with tagged enums (encoding works, but decoding fails) /// This is untagged representation also using compact Uuid representation #[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(test, derive(Eq, PartialEq))] pub enum MatchBy { Any, Orders(HashSet), @@ -250,6 +251,7 @@ impl MakerOrderUpdated { } #[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(test, derive(Eq, PartialEq))] pub struct TakerRequest { pub base: String, pub rel: String, @@ -259,15 +261,16 @@ pub struct TakerRequest { pub uuid: CompactUuid, pub match_by: MatchBy, pub conf_settings: OrderConfirmationsSettings, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub base_protocol_info: Option>, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default, skip_serializing_if = "SwapVersion::is_legacy")] + pub swap_version: SwapVersion, } #[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(test, derive(Eq, PartialEq))] pub struct MakerReserved { pub base: String, pub rel: String, @@ -276,12 +279,12 @@ pub struct MakerReserved { pub taker_order_uuid: CompactUuid, pub maker_order_uuid: CompactUuid, pub conf_settings: OrderConfirmationsSettings, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub base_protocol_info: Option>, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub rel_protocol_info: Option>, + #[serde(default, skip_serializing_if = "SwapVersion::is_legacy")] + pub swap_version: SwapVersion, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -422,4 +425,156 @@ mod new_protocol_tests { let new_serialized = rmp_serde::to_vec_named(&new).unwrap(); let _old_from_new: MakerOrderCreatedV1 = rmp_serde::from_slice(&new_serialized).unwrap(); } + + #[test] + fn test_old_new_taker_request_rmp() { + // Old TakerRequest didn't have swap_version field + #[derive(Debug, Eq, Serialize, Deserialize, PartialEq)] + struct OldTakerRequest { + base: String, + rel: String, + base_amount: BigRational, + rel_amount: BigRational, + action: TakerAction, + uuid: CompactUuid, + match_by: MatchBy, + conf_settings: OrderConfirmationsSettings, + #[serde(default, skip_serializing_if = "Option::is_none")] + base_protocol_info: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + rel_protocol_info: Option>, + } + + let old_instance = OldTakerRequest { + base: "BTC".to_string(), + rel: "ETH".to_string(), + base_amount: BigRational::from_integer(1.into()), + rel_amount: BigRational::from_integer(50.into()), + action: TakerAction::Buy, + uuid: CompactUuid::from(Uuid::new_v4()), + match_by: MatchBy::Any, + conf_settings: OrderConfirmationsSettings::default(), + base_protocol_info: Some(vec![1u8; 10]), + rel_protocol_info: Some(vec![2u8; 10]), + }; + + // ------------------------------------------ + // Step 1: Test Deserialization from Old Format + // ------------------------------------------ + let old_serialized = rmp_serde::to_vec_named(&old_instance).expect("Old MessagePack serialization failed"); + let new_instance: TakerRequest = + rmp_serde::from_slice(&old_serialized).expect("Deserialization into new TakerRequest failed"); + + assert_eq!(new_instance.base, old_instance.base); + assert_eq!(new_instance.rel, old_instance.rel); + assert_eq!(new_instance.base_amount, old_instance.base_amount); + assert_eq!(new_instance.rel_amount, old_instance.rel_amount); + assert_eq!(new_instance.action, old_instance.action); + assert_eq!(new_instance.uuid, old_instance.uuid); + assert_eq!(new_instance.match_by, old_instance.match_by); + assert_eq!(new_instance.conf_settings, old_instance.conf_settings); + assert_eq!(new_instance.base_protocol_info, old_instance.base_protocol_info); + assert_eq!(new_instance.rel_protocol_info, old_instance.rel_protocol_info); + assert_eq!(new_instance.swap_version, SwapVersion::default()); // Default swap_version + + // ------------------------------------------ + // Step 2: Test Serialization from New Format to Old Format + // ------------------------------------------ + let new_serialized = rmp_serde::to_vec_named(&new_instance).expect("Serialization of new type failed"); + let old_from_new: OldTakerRequest = + rmp_serde::from_slice(&new_serialized).expect("Old deserialization from new serialization failed"); + + assert_eq!(old_from_new.base, new_instance.base); + assert_eq!(old_from_new.rel, new_instance.rel); + assert_eq!(old_from_new.base_amount, new_instance.base_amount); + assert_eq!(old_from_new.rel_amount, new_instance.rel_amount); + assert_eq!(old_from_new.action, new_instance.action); + assert_eq!(old_from_new.uuid, new_instance.uuid); + assert_eq!(old_from_new.match_by, new_instance.match_by); + assert_eq!(old_from_new.conf_settings, new_instance.conf_settings); + assert_eq!(old_from_new.base_protocol_info, new_instance.base_protocol_info); + assert_eq!(old_from_new.rel_protocol_info, new_instance.rel_protocol_info); + + // ------------------------------------------ + // Step 3: Round-Trip Test of the New Format + // ------------------------------------------ + let rt_serialized = rmp_serde::to_vec_named(&new_instance).expect("Round-trip serialization failed"); + let round_trip: TakerRequest = + rmp_serde::from_slice(&rt_serialized).expect("Round-trip deserialization failed"); + assert_eq!(round_trip, new_instance); + } + + #[test] + fn test_old_new_maker_reserved_rmp() { + // Old MakerReserved didnt have swap_version field + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] + struct OldMakerReserved { + base: String, + rel: String, + base_amount: BigRational, + rel_amount: BigRational, + taker_order_uuid: CompactUuid, + maker_order_uuid: CompactUuid, + conf_settings: OrderConfirmationsSettings, + #[serde(default, skip_serializing_if = "Option::is_none")] + base_protocol_info: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + rel_protocol_info: Option>, + } + + let old_instance = OldMakerReserved { + base: "BTC".to_string(), + rel: "ETH".to_string(), + base_amount: BigRational::from_integer(1.into()), + rel_amount: BigRational::from_integer(50.into()), + taker_order_uuid: CompactUuid::from(Uuid::new_v4()), + maker_order_uuid: CompactUuid::from(Uuid::new_v4()), + conf_settings: OrderConfirmationsSettings::default(), + base_protocol_info: Some(vec![1u8; 10]), + rel_protocol_info: Some(vec![2u8; 10]), + }; + + // ------------------------------------------ + // Step 1: Test Deserialization from Old Format + // ------------------------------------------ + let old_serialized = rmp_serde::to_vec_named(&old_instance).expect("Old MessagePack serialization failed"); + let new_instance: MakerReserved = + rmp_serde::from_slice(&old_serialized).expect("Deserialization into new MakerReserved failed"); + + assert_eq!(new_instance.base, old_instance.base); + assert_eq!(new_instance.rel, old_instance.rel); + assert_eq!(new_instance.base_amount, old_instance.base_amount); + assert_eq!(new_instance.rel_amount, old_instance.rel_amount); + assert_eq!(new_instance.taker_order_uuid, old_instance.taker_order_uuid); + assert_eq!(new_instance.maker_order_uuid, old_instance.maker_order_uuid); + assert_eq!(new_instance.conf_settings, old_instance.conf_settings); + assert_eq!(new_instance.base_protocol_info, old_instance.base_protocol_info); + assert_eq!(new_instance.rel_protocol_info, old_instance.rel_protocol_info); + assert_eq!(new_instance.swap_version, SwapVersion::default()); // Default swap_version + + // ------------------------------------------ + // Step 2: Test Serialization from New Format to Old Format + // ------------------------------------------ + let new_serialized = rmp_serde::to_vec_named(&new_instance).expect("Serialization of new type failed"); + let old_from_new: OldMakerReserved = + rmp_serde::from_slice(&new_serialized).expect("Old deserialization from new serialization failed"); + + assert_eq!(old_from_new.base, new_instance.base); + assert_eq!(old_from_new.rel, new_instance.rel); + assert_eq!(old_from_new.base_amount, new_instance.base_amount); + assert_eq!(old_from_new.rel_amount, new_instance.rel_amount); + assert_eq!(old_from_new.taker_order_uuid, new_instance.taker_order_uuid); + assert_eq!(old_from_new.maker_order_uuid, new_instance.maker_order_uuid); + assert_eq!(old_from_new.conf_settings, new_instance.conf_settings); + assert_eq!(old_from_new.base_protocol_info, new_instance.base_protocol_info); + assert_eq!(old_from_new.rel_protocol_info, new_instance.rel_protocol_info); + + // ------------------------------------------ + // Step 3: Round-Trip Test of the New Format + // ------------------------------------------ + let rt_serialized = rmp_serde::to_vec_named(&new_instance).expect("Round-trip serialization failed"); + let round_trip: MakerReserved = + rmp_serde::from_slice(&rt_serialized).expect("Round-trip deserialization failed"); + assert_eq!(round_trip, new_instance); + } } diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs index f4a56b20f3..9af79312b7 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs @@ -43,6 +43,7 @@ cfg_native!( cfg_wasm32!( use crate::lp_swap::swap_wasm_db::{MySwapsFiltersTable, SavedSwapTable}; + use crate::swap_versioning::legacy_swap_version; ); // This is needed to have Debug on messages @@ -174,6 +175,7 @@ impl StateMachineStorage for MakerSwapStorage { ":taker_coin_confs": repr.conf_settings.taker_coin_confs, ":taker_coin_nota": repr.conf_settings.taker_coin_nota, ":other_p2p_pub": repr.taker_p2p_pub.to_bytes(), + ":swap_version": repr.swap_version, }; insert_new_swap_v2(&ctx, sql_params)?; Ok(()) @@ -281,6 +283,9 @@ pub struct MakerSwapDbRepr { pub events: Vec, /// Taker's P2P pubkey pub taker_p2p_pub: Secp256k1PubkeySerialize, + /// Swap protocol version + #[cfg_attr(target_arch = "wasm32", serde(default = "legacy_swap_version"))] + pub swap_version: u8, } impl StateMachineDbRepr for MakerSwapDbRepr { @@ -347,6 +352,7 @@ impl MakerSwapDbRepr { .map_err(|e| SqlError::FromSqlConversionFailure(19, SqlType::Blob, Box::new(e))) })? .into(), + swap_version: row.get(20)?, }) } } @@ -393,6 +399,8 @@ pub struct MakerSwapStateMachine @@ -449,6 +457,7 @@ impl { maker_coin_nota: bool, taker_coin_confs: i64, taker_coin_nota: bool, + swap_version: u8, } impl MySwapForRpc { @@ -141,6 +142,7 @@ impl MySwapForRpc { maker_coin_nota: row.get(12)?, taker_coin_confs: row.get(13)?, taker_coin_nota: row.get(14)?, + swap_version: row.get(15)?, }) } } @@ -218,6 +220,7 @@ pub(super) async fn get_maker_swap_data_for_rpc( maker_coin_nota: json_repr.conf_settings.maker_coin_nota, taker_coin_confs: json_repr.conf_settings.taker_coin_confs as i64, taker_coin_nota: json_repr.conf_settings.taker_coin_nota, + swap_version: json_repr.swap_version, })) } @@ -258,6 +261,7 @@ pub(super) async fn get_taker_swap_data_for_rpc( maker_coin_nota: json_repr.conf_settings.maker_coin_nota, taker_coin_confs: json_repr.conf_settings.taker_coin_confs as i64, taker_coin_nota: json_repr.conf_settings.taker_coin_nota, + swap_version: json_repr.swap_version, })) } diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs index 04e026dfc4..0d89363dd7 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs @@ -42,6 +42,7 @@ cfg_native!( cfg_wasm32!( use crate::lp_swap::swap_wasm_db::{MySwapsFiltersTable, SavedSwapTable}; + use crate::swap_versioning::legacy_swap_version; ); // This is needed to have Debug on messages @@ -204,6 +205,7 @@ impl StateMachineStorage for TakerSwapStorage { ":taker_coin_confs": repr.conf_settings.taker_coin_confs, ":taker_coin_nota": repr.conf_settings.taker_coin_nota, ":other_p2p_pub": repr.maker_p2p_pub.to_bytes(), + ":swap_version": repr.swap_version, }; insert_new_swap_v2(&ctx, sql_params)?; Ok(()) @@ -311,6 +313,9 @@ pub struct TakerSwapDbRepr { pub events: Vec, /// Maker's P2P pubkey pub maker_p2p_pub: Secp256k1PubkeySerialize, + /// Swap protocol version + #[cfg_attr(target_arch = "wasm32", serde(default = "legacy_swap_version"))] + pub swap_version: u8, } #[cfg(not(target_arch = "wasm32"))] @@ -365,6 +370,7 @@ impl TakerSwapDbRepr { .map_err(|e| SqlError::FromSqlConversionFailure(19, SqlType::Blob, Box::new(e))) })? .into(), + swap_version: row.get(20)?, }) } } @@ -425,6 +431,8 @@ pub struct TakerSwapStateMachine @@ -480,6 +488,7 @@ impl mpsc::Receiver { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::default(), }, None, ); @@ -996,6 +1039,7 @@ fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::default(), }, None, ); @@ -1018,6 +1062,7 @@ fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::default(), }, None, ); @@ -1037,6 +1082,7 @@ fn prepare_for_cancel_by(ctx: &MmArc) -> mpsc::Receiver { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: SwapVersion::default(), }, order_type: OrderType::GoodTillCancelled, min_volume: 0.into(), @@ -1139,6 +1185,7 @@ fn test_taker_order_match_by() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: SwapVersion::default(), }; let mut order = TakerOrder { @@ -1166,6 +1213,7 @@ fn test_taker_order_match_by() { conf_settings: None, base_protocol_info: None, rel_protocol_info: None, + swap_version: SwapVersion::default(), }; assert_eq!(MatchReservedResult::NotMatched, order.match_reserved(&reserved)); @@ -1206,6 +1254,7 @@ fn test_maker_order_was_updated() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::default(), }; let mut update_msg = MakerOrderUpdated::new(maker_order.uuid); update_msg.with_new_price(BigRational::from_integer(2.into())); @@ -3215,6 +3264,7 @@ fn test_maker_order_balance_loops() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::default(), }; let morty_order = MakerOrder { @@ -3234,6 +3284,7 @@ fn test_maker_order_balance_loops() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::default(), }; assert!(!maker_orders_ctx.balance_loop_exists(rick_ticker)); @@ -3266,6 +3317,7 @@ fn test_maker_order_balance_loops() { base_orderbook_ticker: None, rel_orderbook_ticker: None, p2p_privkey: None, + swap_version: SwapVersion::default(), }; maker_orders_ctx.add_order(ctx.weak(), rick_order_2.clone(), None); diff --git a/mm2src/mm2_main/src/swap_versioning.rs b/mm2src/mm2_main/src/swap_versioning.rs new file mode 100644 index 0000000000..5c21615219 --- /dev/null +++ b/mm2src/mm2_main/src/swap_versioning.rs @@ -0,0 +1,26 @@ +//! Swap Versioning Module +//! +//! This module provides a dedicated type for handling swap versioning + +#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct SwapVersion { + pub version: u8, +} + +impl Default for SwapVersion { + fn default() -> Self { + Self { + version: legacy_swap_version(), + } + } +} + +impl SwapVersion { + pub(crate) const fn is_legacy(&self) -> bool { self.version == legacy_swap_version() } +} + +impl From for SwapVersion { + fn from(version: u8) -> Self { Self { version } } +} + +pub(crate) const fn legacy_swap_version() -> u8 { 1 }