lsp_primitives/lsps0/
common_schemas.rs

1use core::str::FromStr;
2use std::fmt::{Display, Formatter};
3
4use anyhow::{anyhow, Context, Result};
5use bitcoin::address::Address;
6pub use bitcoin::address::{NetworkChecked, NetworkUnchecked, NetworkValidation};
7pub use bitcoin::network::Network;
8
9use serde::de::Error as SeError;
10use serde::ser::Error as DeError;
11use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
12
13use time::format_description::FormatItem;
14use time::macros::{format_description, offset};
15use time::{OffsetDateTime, PrimitiveDateTime};
16
17use crate::secp256k1::PublicKey as _PublicKey;
18
19#[derive(Serialize, Clone, Debug)]
20#[serde(transparent)]
21pub struct OnchainAddress {
22    pub address: Address,
23}
24
25impl<'de> Deserialize<'de> for OnchainAddress {
26    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
27    where
28        D: Deserializer<'de>,
29    {
30        let address_unchecked = Address::<NetworkUnchecked>::deserialize(deserializer)?;
31        Ok(OnchainAddress {
32            address: address_unchecked.assume_checked(),
33        })
34    }
35}
36
37pub trait NetworkCheckable {
38    fn require_network(&self, network: &bitcoin::Network) -> Result<()>;
39}
40
41impl NetworkCheckable for OnchainAddress {
42    fn require_network(&self, network: &bitcoin::Network) -> Result<()> {
43        if network != self.address.network() {
44            Err(anyhow!(
45                "Network mismatch: Expected {} but got {}",
46                network,
47                self.address.network()
48            ))
49        } else {
50            Ok(())
51        }
52    }
53}
54
55impl NetworkCheckable for Option<OnchainAddress> {
56    fn require_network(&self, network: &bitcoin::Network) -> Result<()> {
57        match self {
58            Some(x) => x.require_network(network),
59            None => Ok(()),
60        }
61    }
62}
63
64impl FromStr for OnchainAddress {
65    type Err = anyhow::Error;
66
67    fn from_str(s: &str) -> Result<Self, Self::Err> {
68        let address = Address::<NetworkUnchecked>::from_str(s)?;
69        Ok(Self {
70            address: address.assume_checked(),
71        })
72    }
73}
74
75impl Display for OnchainAddress {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        write!(f, "{}", self.address)
78    }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct PublicKey(_PublicKey);
83
84impl std::hash::Hash for PublicKey {
85    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
86        let x = self.inner().serialize();
87        x.hash(state);
88    }
89}
90
91impl From<_PublicKey> for PublicKey {
92    fn from(public_key: _PublicKey) -> Self {
93        Self(public_key)
94    }
95}
96
97impl Serialize for PublicKey {
98    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99    where
100        S: Serializer,
101    {
102        let s = hex::encode(self.0.serialize());
103        serializer.serialize_str(&s)
104    }
105}
106
107impl<'de> Deserialize<'de> for PublicKey {
108    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109    where
110        D: Deserializer<'de>,
111    {
112        struct PublicKeyVisitor;
113
114        impl Visitor<'_> for PublicKeyVisitor {
115            type Value = PublicKey;
116
117            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
118                write!(formatter, "A compressed public-key that is hex-encoded")
119            }
120
121            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
122            where
123                E: serde::de::Error,
124            {
125                let data =
126                    hex::decode(v).map_err(|_| serde::de::Error::custom("Expected valid hex"))?;
127                let pubkey = _PublicKey::from_slice(&data).map_err(|err| {
128                    serde::de::Error::custom(format!("Invalid public-key: {}", err))
129                })?;
130                Ok(pubkey.into())
131            }
132        }
133
134        let visitor = PublicKeyVisitor;
135        deserializer.deserialize_str(visitor)
136    }
137}
138
139impl PublicKey {
140    pub fn from_hex(hex: &str) -> Result<Self> {
141        let data = hex::decode(hex).context("Invalid hex")?;
142        let publickey =
143            _PublicKey::from_slice(&data).map_err(|m| anyhow!("Error parsing PublicKey: {}", m))?;
144        Ok(PublicKey(publickey))
145    }
146
147    pub fn to_hex(&self) -> String {
148        let data = self.0.serialize();
149        hex::encode(data)
150    }
151
152    pub fn inner(self) -> _PublicKey {
153        self.0
154    }
155}
156
157// Implements all the common schema's defined in LSPS0 common schema's
158
159// Initially I used serde_as for the parsing and serialization of this type.
160// However, the spec is more strict.
161// It requires a yyyy-mm-ddThh:mm:ss.uuuZ format
162//
163// The serde_as provides us options such as rfc_3339.
164// Note, that this also allows formats that are not compliant to the LSP-spec such as dropping
165// the fractional seconds or use non UTC timezones.
166//
167// For LSPS2 the `valid_until`-field must be copied verbatim. As a client this can only be
168// achieved if the LSPS2 sends a fully compliant timestamp.
169//
170// I have decided to fail early if another timestamp is received
171#[derive(Debug, Clone, PartialEq, PartialOrd, Copy)]
172pub struct IsoDatetime {
173    pub datetime: PrimitiveDateTime,
174}
175
176const DATETIME_FORMAT: &[FormatItem] =
177    format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z");
178
179impl IsoDatetime {
180    pub fn now() -> Self {
181        Self::from_offset_date_time(time::OffsetDateTime::now_utc())
182    }
183
184    pub fn from_offset_date_time(datetime: OffsetDateTime) -> Self {
185        let offset = time::UtcOffset::from_whole_seconds(0).unwrap();
186        let datetime_utc = datetime.to_offset(offset);
187        let primitive = PrimitiveDateTime::new(datetime_utc.date(), datetime.time());
188        Self {
189            datetime: primitive,
190        }
191    }
192
193    pub fn from_unix_timestamp(value: i64) -> Result<Self> {
194        let offset =
195            OffsetDateTime::from_unix_timestamp(value).context("Failed to construct datetime")?;
196        Ok(Self::from_offset_date_time(offset))
197    }
198
199    pub fn unix_timestamp(&self) -> i64 {
200        self.datetime.assume_offset(offset!(UTC)).unix_timestamp()
201    }
202
203    pub fn from_primitive_date_time(datetime: PrimitiveDateTime) -> Self {
204        Self { datetime }
205    }
206
207    pub fn datetime(&self) -> OffsetDateTime {
208        self.datetime.assume_utc()
209    }
210}
211
212impl Serialize for IsoDatetime {
213    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
214    where
215        S: serde::Serializer,
216    {
217        let datetime_str = self
218            .datetime
219            .format(&DATETIME_FORMAT)
220            .map_err(|err| S::Error::custom(format!("Failed to format datetime {:?}", err)))?;
221
222        serializer.serialize_str(&datetime_str)
223    }
224}
225
226impl<'de> Deserialize<'de> for IsoDatetime {
227    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
228    where
229        D: serde::Deserializer<'de>,
230    {
231        let str_repr = <String as serde::de::Deserialize>::deserialize(deserializer)?;
232        time::PrimitiveDateTime::parse(&str_repr, DATETIME_FORMAT)
233            .map_err(|err| D::Error::custom(format!("Failed to parse Datetime. {:?}", err)))
234            .map(Self::from_primitive_date_time)
235    }
236}
237
238#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Copy, Default)]
239pub struct SatAmount(u64);
240#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Copy, Default)]
241pub struct MsatAmount(u64);
242
243impl std::fmt::Display for SatAmount {
244    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
245        write!(fmt, "{} sat", self.0)
246    }
247}
248
249impl std::fmt::Display for MsatAmount {
250    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
251        write!(fmt, "{} msat", self.0)
252    }
253}
254
255impl SatAmount {
256    pub fn sat_value(&self) -> u64 {
257        self.0
258    }
259
260    pub fn new(value: u64) -> Self {
261        SatAmount(value)
262    }
263}
264
265impl MsatAmount {
266    pub fn msat_value(&self) -> u64 {
267        self.0
268    }
269
270    pub fn new(value: u64) -> Self {
271        MsatAmount(value)
272    }
273}
274
275impl Serialize for SatAmount {
276    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
277    where
278        S: serde::Serializer,
279    {
280        let amount_str = self.0.to_string();
281        serializer.serialize_str(&amount_str)
282    }
283}
284
285impl Serialize for MsatAmount {
286    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
287    where
288        S: serde::Serializer,
289    {
290        let amount_str = self.0.to_string();
291        serializer.serialize_str(&amount_str)
292    }
293}
294
295impl<'de> Deserialize<'de> for SatAmount {
296    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297    where
298        D: serde::Deserializer<'de>,
299    {
300        let str_repr = <String as serde::de::Deserialize>::deserialize(deserializer)?;
301        let u64_repr: Result<u64, _> = str_repr
302            .parse()
303            .map_err(|_| D::Error::custom(String::from("Failed to parse sat_amount")));
304        Ok(Self(u64_repr.unwrap()))
305    }
306}
307
308impl<'de> Deserialize<'de> for MsatAmount {
309    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
310    where
311        D: serde::Deserializer<'de>,
312    {
313        let str_repr = <String as serde::de::Deserialize>::deserialize(deserializer)?;
314        let u64_repr: Result<u64, _> = str_repr
315            .parse()
316            .map_err(|_| D::Error::custom(String::from("Failed to parse sat_amount")));
317        Ok(Self(u64_repr.unwrap()))
318    }
319}
320
321impl SatAmount {
322    pub fn checked_add(&self, other: &Self) -> Option<Self> {
323        let sat_value = self.0.checked_add(other.0)?;
324        Some(SatAmount::new(sat_value))
325    }
326}
327
328impl MsatAmount {
329    pub fn checked_add(&self, other: &Self) -> Option<Self> {
330        let sat_value = self.0.checked_add(other.0)?;
331        Some(MsatAmount::new(sat_value))
332    }
333}
334
335#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
336pub struct ShortChannelId(u64);
337
338impl Serialize for ShortChannelId {
339    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
340    where
341        S: Serializer,
342    {
343        serializer.serialize_str(&self.to_string())
344    }
345}
346
347impl<'de> Deserialize<'de> for ShortChannelId {
348    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
349    where
350        D: Deserializer<'de>,
351    {
352        use serde::de::Error;
353        let s: String = Deserialize::deserialize(deserializer)?;
354        Self::from_str(&s).map_err(|e| Error::custom(e.to_string()))
355    }
356}
357
358impl FromStr for ShortChannelId {
359    type Err = anyhow::Error;
360    fn from_str(s: &str) -> Result<Self, Self::Err> {
361        let parts: Result<Vec<u64>, _> = s.split('x').map(|p| p.parse()).collect();
362        let parts = parts.with_context(|| format!("Malformed short_channel_id: {}", s))?;
363        if parts.len() != 3 {
364            return Err(anyhow!(
365                "Malformed short_channel_id: element count mismatch"
366            ));
367        }
368
369        Ok(ShortChannelId(
370            (parts[0] << 40) | (parts[1] << 16) | parts[2],
371        ))
372    }
373}
374impl Display for ShortChannelId {
375    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
376        write!(f, "{}x{}x{}", self.block(), self.txindex(), self.outnum())
377    }
378}
379impl ShortChannelId {
380    pub fn block(&self) -> u32 {
381        (self.0 >> 40) as u32 & 0xFFFFFF
382    }
383    pub fn txindex(&self) -> u32 {
384        (self.0 >> 16) as u32 & 0xFFFFFF
385    }
386    pub fn outnum(&self) -> u16 {
387        self.0 as u16
388    }
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
392#[serde(transparent)]
393pub struct FeeRate {
394    fee_rate: u64,
395}
396
397impl FeeRate {
398    pub fn from_sats_per_kwu(sats_kwu: u64) -> Self {
399        Self { fee_rate: sats_kwu }
400    }
401
402    pub fn to_sats_per_kwu(&self) -> u64 {
403        self.fee_rate
404    }
405}
406
407#[derive(Debug, Clone, PartialEq, Eq)]
408pub struct TransactionId([u8; 32]);
409
410impl TransactionId {
411    pub fn from_slice(data: &[u8]) -> Result<Self> {
412        let txid: [u8; 32] = data.try_into().context("Invalid transaction Id")?;
413        Ok(Self(txid))
414    }
415}
416
417impl Serialize for TransactionId {
418    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
419    where
420        S: Serializer,
421    {
422        let str_value = self.to_string();
423        serializer.serialize_str(&str_value)
424    }
425}
426
427impl<'de> Deserialize<'de> for TransactionId {
428    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
429    where
430        D: Deserializer<'de>,
431    {
432        struct TransactionIdVisitor;
433
434        impl<'de> Visitor<'de> for TransactionIdVisitor {
435            type Value = TransactionId;
436
437            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
438                write!(formatter, "Valid TransactionId")
439            }
440
441            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
442            where
443                E: SeError,
444            {
445                Self::Value::from_str(v).map_err(|e| E::custom(e.to_string()))
446            }
447        }
448
449        let visitor = TransactionIdVisitor;
450        deserializer.deserialize_str(visitor)
451    }
452}
453
454impl FromStr for TransactionId {
455    type Err = anyhow::Error;
456
457    fn from_str(txid: &str) -> Result<Self> {
458        let txid = hex::decode(txid).context("txid is not valid hex")?;
459        Self::from_slice(&txid)
460    }
461}
462
463impl Display for TransactionId {
464    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
465        let txid_hex = hex::encode(self.0);
466        write!(f, "{}", txid_hex)
467    }
468}
469
470#[derive(Debug, Clone, PartialEq, Eq)]
471pub struct Outpoint {
472    pub txid: TransactionId,
473    pub outnum: u32,
474}
475
476impl Display for Outpoint {
477    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
478        write!(f, "{}:{}", self.txid, self.outnum)
479    }
480}
481
482impl FromStr for Outpoint {
483    type Err = anyhow::Error;
484
485    fn from_str(v: &str) -> Result<Self, Self::Err> {
486        let split: Vec<&str> = v.split(":").collect();
487
488        if split.len() != 2 {
489            return Err(anyhow!("Invalid outpoint. Should be a txid and outnum. e.g: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0'"));
490        }
491
492        let txid = split[0];
493        let outnum = split[1];
494
495        let txid = TransactionId::from_str(txid).context("Invalid outpoint (txid)")?;
496        let outnum = u32::from_str(outnum).context("Invalid outpoint (outnum)")?;
497
498        Ok(Self { txid, outnum })
499    }
500}
501
502impl Serialize for Outpoint {
503    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
504    where
505        S: Serializer,
506    {
507        let outnum_str = self.to_string();
508        serializer.serialize_str(&outnum_str)
509    }
510}
511
512impl<'de> Deserialize<'de> for Outpoint {
513    fn deserialize<D>(deserializer: D) -> std::prelude::v1::Result<Self, D::Error>
514    where
515        D: Deserializer<'de>,
516    {
517        struct OutnumVisitor;
518
519        impl<'de> Visitor<'de> for OutnumVisitor {
520            type Value = Outpoint;
521
522            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
523                write!(formatter, "A valid outpoint")
524            }
525
526            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
527            where
528                E: SeError,
529            {
530                Self::Value::from_str(v).map_err(|e| E::custom(e.to_string()))
531            }
532        }
533
534        let visitor = OutnumVisitor;
535        deserializer.deserialize_str(visitor)
536    }
537}
538
539#[cfg(test)]
540mod test {
541    use super::*;
542
543    #[test]
544    fn parsing_amount_sats() {
545        // Pick a number which exceeds 2^32 to ensure internal representation exceeds 32 bits
546        let json_str_number = "\"10000000001\"";
547
548        let int_number: u64 = 10000000001;
549
550        let x = serde_json::from_str::<SatAmount>(json_str_number).unwrap();
551        assert_eq!(x.sat_value(), int_number);
552    }
553
554    #[test]
555    fn serializing_amount_sats() {
556        // Pick a number which exceeds 2^32 to ensure internal representation exceeds 32 bits
557        // The json_str includes the " to indicate it is a string
558        let json_str_number = "\"10000000001\"";
559        let int_number: u64 = 10000000001;
560
561        let sat_amount = SatAmount::new(int_number);
562
563        let json_str = serde_json::to_string::<SatAmount>(&sat_amount).unwrap();
564        assert_eq!(json_str, json_str_number);
565    }
566
567    #[test]
568    fn parse_and_serialize_datetime() {
569        let datetime_str = "\"2023-01-01T23:59:59.999Z\"";
570
571        let dt = serde_json::from_str::<IsoDatetime>(datetime_str).unwrap();
572
573        assert_eq!(dt.datetime.year(), 2023);
574        assert_eq!(dt.datetime.month(), time::Month::January);
575        assert_eq!(dt.datetime.day(), 1);
576        assert_eq!(dt.datetime.hour(), 23);
577        assert_eq!(dt.datetime.minute(), 59);
578        assert_eq!(dt.datetime.second(), 59);
579
580        assert_eq!(
581            serde_json::to_string(&dt).expect("Can be serialized"),
582            datetime_str
583        )
584    }
585
586    #[test]
587    fn parse_datetime_that_doesnt_follow_spec() {
588        // The spec doesn't explicitly say that clients have to ignore datetimes that don't follow the spec
589        // However, in LSPS2 the datetime_str must be repeated verbatim
590        let datetime_str = "\"2023-01-01T23:59:59.99Z\"";
591
592        let result = serde_json::from_str::<IsoDatetime>(datetime_str);
593        result.expect_err("datetime_str should not be parsed if it doesn't follow spec");
594    }
595
596    #[test]
597    #[allow(clippy::unusual_byte_groupings)]
598    fn parse_scid_from_string() {
599        // How to read this test
600        //
601        // The shortchannel_id is 8 bytes long.
602        // The 3 first bytes are the blockheight, 3 next bytes are the txid and last 2 bytes are vout
603        // This explains the unusual byte groupings
604
605        // The string representation are the same numbers separated by the letter x
606
607        // Test the largest possible value
608        let scid_str = "16777215x16777215x65535";
609
610        let scid = ShortChannelId::from_str(scid_str).expect("The scid is parseable");
611        assert_eq!(scid.to_string(), scid_str);
612
613        // Test the smallest possible value
614        let scid_str = "0x0x0";
615
616        let scid = ShortChannelId::from_str(scid_str).expect("The scid is parseable");
617        assert_eq!(scid.to_string(), scid_str);
618
619        let scid_str = "1x2x3";
620
621        let scid = ShortChannelId::from_str(scid_str).expect("The scid is parseable");
622        assert_eq!(scid.to_string(), scid_str);
623        // A couple of unparseable scids
624        assert!(ShortChannelId::from_str("xx").is_err());
625        assert!(ShortChannelId::from_str("0x0").is_err());
626        assert!(ShortChannelId::from_str("-2x-12x14").is_err());
627    }
628
629    #[test]
630    fn short_channel_id_is_serialized_as_str() {
631        let scid: ShortChannelId = ShortChannelId::from_str("10x5x8").unwrap();
632        let scid_json_obj = serde_json::to_string(&scid).expect("Can be serialized");
633        assert_eq!("\"10x5x8\"", scid_json_obj);
634    }
635
636    #[test]
637    fn short_channel_id_can_be_deserialized_from_str() {
638        let scid_json = "\"11x12x13\"";
639
640        let scid = serde_json::from_str::<ShortChannelId>(scid_json).expect("scid can be parsed");
641
642        assert_eq!(scid, ShortChannelId::from_str("11x12x13").unwrap());
643    }
644
645    use secp256k1::Secp256k1;
646    use secp256k1::SecretKey;
647
648    #[test]
649    fn pubkey_is_serialized_as_hex() {
650        // Generate a public and private keypair
651        let mut rng = rand::thread_rng();
652        let s = Secp256k1::signing_only();
653        let secret_key = SecretKey::new(&mut rng);
654
655        let public_key = _PublicKey::from_secret_key(&s, &secret_key);
656        let pub_key_hex = hex::encode(public_key.serialize());
657
658        // Convert the to our schema object
659        let public_key = PublicKey::from(public_key);
660
661        // Serialize the string to a json_value
662        let json_value = serde_json::json!(public_key);
663
664        // Compute the compressed hex-encoded string that represents the public key
665
666        assert_eq!(
667            pub_key_hex, json_value,
668            "The key should be serialized as a compressed hex"
669        );
670    }
671
672    #[test]
673    fn deserialize_onchain_address() {
674        let regtest_address_json = "\"bcrt1qkm08480v79rzjp7tx2pjrly423ncv85k65nsmu\"";
675        let parsed = serde_json::from_str::<OnchainAddress>(regtest_address_json).unwrap();
676        parsed
677            .clone()
678            .require_network(&Network::Regtest)
679            .expect("Is okay because it is a regtest address");
680        parsed
681            .clone()
682            .require_network(&Network::Bitcoin)
683            .expect_err("Should fail because the address is not a mainnet address");
684
685        // We don't te test the NetworkChecked case here
686        // Deserializing into a NetworkChecked type
687
688        // This is a compiler error
689        //
690        // let parsed = serde_json::from_str::<OnchainAddress<NetworkChecked>>(regtest_address_json).unwrap();
691    }
692
693    #[test]
694    fn serialize_onchain_address() {
695        // Generate random key pair.
696        let address_unchecked: Address<NetworkUnchecked> =
697            "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf".parse().unwrap();
698
699        // NetworkUnchecked cannot be serialized
700        let json_value = serde_json::to_value(address_unchecked.clone()).unwrap();
701        assert_eq!(json_value, "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf");
702
703        // NetworkChecked can be serialized
704        let address_checked = address_unchecked.require_network(Network::Bitcoin).unwrap();
705        let json_value = serde_json::to_value(address_checked).unwrap();
706        assert_eq!(json_value, "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf");
707    }
708
709    #[test]
710    fn serialize_fee_rate() {
711        let min_fee = FeeRate::from_sats_per_kwu(253);
712        let json_value = serde_json::to_value(min_fee).unwrap();
713
714        assert_eq!(json_value, serde_json::json!(253));
715    }
716
717    #[test]
718    fn serialize_txid_type() {
719        let txid_str = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b";
720        let tx = TransactionId::from_str(txid_str).unwrap();
721        assert_eq!(tx.to_string(), txid_str);
722
723        let tx_deserialized: TransactionId =
724            serde_json::from_value(serde_json::json!(txid_str)).unwrap();
725        assert_eq!(
726            tx, tx_deserialized,
727            "Failed to deserialize transaction correctly"
728        );
729
730        let tx_serialized = serde_json::to_value(&tx).unwrap();
731        assert_eq!(tx_serialized, serde_json::json!(txid_str));
732    }
733
734    #[test]
735    fn serialize_outpoint() {
736        let outpoint_str = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0";
737        let outpoint = Outpoint::from_str(outpoint_str).unwrap();
738        assert_eq!(outpoint.to_string(), outpoint_str);
739
740        let outpoint_deserialized: Outpoint =
741            serde_json::from_value(serde_json::json!(outpoint_str)).unwrap();
742        assert_eq!(
743            outpoint, outpoint_deserialized,
744            "Failed to deserialize transaction correctly"
745        );
746
747        let outpoint_serialized = serde_json::to_value(&outpoint).unwrap();
748        assert_eq!(outpoint_serialized, serde_json::json!(outpoint_str));
749    }
750}