@@ -43,6 +43,7 @@ use std::fmt::Display;
4343use std:: str:: FromStr ;
4444use thiserror:: Error ;
4545use tiny_keccak:: { Hasher , Keccak } ;
46+ use uuid:: Uuid ;
4647
4748use std:: fmt;
4849use std:: io;
@@ -107,10 +108,10 @@ pub enum Error {
107108fixed_hash:: construct_fixed_hash!(
108109 /// Identify an offer by it's content, internally store the hash of the offer serialized with
109110 /// Farcaster consensus.
110- pub struct OfferId ( 32 ) ;
111+ pub struct OfferFingerprint ( 32 ) ;
111112) ;
112113
113- impl Serialize for OfferId {
114+ impl Serialize for OfferFingerprint {
114115 fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
115116 where
116117 S : Serializer ,
@@ -119,12 +120,13 @@ impl Serialize for OfferId {
119120 }
120121}
121122
122- impl < ' de > Deserialize < ' de > for OfferId {
123- fn deserialize < D > ( deserializer : D ) -> Result < OfferId , D :: Error >
123+ impl < ' de > Deserialize < ' de > for OfferFingerprint {
124+ fn deserialize < D > ( deserializer : D ) -> Result < OfferFingerprint , D :: Error >
124125 where
125126 D : Deserializer < ' de > ,
126127 {
127- OfferId :: from_str ( & deserializer. deserialize_string ( HashString ) ?) . map_err ( de:: Error :: custom)
128+ OfferFingerprint :: from_str ( & deserializer. deserialize_string ( HashString ) ?)
129+ . map_err ( de:: Error :: custom)
128130 }
129131}
130132
@@ -140,6 +142,8 @@ impl<'de> Deserialize<'de> for OfferId {
140142/// de/serialize generic amounts.
141143#[ derive( Debug , Clone , Eq , PartialEq , Hash , Serialize , Deserialize ) ]
142144pub struct Offer < Amt , Bmt , Ti , F > {
145+ /// The offer unique identifier.
146+ pub uuid : Uuid ,
143147 /// Type of offer and network to use.
144148 pub network : Network ,
145149 /// The chosen arbitrating blockchain.
@@ -194,12 +198,15 @@ mod string {
194198
195199impl < Amt , Bmt , Ti , F > Display for Offer < Amt , Bmt , Ti , F >
196200where
201+ Self : Encodable ,
197202 Amt : Display ,
198203 Bmt : Display ,
199204 Ti : Display ,
200205 F : Display ,
201206{
202207 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
208+ writeln ! ( f, "Uuid: {}" , self . uuid) ?;
209+ writeln ! ( f, "Fingerprint: {:?}" , self . fingerprint( ) ) ?;
203210 writeln ! ( f, "Network: {}" , self . network) ?;
204211 writeln ! ( f, "Blockchain: {}" , self . arbitrating_blockchain) ?;
205212 writeln ! ( f, "- amount: {}" , self . arbitrating_amount) ?;
@@ -237,17 +244,35 @@ impl<Amt, Bmt, Ti, F> Offer<Amt, Bmt, Ti, F> {
237244 }
238245}
239246
247+ impl < Amt , Bmt , Ti , F > Offer < Amt , Bmt , Ti , F > {
248+ /// Return the unique offer identifier. Same as [`Self::uuid()`].
249+ pub fn id ( & self ) -> Uuid {
250+ self . uuid ( )
251+ }
252+
253+ /// Return the unique offer identifier.
254+ pub fn uuid ( & self ) -> Uuid {
255+ self . uuid
256+ }
257+
258+ /// Reset offer's uuid with a new identifier.
259+ pub fn randomize_uuid ( & mut self ) {
260+ self . uuid = Uuid :: new_v4 ( ) ;
261+ }
262+ }
263+
240264impl < Amt , Bmt , Ti , F > Offer < Amt , Bmt , Ti , F >
241265where
242266 Self : Encodable ,
243267{
244- /// Generate the [`OfferId`] from the offer.
245- pub fn id ( & self ) -> OfferId {
268+ /// Generate the [`OfferFingerprint`] from the offer. The fingerprint identifies the content of
269+ /// an offer (**without the uuid**) by taking the hash value of its serialization.
270+ pub fn fingerprint ( & self ) -> OfferFingerprint {
246271 let mut keccak = Keccak :: v256 ( ) ;
247272 let mut out = [ 0u8 ; 32 ] ;
248- keccak. update ( serialize ( self ) . as_ref ( ) ) ;
273+ keccak. update ( & serialize ( self ) [ 16 .. ] ) ;
249274 keccak. finalize ( & mut out) ;
250- OfferId ( out)
275+ OfferFingerprint ( out)
251276 }
252277}
253278
@@ -259,7 +284,8 @@ where
259284 F : CanonicalBytes ,
260285{
261286 fn consensus_encode < W : io:: Write > ( & self , s : & mut W ) -> Result < usize , io:: Error > {
262- let mut len = self . network . consensus_encode ( s) ?;
287+ let mut len = self . uuid . to_bytes_le ( ) . consensus_encode ( s) ?;
288+ len += self . network . consensus_encode ( s) ?;
263289 len += self . arbitrating_blockchain . consensus_encode ( s) ?;
264290 len += self . accordant_blockchain . consensus_encode ( s) ?;
265291 len += self
@@ -292,6 +318,7 @@ where
292318{
293319 fn consensus_decode < D : io:: Read > ( d : & mut D ) -> Result < Self , consensus:: Error > {
294320 Ok ( Offer {
321+ uuid : Uuid :: from_bytes_le ( Decodable :: consensus_decode ( d) ?) ,
295322 network : Decodable :: consensus_decode ( d) ?,
296323 arbitrating_blockchain : Decodable :: consensus_decode ( d) ?,
297324 accordant_blockchain : Decodable :: consensus_decode ( d) ?,
@@ -307,46 +334,6 @@ where
307334
308335impl_strict_encoding ! ( Offer <Amt , Bmt , Ti , F >, Amt : CanonicalBytes , Bmt : CanonicalBytes , Ti : CanonicalBytes , F : CanonicalBytes , ) ;
309336
310- fixed_hash:: construct_fixed_hash!(
311- /// Identify a public offer by it's content, internally store the hash of the offer serialized
312- /// with Farcaster consensus.
313- pub struct PublicOfferId ( 32 ) ;
314- ) ;
315-
316- impl Serialize for PublicOfferId {
317- fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
318- where
319- S : Serializer ,
320- {
321- serializer. serialize_str ( format ! ( "{:#x}" , self ) . as_ref ( ) )
322- }
323- }
324-
325- impl < ' de > Deserialize < ' de > for PublicOfferId {
326- fn deserialize < D > ( deserializer : D ) -> Result < PublicOfferId , D :: Error >
327- where
328- D : Deserializer < ' de > ,
329- {
330- PublicOfferId :: from_str ( & deserializer. deserialize_string ( HashString ) ?)
331- . map_err ( de:: Error :: custom)
332- }
333- }
334-
335- impl Encodable for PublicOfferId {
336- fn consensus_encode < W : io:: Write > ( & self , s : & mut W ) -> Result < usize , io:: Error > {
337- self . 0 . consensus_encode ( s)
338- }
339- }
340-
341- impl Decodable for PublicOfferId {
342- fn consensus_decode < D : io:: Read > ( d : & mut D ) -> Result < Self , consensus:: Error > {
343- let bytes: [ u8 ; 32 ] = Decodable :: consensus_decode ( d) ?;
344- Ok ( Self :: from_slice ( & bytes) )
345- }
346- }
347-
348- impl_strict_encoding ! ( PublicOfferId ) ;
349-
350337/// A public offer is shared across [`TradeRole::Maker`]'s prefered network to signal is willing of
351338/// trading some assets at some conditions. The assets and condition are defined in the [`Offer`],
352339/// maker peer connection information are contained in the public offer.
@@ -387,14 +374,16 @@ impl<Amt, Bmt, Ti, F> PublicOffer<Amt, Bmt, Ti, F>
387374where
388375 Self : Encodable ,
389376{
390- /// Generate the [`PublicOfferId`] from the offer . Serialized the public offer with consensus
391- /// encoding and return the keccak hash result with [`PublicOfferId`] .
392- pub fn id ( & self ) -> PublicOfferId {
377+ /// Generate the public offer [`OfferFingerprint`] . Serialized the public offer (**without
378+ /// uuid**) and return its keccak hash.
379+ pub fn fingerprint ( & self ) -> OfferFingerprint {
393380 let mut keccak = Keccak :: v256 ( ) ;
394381 let mut out = [ 0u8 ; 32 ] ;
395- keccak. update ( serialize ( self ) . as_ref ( ) ) ;
382+ let ser = serialize ( self ) ;
383+ keccak. update ( & ser[ ..8 ] ) ;
384+ keccak. update ( & ser[ 24 ..] ) ;
396385 keccak. finalize ( & mut out) ;
397- PublicOfferId ( out)
386+ OfferFingerprint ( out)
398387 }
399388
400389 /// Returns the hex string representation of the consensus encoded public offer.
@@ -404,6 +393,21 @@ where
404393}
405394
406395impl < Amt , Bmt , Ti , F > PublicOffer < Amt , Bmt , Ti , F > {
396+ /// Return the unique offer identifier. Same as [`Self::uuid()`].
397+ pub fn id ( & self ) -> Uuid {
398+ self . uuid ( )
399+ }
400+
401+ /// Return the unique offer identifier.
402+ pub fn uuid ( & self ) -> Uuid {
403+ self . offer . uuid ( )
404+ }
405+
406+ /// Reset offer's uuid with a new identifier.
407+ pub fn randomize_uuid ( & mut self ) {
408+ self . offer . randomize_uuid ( ) ;
409+ }
410+
407411 /// Return the future swap role for the given trade role.
408412 pub fn swap_role ( & self , trade_role : & TradeRole ) -> SwapRole {
409413 self . offer . swap_role ( trade_role)
@@ -500,8 +504,9 @@ mod tests {
500504 } ;
501505 use inet2_addr:: InetSocketAddr ;
502506 use secp256k1:: PublicKey ;
507+ use uuid:: uuid;
503508
504- const S : & str = "Offer:Cke4ftrP5A71LQM2fvVdFMNR4gmBqNCsR11111uMM4pF11111112Lvo11111TBALTh113GTvtvqfD1111114A4TUWxWeBc1WxwGBKaUssrb6pnijjhnb6RAs1HBr1CaX7o1a1111111111111111111111111111111111111111115T1WG8uDoExnA3T " ;
509+ const S : & str = "Offer:Cke4ftrP5A7CRkYdGNd87TRU6sUP1kBKM1LQM2fvVdFMNR4gmBqNCsR11111uMM4pF11111112Lvo11111TBALTh113GTvtvqfD1111114A4TUWxWeBc1WxwGBKaUssrb6pnijjhnb6RAs1HBr1CaX7o1a1111111111111111111111111111111111111111115T1WG8uDoZeAW1q " ;
505510
506511 lazy_static:: lazy_static! {
507512 pub static ref NODE_ID : PublicKey = {
@@ -521,6 +526,7 @@ mod tests {
521526
522527 pub static ref OFFER : Offer <bitcoin:: Amount , monero:: Amount , CSVTimelock , SatPerVByte > = {
523528 Offer {
529+ uuid: uuid!( "67e55044-10b1-426f-9247-bb680e5fe0c8" ) ,
524530 network: Network :: Testnet ,
525531 arbitrating_blockchain: Blockchain :: Bitcoin ,
526532 accordant_blockchain: Blockchain :: Monero ,
@@ -561,7 +567,7 @@ mod tests {
561567
562568 #[ test]
563569 fn display_offer ( ) {
564- assert_eq ! ( & format!( "{}" , * OFFER ) , "Network : Testnet\n Blockchain: Bitcoin\n - amount: 0.00001350 BTC\n Blockchain: Monero\n - amount: 0.000000010000 XMR\n Timelocks\n - cancel: 4 blocks\n - punish: 6 blocks\n Fee strategy: 1 satoshi/vByte\n Maker swap role: Bob\n " ) ;
570+ assert_eq ! ( & format!( "{}" , * OFFER ) , "Uuid: 67e55044-10b1-426f-9247-bb680e5fe0c8 \n Fingerprint: 0xd68b1483de11001050026ca012a2b440818dac23341384c60680f668b52697b0 \n Network : Testnet\n Blockchain: Bitcoin\n - amount: 0.00001350 BTC\n Blockchain: Monero\n - amount: 0.000000010000 XMR\n Timelocks\n - cancel: 4 blocks\n - punish: 6 blocks\n Fee strategy: 1 satoshi/vByte\n Maker swap role: Bob\n " ) ;
565571 }
566572
567573 #[ test]
@@ -573,6 +579,7 @@ mod tests {
573579 #[ test]
574580 fn serialize_offer_in_yaml ( ) {
575581 let offer: Offer < bitcoin:: Amount , monero:: Amount , CSVTimelock , SatPerVByte > = Offer {
582+ uuid : uuid ! ( "67e55044-10b1-426f-9247-bb680e5fe0c8" ) ,
576583 network : Network :: Testnet ,
577584 arbitrating_blockchain : Blockchain :: Bitcoin ,
578585 accordant_blockchain : Blockchain :: Monero ,
@@ -585,17 +592,18 @@ mod tests {
585592 } ;
586593 let s = serde_yaml:: to_string ( & offer) . expect ( "Encode public offer in yaml" ) ;
587594 assert_eq ! (
588- "---\n network: Testnet\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00000005 BTC\n accordant_amount: 0.000000000006 XMR\n cancel_timelock: 7\n punish_timelock: 8\n fee_strategy:\n Fixed: 9 satoshi/vByte\n maker_role: Bob\n " ,
595+ "---\n uuid: 67e55044-10b1-426f-9247-bb680e5fe0c8 \ n network: Testnet\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00000005 BTC\n accordant_amount: 0.000000000006 XMR\n cancel_timelock: 7\n punish_timelock: 8\n fee_strategy:\n Fixed: 9 satoshi/vByte\n maker_role: Bob\n " ,
589596 s
590597 ) ;
591598 }
592599
593600 #[ test]
594601 fn deserialize_offer_from_yaml ( ) {
595- let s = "---\n network: Testnet\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00000005 BTC\n accordant_amount: 0.000000000006 XMR\n cancel_timelock: 7\n punish_timelock: 8\n fee_strategy:\n Fixed: 9 satoshi/vByte\n maker_role: Bob\n " ;
602+ let s = "---\n uuid: 67e55044-10b1-426f-9247-bb680e5fe0c8 \ n network: Testnet\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00000005 BTC\n accordant_amount: 0.000000000006 XMR\n cancel_timelock: 7\n punish_timelock: 8\n fee_strategy:\n Fixed: 9 satoshi/vByte\n maker_role: Bob\n " ;
596603 let offer = serde_yaml:: from_str ( & s) . expect ( "Decode offer from yaml" ) ;
597604 assert_eq ! (
598605 Offer {
606+ uuid: uuid!( "67e55044-10b1-426f-9247-bb680e5fe0c8" ) ,
599607 network: Network :: Testnet ,
600608 arbitrating_blockchain: Blockchain :: Bitcoin ,
601609 accordant_blockchain: Blockchain :: Monero ,
@@ -613,21 +621,21 @@ mod tests {
613621 #[ test]
614622 fn serialize_public_offer_in_yaml ( ) {
615623 let public_offer =
616- PublicOffer :: < bitcoin:: Amount , monero:: Amount , CSVTimelock , SatPerVByte > :: from_str ( "Offer:Cke4ftrP5A71W723UjzEWsNR4gmBqNCsR11111uMFubBevJ2E5fp6ZR11111TBALTh113GTvtvqfD1111114A4TTfifktDH7QZD71vpdfo6EVo2ds7KviHz7vYbLZDkgsMNb11111111111111111111111111111111111111111AfZ113XRBum3er3R " )
624+ PublicOffer :: < bitcoin:: Amount , monero:: Amount , CSVTimelock , SatPerVByte > :: from_str ( "Offer:Cke4ftrP5A7CRkYdGNd87TRU6sUP1kBKM1W723UjzEWsNR4gmBqNCsR11111uMFubBevJ2E5fp6ZR11111TBALTh113GTvtvqfD1111114A4TTfifktDH7QZD71vpdfo6EVo2ds7KviHz7vYbLZDkgsMNb11111111111111111111111111111111111111111AfZ113XRBuL3QS1m " )
617625 . expect ( "Valid public offer" ) ;
618626 let s = serde_yaml:: to_string ( & public_offer) . expect ( "Encode public offer in yaml" ) ;
619627 assert_eq ! (
620- "---\n version: 1\n offer:\n network: Local\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00001350 BTC\n accordant_amount: 1000000.001000000000 XMR\n cancel_timelock: 4\n punish_timelock: 6\n fee_strategy:\n Fixed: 1 satoshi/vByte\n maker_role: Bob\n node_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\n peer_address:\n IPv4: \" 127.0.0.1:9735\" \n " ,
628+ "---\n version: 1\n offer:\n uuid: 67e55044-10b1-426f-9247-bb680e5fe0c8 \n network: Local\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00001350 BTC\n accordant_amount: 1000000.001000000000 XMR\n cancel_timelock: 4\n punish_timelock: 6\n fee_strategy:\n Fixed: 1 satoshi/vByte\n maker_role: Bob\n node_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\n peer_address:\n IPv4: \" 127.0.0.1:9735\" \n " ,
621629 s
622630 ) ;
623631 }
624632
625633 #[ test]
626634 fn deserialize_public_offer_from_yaml ( ) {
627- let s = "---\n version: 1\n offer:\n network: Local\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00001350 BTC\n accordant_amount: 1000000.001000000000 XMR\n cancel_timelock: 4\n punish_timelock: 6\n fee_strategy:\n Fixed: 1 satoshi/vByte\n maker_role: Bob\n node_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\n peer_address:\n IPv4: \" 127.0.0.1:9735\" \n " ;
635+ let s = "---\n version: 1\n offer:\n uuid: 67e55044-10b1-426f-9247-bb680e5fe0c8 \n network: Local\n arbitrating_blockchain: Bitcoin\n accordant_blockchain: Monero\n arbitrating_amount: 0.00001350 BTC\n accordant_amount: 1000000.001000000000 XMR\n cancel_timelock: 4\n punish_timelock: 6\n fee_strategy:\n Fixed: 1 satoshi/vByte\n maker_role: Bob\n node_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\n peer_address:\n IPv4: \" 127.0.0.1:9735\" \n " ;
628636 let public_offer = serde_yaml:: from_str ( & s) . expect ( "Decode public offer from yaml" ) ;
629637 assert_eq ! (
630- PublicOffer :: <bitcoin:: Amount , monero:: Amount , CSVTimelock , SatPerVByte >:: from_str( "Offer:Cke4ftrP5A71W723UjzEWsNR4gmBqNCsR11111uMFubBevJ2E5fp6ZR11111TBALTh113GTvtvqfD1111114A4TTfifktDH7QZD71vpdfo6EVo2ds7KviHz7vYbLZDkgsMNb11111111111111111111111111111111111111111AfZ113XRBum3er3R " )
638+ PublicOffer :: <bitcoin:: Amount , monero:: Amount , CSVTimelock , SatPerVByte >:: from_str( "Offer:Cke4ftrP5A7CRkYdGNd87TRU6sUP1kBKM1W723UjzEWsNR4gmBqNCsR11111uMFubBevJ2E5fp6ZR11111TBALTh113GTvtvqfD1111114A4TTfifktDH7QZD71vpdfo6EVo2ds7KviHz7vYbLZDkgsMNb11111111111111111111111111111111111111111AfZ113XRBuL3QS1m " )
631639 . expect( "Valid public offer" ) ,
632640 public_offer
633641 ) ;
0 commit comments