Skip to content

Commit e2e2c9e

Browse files
authored
feat: new offer uuid and fingerprint (#292)
* feat: new offer uuid and fingerprint * chore: update changelog * chore: fix short name
1 parent ed60a25 commit e2e2c9e

6 files changed

Lines changed: 158 additions & 122 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Offer `uuid` of type `Uuid` ([#292](https://github.com/farcaster-project/farcaster-core/pull/292))
13+
- Offer and public offer `fingerprint` functions, returns `OfferFingerprint` ([#292](https://github.com/farcaster-project/farcaster-core/pull/292))
14+
15+
### Removed
16+
17+
- `OfferId` and `PublicOfferId` are replaced by the new offer `uuid` and `fingerprint` functions ([#292](https://github.com/farcaster-project/farcaster-core/pull/292))
18+
1019
## [0.5.0] - 2022-07-27
1120

1221
### Added

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ strict_encoding = "0.8"
4343
strict_encoding_derive = "1.7"
4444
thiserror = "1"
4545
tiny-keccak = { version = "2", features = ["keccak"] }
46+
uuid = { version = "1.1", features = ["v4", "serde"] }
4647

4748
# crypto libs
4849

src/consensus.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ macro_rules! impl_fixed_array {
226226
}
227227

228228
impl_fixed_array!(6);
229+
impl_fixed_array!(16);
229230
impl_fixed_array!(32);
230231
impl_fixed_array!(33);
231232
impl_fixed_array!(64);

src/negotiation.rs

Lines changed: 71 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use std::fmt::Display;
4343
use std::str::FromStr;
4444
use thiserror::Error;
4545
use tiny_keccak::{Hasher, Keccak};
46+
use uuid::Uuid;
4647

4748
use std::fmt;
4849
use std::io;
@@ -107,10 +108,10 @@ pub enum Error {
107108
fixed_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)]
142144
pub 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

195199
impl<Amt, Bmt, Ti, F> Display for Offer<Amt, Bmt, Ti, F>
196200
where
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+
240264
impl<Amt, Bmt, Ti, F> Offer<Amt, Bmt, Ti, F>
241265
where
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

308335
impl_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>
387374
where
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

406395
impl<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\nBlockchain: Bitcoin\n- amount: 0.00001350 BTC\nBlockchain: Monero\n- amount: 0.000000010000 XMR\nTimelocks\n- cancel: 4 blocks\n- punish: 6 blocks\nFee strategy: 1 satoshi/vByte\nMaker swap role: Bob\n");
570+
assert_eq!(&format!("{}", *OFFER), "Uuid: 67e55044-10b1-426f-9247-bb680e5fe0c8\nFingerprint: 0xd68b1483de11001050026ca012a2b440818dac23341384c60680f668b52697b0\nNetwork: Testnet\nBlockchain: Bitcoin\n- amount: 0.00001350 BTC\nBlockchain: Monero\n- amount: 0.000000010000 XMR\nTimelocks\n- cancel: 4 blocks\n- punish: 6 blocks\nFee strategy: 1 satoshi/vByte\nMaker 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-
"---\nnetwork: Testnet\narbitrating_blockchain: Bitcoin\naccordant_blockchain: Monero\narbitrating_amount: 0.00000005 BTC\naccordant_amount: 0.000000000006 XMR\ncancel_timelock: 7\npunish_timelock: 8\nfee_strategy:\n Fixed: 9 satoshi/vByte\nmaker_role: Bob\n",
595+
"---\nuuid: 67e55044-10b1-426f-9247-bb680e5fe0c8\nnetwork: Testnet\narbitrating_blockchain: Bitcoin\naccordant_blockchain: Monero\narbitrating_amount: 0.00000005 BTC\naccordant_amount: 0.000000000006 XMR\ncancel_timelock: 7\npunish_timelock: 8\nfee_strategy:\n Fixed: 9 satoshi/vByte\nmaker_role: Bob\n",
589596
s
590597
);
591598
}
592599

593600
#[test]
594601
fn deserialize_offer_from_yaml() {
595-
let s = "---\nnetwork: Testnet\narbitrating_blockchain: Bitcoin\naccordant_blockchain: Monero\narbitrating_amount: 0.00000005 BTC\naccordant_amount: 0.000000000006 XMR\ncancel_timelock: 7\npunish_timelock: 8\nfee_strategy:\n Fixed: 9 satoshi/vByte\nmaker_role: Bob\n";
602+
let s = "---\nuuid: 67e55044-10b1-426f-9247-bb680e5fe0c8\nnetwork: Testnet\narbitrating_blockchain: Bitcoin\naccordant_blockchain: Monero\narbitrating_amount: 0.00000005 BTC\naccordant_amount: 0.000000000006 XMR\ncancel_timelock: 7\npunish_timelock: 8\nfee_strategy:\n Fixed: 9 satoshi/vByte\nmaker_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-
"---\nversion: 1\noffer:\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\nnode_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\npeer_address:\n IPv4: \"127.0.0.1:9735\"\n",
628+
"---\nversion: 1\noffer:\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\nnode_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\npeer_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 = "---\nversion: 1\noffer:\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\nnode_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\npeer_address:\n IPv4: \"127.0.0.1:9735\"\n";
635+
let s = "---\nversion: 1\noffer:\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\nnode_id: 02e77b779cdc2c713823f7a19147a67e4209c74d77e2cb5045bce0584a6be064d4\npeer_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

Comments
 (0)