diff --git a/CHANGELOG.md b/CHANGELOG.md index 83b5e0e..5be4649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `Uuid` wrapper type against `uuid:Uuid` to identify trades and swaps, the wrapper implements strict encoding functionalities by @h4sh3d ([#297](https://github.com/farcaster-project/farcaster-core/pull/297)) +- New `DealId` and `SwapId` types wrapping generic `Uuid` by @h4sh3d ([#297](https://github.com/farcaster-project/farcaster-core/pull/297)) + ### Changed -- Module `negotiation` is renamed as the `trade` module -- `Offer` and `PublicOffer` are renamed `DealParameters` and `Deal`, these structs are used to initialized a swap during the trade setup and should be the outcome of a proper negotiation phase currently out-of-scope for this library +- Module `negotiation` is renamed as the `trade` module by @h4sh3d and @Lederstrumpf ([#296](https://github.com/farcaster-project/farcaster-core/pull/296)) +- `Offer` and `PublicOffer` are renamed `DealParameters` and `Deal`, these structs are used to initialized a swap during the trade setup and should be the outcome of a proper negotiation phase currently out-of-scope for this library by @h4sh3d and @Lederstrumpf ([#296](https://github.com/farcaster-project/farcaster-core/pull/296)) +- Deal `uuid` type is switched to a wrapper type by @h4sh3d ([#297](https://github.com/farcaster-project/farcaster-core/pull/297)) ### Removed -- `lightning_encoding` is removed for the protocol messages +- `SwapId` is removed and use the new `Uuid` wrapper type by @h4sh3d ([#297](https://github.com/farcaster-project/farcaster-core/pull/297)) +- `lightning_encoding` is removed for the protocol messages by @h4sh3d ([#298](https://github.com/farcaster-project/farcaster-core/pull/298)) ## [0.5.1] - 2022-08-15 diff --git a/src/lib.rs b/src/lib.rs index e60883a..d94c43c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,6 +72,12 @@ extern crate serde; #[macro_use] extern crate clap; +use std::io; +use std::str::FromStr; + +use crate::consensus::{Decodable, Encodable}; + +use serde::{Deserialize, Serialize}; use thiserror::Error; #[macro_use] @@ -115,3 +121,96 @@ pub enum Error { /// Result of an high level computation such as in Alice and Bob roles executing the protocol, /// wraps the crate level [`enum@Error`] type. pub type Res = Result; + +/// A unique identifier used to identify trades and swaps. +/// +/// This is a wrapper against `uuid::Uuid` with `StrictEncode` and `StrictDecode` implementation. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, Serialize, Deserialize)] +#[display(inner)] +pub struct Uuid(uuid::Uuid); + +impl Uuid { + /// Creates a new uuid for trades and swaps. + pub fn new() -> Self { + Self(uuid::Uuid::new_v4()) + } + + /// Creates a new random uuid, same as `Self::new()`. + pub fn random() -> Self { + Self::new() + } +} + +impl Default for Uuid { + fn default() -> Self { + Self::new() + } +} + +impl From for Uuid { + fn from(u: uuid::Uuid) -> Self { + Self(u) + } +} + +impl FromStr for Uuid { + type Err = uuid::Error; + + fn from_str(s: &str) -> Result { + Ok(Self(uuid::Uuid::from_str(s)?)) + } +} + +impl AsRef for Uuid { + fn as_ref(&self) -> &uuid::Uuid { + &self.0 + } +} + +impl Encodable for Uuid { + fn consensus_encode(&self, s: &mut W) -> Result { + self.0.to_bytes_le().consensus_encode(s) + } +} + +impl Decodable for Uuid { + fn consensus_decode(d: &mut D) -> Result { + Ok(Self(uuid::Uuid::from_bytes_le( + Decodable::consensus_decode(d)?, + ))) + } +} + +impl strict_encoding::StrictEncode for Uuid { + fn strict_encode(&self, mut e: E) -> Result { + self.as_ref().to_bytes_le().strict_encode(&mut e) + } +} + +impl strict_encoding::StrictDecode for Uuid { + fn strict_decode(mut d: D) -> Result { + Ok(Self(uuid::Uuid::from_bytes_le(<[u8; 16]>::strict_decode( + &mut d, + )?))) + } +} + +#[cfg(test)] +mod tests { + use super::Uuid; + use uuid::uuid; + + #[test] + fn serialize_swapid_in_yaml() { + let id: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(); + let s = serde_yaml::to_string(&id).expect("Encode swap id in yaml"); + assert_eq!("---\n67e55044-10b1-426f-9247-bb680e5fe0c8\n", s); + } + + #[test] + fn deserialize_swapid_from_yaml() { + let s = "---\n67e55044-10b1-426f-9247-bb680e5fe0c8\n"; + let id: Uuid = serde_yaml::from_str(&s).expect("Decode uuid from yaml"); + assert_eq!(id, uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(),); + } +} diff --git a/src/protocol.rs b/src/protocol.rs index 0d26f00..bb24ea1 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -64,13 +64,16 @@ pub struct CoreArbitratingTransactions { } impl CoreArbitratingTransactions { - pub fn into_arbitrating_setup( + pub fn into_arbitrating_setup( self, - swap_id: SwapId, + swap_id: U, cancel_sig: Sig, - ) -> CoreArbitratingSetup { + ) -> CoreArbitratingSetup + where + U: Into, + { CoreArbitratingSetup { - swap_id, + swap_id: swap_id.into(), lock: self.lock, cancel: self.cancel, refund: self.refund, @@ -262,13 +265,13 @@ where Sk: CanonicalBytes, { /// Generates protocol message that commits to Alice's parameters. - pub fn commit_alice( + pub fn commit_alice>( &self, - swap_id: SwapId, + swap_id: U, wallet: &impl Commit, ) -> CommitAliceParameters { CommitAliceParameters { - swap_id, + swap_id: swap_id.into(), buy: wallet.commit_to(self.buy.as_canonical_bytes()), cancel: wallet.commit_to(self.cancel.as_canonical_bytes()), refund: wallet.commit_to(self.refund.as_canonical_bytes()), @@ -288,9 +291,12 @@ where } /// Create the reveal protocol message based on the set of parameters. - pub fn reveal_alice(self, swap_id: SwapId) -> RevealAliceParameters { + pub fn reveal_alice>( + self, + swap_id: U, + ) -> RevealAliceParameters { RevealAliceParameters { - swap_id, + swap_id: swap_id.into(), buy: self.buy, cancel: self.cancel, refund: self.refund, @@ -306,13 +312,13 @@ where } /// Generates protocol message that commits to Bob's parameters. - pub fn commit_bob( + pub fn commit_bob>( &self, - swap_id: SwapId, + swap_id: U, wallet: &impl Commit, ) -> CommitBobParameters { CommitBobParameters { - swap_id, + swap_id: swap_id.into(), buy: wallet.commit_to(self.buy.as_canonical_bytes()), cancel: wallet.commit_to(self.cancel.as_canonical_bytes()), refund: wallet.commit_to(self.refund.as_canonical_bytes()), @@ -326,9 +332,12 @@ where } /// Create the reveal protocol message based on the set of parameters. - pub fn reveal_bob(self, swap_id: SwapId) -> RevealBobParameters { + pub fn reveal_bob>( + self, + swap_id: U, + ) -> RevealBobParameters { RevealBobParameters { - swap_id, + swap_id: swap_id.into(), buy: self.buy, cancel: self.cancel, refund: self.refund, @@ -1399,9 +1408,9 @@ where /// [`sign_adaptor_buy`]: Bob::sign_adaptor_buy /// [`validate_adaptor_refund`]: Bob::validate_adaptor_refund /// - pub fn sign_adaptor_buy( + pub fn sign_adaptor_buy( &self, - swap_id: SwapId, + swap_id: U, wallet: &mut S, alice_parameters: &Parameters, bob_parameters: &Parameters, @@ -1414,6 +1423,7 @@ where Px: Clone + Fee, Pk: Copy, Ti: Copy, + U: Into, { // Extract the partial transaction from the core arbitrating protocol message, this // operation should not error if the message is well formed. @@ -1459,7 +1469,7 @@ where let sig = wallet.encrypt_sign(ArbitratingKeyId::Buy, adaptor, msg)?; Ok(BuyProcedureSignature { - swap_id, + swap_id: swap_id.into(), buy: buy.to_partial(), buy_adaptor_sig: sig, }) diff --git a/src/swap.rs b/src/swap.rs index 4ad936d..be0613c 100644 --- a/src/swap.rs +++ b/src/swap.rs @@ -18,35 +18,49 @@ //! and its concrete instances of swaps. use std::io; -use std::str::FromStr; -use crate::consensus::{self, Decodable, Encodable}; -use crate::hash::HashString; +use strict_encoding::{StrictDecode, StrictEncode}; -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use crate::consensus::{self, Decodable, Encodable}; +use crate::trade::DealId; +use crate::Uuid; pub mod btcxmr; -fixed_hash::construct_fixed_hash!( - /// A unique swap identifier represented as an 32 bytes hash. - pub struct SwapId(32); -); +/// The identifier of a swap. This is a wrapper around [`Uuid`] that can be constructed from +/// [`DealId`]. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + Display, + Serialize, + Deserialize, + StrictEncode, + StrictDecode, +)] +#[serde(transparent)] +#[display(inner)] +pub struct SwapId(pub Uuid); + +impl From for SwapId { + fn from(u: Uuid) -> Self { + SwapId(u) + } +} -impl Serialize for SwapId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(format!("{:#x}", self).as_ref()) +impl From for SwapId { + fn from(u: uuid::Uuid) -> Self { + SwapId(u.into()) } } -impl<'de> Deserialize<'de> for SwapId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - SwapId::from_str(&deserializer.deserialize_string(HashString)?).map_err(de::Error::custom) +impl From for SwapId { + fn from(t: DealId) -> Self { + SwapId(t.0) } } @@ -58,37 +72,6 @@ impl Encodable for SwapId { impl Decodable for SwapId { fn consensus_decode(d: &mut D) -> Result { - let bytes: [u8; 32] = Decodable::consensus_decode(d)?; - Ok(Self::from_slice(&bytes)) - } -} - -impl_strict_encoding!(SwapId); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn serialize_swapid_in_yaml() { - let swap_id = - SwapId::from_str("0x1baf1b36075de25a0f8e914b36759cac6f5d825622f8ccee597d87d4850c0d38") - .expect("Valid hex string"); - let s = serde_yaml::to_string(&swap_id).expect("Encode swap id in yaml"); - assert_eq!( - "---\n\"0x1baf1b36075de25a0f8e914b36759cac6f5d825622f8ccee597d87d4850c0d38\"\n", - s - ); - } - - #[test] - fn deserialize_swapid_from_yaml() { - let s = "---\n\"0x1baf1b36075de25a0f8e914b36759cac6f5d825622f8ccee597d87d4850c0d38\"\n"; - let swap_id = serde_yaml::from_str(&s).expect("Decode swap id from yaml"); - assert_eq!( - SwapId::from_str("0x1baf1b36075de25a0f8e914b36759cac6f5d825622f8ccee597d87d4850c0d38") - .expect("Valid hex string"), - swap_id - ); + Ok(Self(Decodable::consensus_decode(d)?)) } } diff --git a/src/swap/btcxmr.rs b/src/swap/btcxmr.rs index 5aaaa2b..6f3af19 100644 --- a/src/swap/btcxmr.rs +++ b/src/swap/btcxmr.rs @@ -235,7 +235,7 @@ impl KeyManager { key_id: impl Derivation, ) -> Result { let path = blockchain.derivation_path()?; - let path = path.extend(&[self.swap_index]); + let path = path.extend([self.swap_index]); Ok(path.extend(&key_id.derivation_path()?)) } diff --git a/src/trade.rs b/src/trade.rs index 7d43660..4cad5cd 100644 --- a/src/trade.rs +++ b/src/trade.rs @@ -40,9 +40,9 @@ use serde::ser::{Serialize, Serializer}; use serde::{de, Deserialize, Deserializer}; use std::fmt::Display; use std::str::FromStr; +use strict_encoding::{StrictDecode, StrictEncode}; use thiserror::Error; use tiny_keccak::{Hasher, Keccak}; -use uuid::Uuid; use std::fmt; use std::io; @@ -52,6 +52,7 @@ use crate::consensus::{self, serialize, serialize_hex, CanonicalBytes, Decodable use crate::hash::HashString; use crate::protocol::ArbitratingParameters; use crate::role::{SwapRole, TradeRole}; +use crate::Uuid; /// First six magic bytes of a deal. Bytes are included inside the base58 encoded part. pub const DEAL_MAGIC_BYTES: &[u8; 6] = b"FCSWAP"; @@ -104,6 +105,37 @@ pub enum Error { InvalidSignature, } +/// The identifier of a trade. This is a wrapper around [`Uuid`] that can be transformed into a +/// `SwapId`. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + Display, + Serialize, + Deserialize, + StrictEncode, + StrictDecode, +)] +#[serde(transparent)] +#[display(inner)] +pub struct DealId(pub Uuid); + +impl From for DealId { + fn from(u: Uuid) -> Self { + DealId(u) + } +} + +impl From for DealId { + fn from(u: uuid::Uuid) -> Self { + DealId(u.into()) + } +} + fixed_hash::construct_fixed_hash!( /// Identify a deal by its content, internally store the hash of the deal serialized with /// Farcaster consensus. @@ -142,7 +174,7 @@ impl<'de> Deserialize<'de> for DealFingerprint { #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct DealParameters { /// The deal unique identifier. - pub uuid: Uuid, + pub uuid: DealId, /// Type of deal and network to use. pub network: Network, /// The chosen arbitrating blockchain. @@ -241,18 +273,18 @@ impl DealParameters { impl DealParameters { /// Return the unique deal identifier. Same as [`Self::uuid()`]. - pub fn id(&self) -> Uuid { + pub fn id(&self) -> DealId { self.uuid() } /// Return the unique deal identifier. - pub fn uuid(&self) -> Uuid { + pub fn uuid(&self) -> DealId { self.uuid } /// Reset deal's uuid with a new identifier. pub fn randomize_uuid(&mut self) { - self.uuid = Uuid::new_v4(); + self.uuid = DealId(Uuid::new()); } } @@ -280,7 +312,7 @@ where F: CanonicalBytes, { fn consensus_encode(&self, s: &mut W) -> Result { - let mut len = self.uuid.to_bytes_le().consensus_encode(s)?; + let mut len = self.uuid.0.consensus_encode(s)?; len += self.network.consensus_encode(s)?; len += self.arbitrating_blockchain.consensus_encode(s)?; len += self.accordant_blockchain.consensus_encode(s)?; @@ -314,7 +346,7 @@ where { fn consensus_decode(d: &mut D) -> Result { Ok(DealParameters { - uuid: Uuid::from_bytes_le(Decodable::consensus_decode(d)?), + uuid: DealId(Decodable::consensus_decode(d)?), network: Decodable::consensus_decode(d)?, arbitrating_blockchain: Decodable::consensus_decode(d)?, accordant_blockchain: Decodable::consensus_decode(d)?, @@ -390,12 +422,12 @@ where impl Deal { /// Return the unique deal identifier. Same as [`Self::uuid()`]. - pub fn id(&self) -> Uuid { + pub fn id(&self) -> DealId { self.uuid() } /// Return the unique deal identifier. - pub fn uuid(&self) -> Uuid { + pub fn uuid(&self) -> DealId { self.parameters.uuid() } @@ -522,7 +554,7 @@ mod tests { pub static ref DEAL_PARAMS: DealParameters = { DealParameters { - uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"), + uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(), network: Network::Testnet, arbitrating_blockchain: Blockchain::Bitcoin, accordant_blockchain: Blockchain::Monero, @@ -573,7 +605,7 @@ mod tests { fn serialize_deal_params_in_yaml() { let deal_params: DealParameters = DealParameters { - uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"), + uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(), network: Network::Testnet, arbitrating_blockchain: Blockchain::Bitcoin, accordant_blockchain: Blockchain::Monero, @@ -597,7 +629,7 @@ mod tests { let deal_params = serde_yaml::from_str(&s).expect("Decode deal from yaml"); assert_eq!( DealParameters { - uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"), + uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(), network: Network::Testnet, arbitrating_blockchain: Blockchain::Bitcoin, accordant_blockchain: Blockchain::Monero, diff --git a/tests/protocol.rs b/tests/protocol.rs index 1e98619..12598c1 100644 --- a/tests/protocol.rs +++ b/tests/protocol.rs @@ -27,8 +27,8 @@ use farcaster_core::crypto::{ }; use farcaster_core::protocol::message::*; use farcaster_core::swap::btcxmr::{Alice, Bob, Deal, Parameters}; -use farcaster_core::swap::SwapId; use farcaster_core::transaction::*; +use farcaster_core::Uuid; use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut}; use bitcoin::blockdata::witness::Witness; @@ -90,7 +90,7 @@ fn execute_offline_protocol() { ) .unwrap(); - let swap_id = SwapId::random(); + let swap_id = Uuid::random(); // // Commit/Reveal round @@ -188,7 +188,7 @@ fn execute_offline_protocol() { .unwrap(); let refund_proc_sig = RefundProcedureSignatures { - swap_id, + swap_id: swap_id.into(), cancel_sig, refund_adaptor_sig: refund_adaptor_sig.clone(), }; diff --git a/tests/trade.rs b/tests/trade.rs index a7e899c..0934ca5 100644 --- a/tests/trade.rs +++ b/tests/trade.rs @@ -35,7 +35,7 @@ fn create_deal_parameters() { 10800090000000000000002"; let deal: DealParameters = DealParameters { - uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"), + uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(), network: Network::Testnet, arbitrating_blockchain: Blockchain::Bitcoin, accordant_blockchain: Blockchain::Monero, @@ -81,7 +81,10 @@ fn get_deal_parameters_uuid() { 00000000000040007000000040008000000010800090000000000000002"; let res: DealParameters = strict_encoding::strict_deserialize(&hex::decode(hex).unwrap()).unwrap(); - assert_eq!(uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"), res.uuid()); + assert_eq!( + res.uuid(), + uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(), + ); } #[test] @@ -92,7 +95,7 @@ fn serialize_deal() { 0000000000000000000000000000000000000000000000000000000260700"; let deal: DealParameters = DealParameters { - uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"), + uuid: uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(), network: Network::Testnet, arbitrating_blockchain: Blockchain::Bitcoin, accordant_blockchain: Blockchain::Monero, @@ -156,7 +159,10 @@ fn get_deal_uuid() { 0000000000000000000000000000000000000000000000000000000260700"; let res: Deal = strict_encoding::strict_deserialize(&hex::decode(hex).unwrap()).unwrap(); - assert_eq!(uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"), res.uuid()); + assert_eq!( + res.uuid(), + uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8").into(), + ); } #[test]