ddk_messages/
lib.rs

1//! Data structure and functions related to peer communication.
2
3// Coding conventions
4#![forbid(unsafe_code)]
5#![deny(non_upper_case_globals)]
6#![deny(non_camel_case_types)]
7#![deny(non_snake_case)]
8#![deny(unused_mut)]
9#![deny(dead_code)]
10#![deny(unused_imports)]
11#![deny(missing_docs)]
12
13extern crate bitcoin;
14extern crate ddk_dlc;
15extern crate lightning;
16extern crate secp256k1_zkp;
17#[macro_use]
18pub mod ser_macros;
19pub mod ser_impls;
20
21#[cfg(any(test, feature = "use-serde"))]
22extern crate serde;
23
24#[cfg(test)]
25extern crate serde_json;
26
27pub mod channel;
28pub mod contract_msgs;
29pub mod message_handler;
30pub mod oracle_msgs;
31pub mod segmentation;
32pub mod types;
33
34#[cfg(any(test, feature = "use-serde"))]
35pub mod serde_utils;
36
37use std::fmt::Display;
38
39use crate::ser_impls::{read_ecdsa_adaptor_signature, write_ecdsa_adaptor_signature};
40use crate::types::*;
41use bitcoin::{consensus::Decodable, OutPoint, Transaction};
42use bitcoin::{Amount, ScriptBuf};
43use channel::{
44    AcceptChannel, CollaborativeCloseOffer, OfferChannel, Reject, RenewAccept, RenewConfirm,
45    RenewFinalize, RenewOffer, RenewRevoke, SettleAccept, SettleConfirm, SettleFinalize,
46    SettleOffer, SignChannel,
47};
48use contract_msgs::ContractInfo;
49use ddk_dlc::dlc_input::DlcInputInfo;
50use ddk_dlc::{Error, TxInputInfo};
51use lightning::ln::msgs::DecodeError;
52use lightning::ln::wire::Type;
53use lightning::util::ser::{Readable, Writeable, Writer};
54use secp256k1_zkp::Verification;
55use secp256k1_zkp::{ecdsa::Signature, EcdsaAdaptorSignature, PublicKey, Secp256k1};
56use segmentation::{SegmentChunk, SegmentStart};
57
58#[derive(Clone, Debug, PartialEq, Eq)]
59#[cfg_attr(
60    feature = "use-serde",
61    derive(serde::Serialize, serde::Deserialize),
62    serde(rename_all = "camelCase")
63)]
64/// Contains information about a DLC input to be used in a funding transaction.
65pub struct DlcInput {
66    /// The local funding public key.
67    pub local_fund_pubkey: PublicKey,
68    /// The remote funding public key.
69    pub remote_fund_pubkey: PublicKey,
70    /// Contract id of the DLC input.
71    pub contract_id: [u8; 32],
72}
73
74impl_dlc_writeable!(DlcInput, {
75    (local_fund_pubkey, writeable),
76    (remote_fund_pubkey, writeable),
77    (contract_id, writeable)
78});
79
80#[derive(Clone, Debug, PartialEq, Eq)]
81#[cfg_attr(
82    feature = "use-serde",
83    derive(serde::Serialize, serde::Deserialize),
84    serde(rename_all = "camelCase")
85)]
86/// Contains information about a specific input to be used in a funding transaction,
87/// as well as its corresponding on-chain UTXO.
88pub struct FundingInput {
89    /// Serial id used for input ordering in the funding transaction.
90    pub input_serial_id: u64,
91    #[cfg_attr(
92        feature = "use-serde",
93        serde(
94            serialize_with = "crate::serde_utils::serialize_hex",
95            deserialize_with = "crate::serde_utils::deserialize_hex_string"
96        )
97    )]
98    /// The previous transaction used by the associated input in serialized format.
99    pub prev_tx: Vec<u8>,
100    /// The vout of the output used by the associated input.
101    pub prev_tx_vout: u32,
102    /// The sequence number to use for the input.
103    pub sequence: u32,
104    /// The maximum witness length that can be used to spend the previous UTXO.
105    pub max_witness_len: u16,
106    /// The redeem script of the previous UTXO.
107    pub redeem_script: ScriptBuf,
108    /// The optional sub-type of including a DLC input.
109    pub dlc_input: Option<DlcInput>,
110}
111
112impl_dlc_writeable!(FundingInput, {
113    (input_serial_id, writeable),
114    (prev_tx, vec),
115    (prev_tx_vout, writeable),
116    (sequence, writeable),
117    (max_witness_len, writeable),
118    (redeem_script, writeable),
119    (dlc_input, option)
120});
121
122impl From<&FundingInput> for TxInputInfo {
123    fn from(funding_input: &FundingInput) -> TxInputInfo {
124        TxInputInfo {
125            outpoint: OutPoint {
126                txid: Transaction::consensus_decode(&mut funding_input.prev_tx.as_slice())
127                    .expect("Transaction Decode Error")
128                    .compute_txid(),
129                vout: funding_input.prev_tx_vout,
130            },
131            max_witness_len: (funding_input.max_witness_len as usize),
132            redeem_script: funding_input.redeem_script.clone(),
133            serial_id: funding_input.input_serial_id,
134        }
135    }
136}
137
138impl From<&FundingInput> for DlcInputInfo {
139    fn from(funding_input: &FundingInput) -> Self {
140        let fund_tx = Transaction::consensus_decode(&mut funding_input.prev_tx.as_slice()).unwrap();
141        Self {
142            fund_tx: fund_tx.clone(),
143            fund_vout: funding_input.prev_tx_vout,
144            local_fund_pubkey: funding_input.dlc_input.as_ref().unwrap().local_fund_pubkey,
145            remote_fund_pubkey: funding_input.dlc_input.as_ref().unwrap().remote_fund_pubkey,
146            fund_amount: fund_tx.output[funding_input.prev_tx_vout as usize].value,
147            max_witness_len: funding_input.max_witness_len as usize,
148            input_serial_id: funding_input.input_serial_id,
149            contract_id: funding_input.dlc_input.as_ref().unwrap().contract_id,
150        }
151    }
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
155#[cfg_attr(
156    feature = "use-serde",
157    derive(serde::Serialize, serde::Deserialize),
158    serde(rename_all = "camelCase")
159)]
160/// Contains an adaptor signature for a CET input and its associated DLEQ proof.
161pub struct CetAdaptorSignature {
162    /// The signature.
163    pub signature: EcdsaAdaptorSignature,
164}
165
166impl_dlc_writeable!(CetAdaptorSignature, {
167     (signature, { cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature })
168});
169
170#[derive(Clone, Debug, PartialEq, Eq)]
171#[cfg_attr(
172    feature = "use-serde",
173    derive(serde::Serialize, serde::Deserialize),
174    serde(rename_all = "camelCase")
175)]
176/// Contains a list of adaptor signature for a number of CET inputs.
177pub struct CetAdaptorSignatures {
178    /// The set of signatures.
179    pub ecdsa_adaptor_signatures: Vec<CetAdaptorSignature>,
180}
181
182impl From<&[EcdsaAdaptorSignature]> for CetAdaptorSignatures {
183    fn from(signatures: &[EcdsaAdaptorSignature]) -> Self {
184        CetAdaptorSignatures {
185            ecdsa_adaptor_signatures: signatures
186                .iter()
187                .map(|x| CetAdaptorSignature { signature: *x })
188                .collect(),
189        }
190    }
191}
192
193impl From<&CetAdaptorSignatures> for Vec<EcdsaAdaptorSignature> {
194    fn from(signatures: &CetAdaptorSignatures) -> Vec<EcdsaAdaptorSignature> {
195        signatures
196            .ecdsa_adaptor_signatures
197            .iter()
198            .map(|x| x.signature)
199            .collect::<Vec<_>>()
200    }
201}
202
203impl_dlc_writeable!(CetAdaptorSignatures, { (ecdsa_adaptor_signatures, vec) });
204
205#[derive(Clone, Debug, PartialEq, Eq)]
206#[cfg_attr(
207    feature = "use-serde",
208    derive(serde::Serialize, serde::Deserialize),
209    serde(rename_all = "camelCase")
210)]
211/// Contains the witness elements to use to make a funding transaction input valid.
212pub struct FundingSignature {
213    /// The set of witness elements.
214    pub witness_elements: Vec<WitnessElement>,
215}
216
217impl_dlc_writeable!(FundingSignature, { (witness_elements, vec) });
218
219#[derive(Clone, Debug, PartialEq, Eq)]
220#[cfg_attr(
221    feature = "use-serde",
222    derive(serde::Serialize, serde::Deserialize),
223    serde(rename_all = "camelCase")
224)]
225/// Contains a list of witness elements to satisfy the spending conditions of
226/// funding inputs.
227pub struct FundingSignatures {
228    /// The set of funding signatures.
229    pub funding_signatures: Vec<FundingSignature>,
230}
231
232impl_dlc_writeable!(FundingSignatures, { (funding_signatures, vec) });
233
234#[derive(Clone, Debug, PartialEq, Eq)]
235#[cfg_attr(
236    feature = "use-serde",
237    derive(serde::Serialize, serde::Deserialize),
238    serde(rename_all = "camelCase")
239)]
240/// Contains serialized data representing a single witness stack element.
241pub struct WitnessElement {
242    #[cfg_attr(
243        feature = "use-serde",
244        serde(
245            serialize_with = "crate::serde_utils::serialize_hex",
246            deserialize_with = "crate::serde_utils::deserialize_hex_string"
247        )
248    )]
249    /// The serialized witness data.
250    pub witness: Vec<u8>,
251}
252
253impl_dlc_writeable!(WitnessElement, { (witness, vec) });
254
255#[derive(Clone, Debug, PartialEq, Eq)]
256#[cfg_attr(
257    feature = "use-serde",
258    derive(serde::Serialize, serde::Deserialize),
259    serde(rename_all = "camelCase")
260)]
261/// Fields used to negotiate contract information.
262pub enum NegotiationFields {
263    /// Negotiation for single event based contract.
264    Single(SingleNegotiationFields),
265    /// Negotiation for multiple event based contract.
266    Disjoint(DisjointNegotiationFields),
267}
268
269impl_dlc_writeable_enum!(NegotiationFields, (0, Single), (1, Disjoint);;;);
270
271#[derive(Clone, Debug, PartialEq, Eq)]
272#[cfg_attr(
273    feature = "use-serde",
274    derive(serde::Serialize, serde::Deserialize),
275    serde(rename_all = "camelCase")
276)]
277/// Negotiation fields for contract based on a single event.
278pub struct SingleNegotiationFields {
279    /// Proposed rounding intervals.
280    rounding_intervals: contract_msgs::RoundingIntervals,
281}
282
283impl_dlc_writeable!(SingleNegotiationFields, { (rounding_intervals, writeable) });
284
285#[derive(Clone, Debug, PartialEq, Eq)]
286#[cfg_attr(
287    feature = "use-serde",
288    derive(serde::Serialize, serde::Deserialize),
289    serde(rename_all = "camelCase")
290)]
291/// Negotiation fields for contract based on multiple events.
292pub struct DisjointNegotiationFields {
293    /// The negotiation fields for each contract event.
294    negotiation_fields: Vec<NegotiationFields>,
295}
296
297impl_dlc_writeable!(DisjointNegotiationFields, { (negotiation_fields, vec) });
298
299#[derive(Clone, Debug, PartialEq)]
300#[cfg_attr(
301    feature = "use-serde",
302    derive(serde::Serialize, serde::Deserialize),
303    serde(rename_all = "camelCase")
304)]
305/// Contains information about a party wishing to enter into a DLC with
306/// another party. The contained information is sufficient for any other party
307/// to create a set of transactions representing the contract and its terms.
308pub struct OfferDlc {
309    /// The version of the protocol used by the peer.
310    pub protocol_version: u32,
311    /// Feature flags to be used for the offered contract.
312    pub contract_flags: u8,
313    #[cfg_attr(
314        feature = "use-serde",
315        serde(
316            serialize_with = "crate::serde_utils::serialize_hex",
317            deserialize_with = "crate::serde_utils::deserialize_hex_array"
318        )
319    )]
320    /// The identifier of the chain on which the contract will be settled.
321    pub chain_hash: [u8; 32],
322    #[cfg_attr(
323        feature = "use-serde",
324        serde(
325            serialize_with = "crate::serde_utils::serialize_hex",
326            deserialize_with = "crate::serde_utils::deserialize_hex_array"
327        )
328    )]
329    /// Temporary contract id to identify the contract.
330    pub temporary_contract_id: [u8; 32],
331    /// Information about the contract event, payouts and oracles.
332    pub contract_info: ContractInfo,
333    /// The public key of the offerer to be used to lock the collateral.
334    pub funding_pubkey: PublicKey,
335    /// The SPK where the offerer will receive their payout.
336    pub payout_spk: ScriptBuf,
337    /// Serial id to order CET outputs.
338    pub payout_serial_id: u64,
339    /// Collateral of the offer party.
340    pub offer_collateral: Amount,
341    /// Inputs used by the offer party to fund the contract.
342    pub funding_inputs: Vec<FundingInput>,
343    /// The SPK where the offer party will receive their change.
344    pub change_spk: ScriptBuf,
345    /// Serial id to order funding transaction outputs.
346    pub change_serial_id: u64,
347    /// Serial id to order funding transaction outputs.
348    pub fund_output_serial_id: u64,
349    /// The fee rate to use to compute transaction fees for this contract.
350    pub fee_rate_per_vb: u64,
351    /// The lock time for the CETs.
352    pub cet_locktime: u32,
353    /// The lock time for the refund transactions.
354    pub refund_locktime: u32,
355}
356
357impl OfferDlc {
358    /// Returns the total collateral locked in the contract.
359    pub fn get_total_collateral(&self) -> Amount {
360        match &self.contract_info {
361            ContractInfo::SingleContractInfo(single) => single.total_collateral,
362            ContractInfo::DisjointContractInfo(disjoint) => disjoint.total_collateral,
363        }
364    }
365
366    /// Returns whether the message satisfies validity requirements.
367    pub fn validate<C: Verification>(
368        &self,
369        secp: &Secp256k1<C>,
370        min_timeout_interval: u32,
371        max_timeout_interval: u32,
372    ) -> Result<(), Error> {
373        match &self.contract_info {
374            ContractInfo::SingleContractInfo(s) => s.contract_info.oracle_info.validate(secp)?,
375            ContractInfo::DisjointContractInfo(d) => {
376                if d.contract_infos.len() < 2 {
377                    return Err(Error::InvalidArgument(
378                        "Need at least two contract infos for disjoint contract".to_string(),
379                    ));
380                }
381
382                for c in &d.contract_infos {
383                    c.oracle_info.validate(secp)?;
384                }
385            }
386        }
387
388        let closest_maturity_date = self.contract_info.get_closest_maturity_date();
389        let valid_dates = self.cet_locktime <= closest_maturity_date
390            && closest_maturity_date + min_timeout_interval <= self.refund_locktime
391            && self.refund_locktime <= closest_maturity_date + max_timeout_interval;
392        if !valid_dates {
393            return Err(Error::InvalidArgument(
394                "Locktime is less than closest maturity date".to_string(),
395            ));
396        }
397
398        Ok(())
399    }
400}
401
402impl_dlc_writeable!(OfferDlc, OFFER_TYPE, {
403        (protocol_version, writeable),
404        (contract_flags, writeable),
405        (chain_hash, writeable),
406        (temporary_contract_id, writeable),
407        (contract_info, writeable),
408        (funding_pubkey, writeable),
409        (payout_spk, writeable),
410        (payout_serial_id, writeable),
411        (offer_collateral, writeable),
412        (funding_inputs, vec),
413        (change_spk, writeable),
414        (change_serial_id, writeable),
415        (fund_output_serial_id, writeable),
416        (fee_rate_per_vb, writeable),
417        (cet_locktime, writeable),
418        (refund_locktime, writeable)
419});
420
421/// Contains information about a party wishing to accept a DLC offer. The contained
422/// information is sufficient for the offering party to re-build the set of
423/// transactions representing the contract and its terms, and guarantees the offering
424/// party that they can safely provide signatures for their funding input.
425#[derive(Clone, Debug, PartialEq, Eq)]
426#[cfg_attr(
427    feature = "use-serde",
428    derive(serde::Serialize, serde::Deserialize),
429    serde(rename_all = "camelCase")
430)]
431pub struct AcceptDlc {
432    /// The version of the protocol used by the peer.
433    pub protocol_version: u32,
434    #[cfg_attr(
435        feature = "use-serde",
436        serde(
437            serialize_with = "crate::serde_utils::serialize_hex",
438            deserialize_with = "crate::serde_utils::deserialize_hex_array"
439        )
440    )]
441    /// The temporary contract id for the contract.
442    pub temporary_contract_id: [u8; 32],
443    /// The collateral input by the accept party.
444    pub accept_collateral: Amount,
445    /// The public key of the accept party to be used to lock the collateral.
446    pub funding_pubkey: PublicKey,
447    /// The SPK where the accept party will receive their payout.
448    pub payout_spk: ScriptBuf,
449    /// Serial id to order CET outputs.
450    pub payout_serial_id: u64,
451    /// Inputs used by the accept party to fund the contract.
452    pub funding_inputs: Vec<FundingInput>,
453    /// The SPK where the accept party will receive their change.
454    pub change_spk: ScriptBuf,
455    /// Serial id to order funding transaction outputs.
456    pub change_serial_id: u64,
457    /// The set of adaptor signatures from the accept party.
458    pub cet_adaptor_signatures: CetAdaptorSignatures,
459    /// The refund signature of the accept party.
460    pub refund_signature: Signature,
461    /// The negotiation fields from the accept party.
462    pub negotiation_fields: Option<NegotiationFields>,
463}
464
465impl_dlc_writeable!(AcceptDlc, ACCEPT_TYPE, {
466    (protocol_version, writeable),
467    (temporary_contract_id, writeable),
468    (accept_collateral, writeable),
469    (funding_pubkey, writeable),
470    (payout_spk, writeable),
471    (payout_serial_id, writeable),
472    (funding_inputs, vec),
473    (change_spk, writeable),
474    (change_serial_id, writeable),
475    (cet_adaptor_signatures, writeable),
476    (refund_signature, writeable),
477    (negotiation_fields, option)
478});
479
480/// Contains all the required signatures for the DLC transactions from the offering
481/// party.
482#[derive(Clone, Debug, PartialEq, Eq)]
483#[cfg_attr(
484    feature = "use-serde",
485    derive(serde::Serialize, serde::Deserialize),
486    serde(rename_all = "camelCase")
487)]
488pub struct SignDlc {
489    /// The version of the protocol used by the peer.
490    pub protocol_version: u32,
491    #[cfg_attr(
492        feature = "use-serde",
493        serde(
494            serialize_with = "crate::serde_utils::serialize_hex",
495            deserialize_with = "crate::serde_utils::deserialize_hex_array"
496        )
497    )]
498    /// The id of the contract referred to by this message.
499    pub contract_id: [u8; 32],
500    /// The set of adaptor signatures from the offer party.
501    pub cet_adaptor_signatures: CetAdaptorSignatures,
502    /// The refund signature from the offer party.
503    pub refund_signature: Signature,
504    /// The set of funding signatures from the offer party.
505    pub funding_signatures: FundingSignatures,
506}
507
508impl_dlc_writeable!(SignDlc, SIGN_TYPE, {
509    (protocol_version, writeable),
510    (contract_id, writeable),
511    (cet_adaptor_signatures, writeable),
512    (refund_signature, writeable),
513    (funding_signatures, writeable)
514});
515
516/// Contains information about a party wishing to close a DLC contract.
517#[derive(Clone, Debug, PartialEq, Eq)]
518#[cfg_attr(
519    feature = "use-serde",
520    derive(serde::Serialize, serde::Deserialize),
521    serde(rename_all = "camelCase")
522)]
523pub struct CloseDlc {
524    /// The version of the protocol used by the peer.
525    pub protocol_version: u32,
526    #[cfg_attr(
527        feature = "use-serde",
528        serde(
529            serialize_with = "crate::serde_utils::serialize_hex",
530            deserialize_with = "crate::serde_utils::deserialize_hex_array"
531        )
532    )]
533    /// The id of the contract to close.
534    pub contract_id: [u8; 32],
535    /// The signature for the closing transaction.
536    pub close_signature: Signature,
537    /// The payout amount for the accept party in satoshis.
538    pub accept_payout: Amount,
539    /// The fee rate to use to compute transaction fees for this contract.
540    pub fee_rate_per_vb: u64,
541    /// Serial id for the funding input.
542    pub fund_input_serial_id: u64,
543    /// The funding inputs to use.
544    pub funding_inputs: Vec<FundingInput>,
545    /// The funding signatures.
546    pub funding_signatures: FundingSignatures,
547}
548
549impl_dlc_writeable!(CloseDlc, CLOSE_TYPE, {
550    (protocol_version, writeable),
551    (contract_id, writeable),
552    (close_signature, writeable),
553    (accept_payout, writeable),
554    (fee_rate_per_vb, writeable),
555    (fund_input_serial_id, writeable),
556    (funding_inputs, vec),
557    (funding_signatures, writeable)
558});
559
560#[allow(missing_docs)]
561#[derive(Debug, Clone)]
562pub enum Message {
563    Offer(OfferDlc),
564    Accept(AcceptDlc),
565    Sign(SignDlc),
566    Close(CloseDlc),
567    OfferChannel(OfferChannel),
568    AcceptChannel(AcceptChannel),
569    SignChannel(SignChannel),
570    SettleOffer(SettleOffer),
571    SettleAccept(SettleAccept),
572    SettleConfirm(SettleConfirm),
573    SettleFinalize(SettleFinalize),
574    RenewOffer(RenewOffer),
575    RenewAccept(RenewAccept),
576    RenewConfirm(RenewConfirm),
577    RenewFinalize(RenewFinalize),
578    RenewRevoke(RenewRevoke),
579    CollaborativeCloseOffer(CollaborativeCloseOffer),
580    Reject(Reject),
581}
582
583macro_rules! impl_type_writeable_for_enum {
584    ($type_name: ident, {$($variant_name: ident),*}) => {
585       impl Type for $type_name {
586           fn type_id(&self) -> u16 {
587               match self {
588                   $($type_name::$variant_name(v) => v.type_id(),)*
589               }
590           }
591       }
592
593       impl Writeable for $type_name {
594            fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::lightning::io::Error> {
595                match self {
596                   $($type_name::$variant_name(v) => v.write(writer),)*
597                }
598            }
599       }
600    };
601}
602
603impl_type_writeable_for_enum!(Message,
604{
605    Offer,
606    Accept,
607    Sign,
608    Close,
609    OfferChannel,
610    AcceptChannel,
611    SignChannel,
612    SettleOffer,
613    SettleAccept,
614    SettleConfirm,
615    SettleFinalize,
616    RenewOffer,
617    RenewAccept,
618    RenewConfirm,
619    RenewFinalize,
620    RenewRevoke,
621    CollaborativeCloseOffer,
622    Reject
623});
624
625#[derive(Debug, Clone)]
626/// Wrapper for DLC related message and segmentation related messages.
627#[allow(clippy::large_enum_variant)]
628pub enum WireMessage {
629    /// Message related to establishment of a DLC contract.
630    Message(Message),
631    /// Message indicating an incoming segmented message.
632    SegmentStart(SegmentStart),
633    /// Message providing a chunk of a segmented message.
634    SegmentChunk(SegmentChunk),
635}
636
637impl Display for WireMessage {
638    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
639        let name = match self {
640            Self::Message(_) => "Message",
641            Self::SegmentStart(_) => "SegmentStart",
642            Self::SegmentChunk(_) => "SegmentChunk",
643        };
644        f.write_str(name)
645    }
646}
647
648impl_type_writeable_for_enum!(WireMessage, { Message, SegmentStart, SegmentChunk });
649
650#[cfg(test)]
651mod tests {
652    use secp256k1_zkp::SECP256K1;
653
654    use super::*;
655
656    macro_rules! roundtrip_test {
657        ($type: ty, $input: ident) => {
658            let msg: $type = serde_json::from_str(&$input).unwrap();
659            test_roundtrip(msg);
660        };
661    }
662
663    fn test_roundtrip<T: Writeable + Readable + PartialEq + std::fmt::Debug>(msg: T) {
664        let mut buf = Vec::new();
665        msg.write(&mut buf).expect("Error writing message");
666        let mut cursor = lightning::io::Cursor::new(buf);
667        let deser = Readable::read(&mut cursor).expect("Error reading message");
668        assert_eq!(msg, deser);
669    }
670
671    #[test]
672    fn offer_msg_roundtrip() {
673        let input = include_str!("./test_inputs/offer_msg.json");
674        roundtrip_test!(OfferDlc, input);
675    }
676
677    #[test]
678    fn accept_msg_roundtrip() {
679        let input = include_str!("./test_inputs/accept_msg.json");
680        roundtrip_test!(AcceptDlc, input);
681    }
682
683    #[test]
684    fn sign_msg_roundtrip() {
685        let input = include_str!("./test_inputs/sign_msg.json");
686        roundtrip_test!(SignDlc, input);
687    }
688
689    #[test]
690    fn close_msg_roundtrip() {
691        let input = include_str!("./test_inputs/close_msg.json");
692        roundtrip_test!(CloseDlc, input);
693    }
694
695    #[test]
696    fn valid_offer_message_passes_validation() {
697        let input = include_str!("./test_inputs/offer_msg.json");
698        let valid_offer: OfferDlc = serde_json::from_str(input).unwrap();
699        valid_offer
700            .validate(SECP256K1, 86400 * 7, 86400 * 14)
701            .expect("to validate valid offer messages.");
702    }
703
704    #[test]
705    fn valid_offer_message_passes_with_dlc_input() {
706        let input = include_str!("./test_inputs/offer_msg_with_dlc_input.json");
707        let valid_offer: OfferDlc = serde_json::from_str(input).unwrap();
708
709        for input in &valid_offer.funding_inputs {
710            assert!(input.dlc_input.is_some());
711        }
712        valid_offer
713            .validate(SECP256K1, 86400 * 7, 86400 * 14)
714            .expect("to validate valid offer messages.");
715    }
716
717    #[test]
718    fn invalid_offer_messages_fail_validation() {
719        let input = include_str!("./test_inputs/offer_msg.json");
720        let offer: OfferDlc = serde_json::from_str(input).unwrap();
721
722        let mut invalid_maturity = offer.clone();
723        invalid_maturity.cet_locktime += 3;
724
725        let mut too_short_timeout = offer.clone();
726        too_short_timeout.refund_locktime -= 100;
727
728        let mut too_long_timeout = offer;
729        too_long_timeout.refund_locktime -= 100;
730
731        for invalid in &[invalid_maturity, too_short_timeout, too_long_timeout] {
732            invalid
733                .validate(SECP256K1, 86400 * 7, 86400 * 14)
734                .expect_err("Should not pass validation of invalid offer message.");
735        }
736    }
737
738    #[test]
739    fn disjoint_contract_offer_messages_fail_validation() {
740        let input = include_str!("./test_inputs/offer_msg_disjoint.json");
741        let offer: OfferDlc = serde_json::from_str(input).unwrap();
742
743        let mut no_contract_input = offer.clone();
744        no_contract_input.contract_info =
745            ContractInfo::DisjointContractInfo(contract_msgs::DisjointContractInfo {
746                total_collateral: Amount::ONE_BTC,
747                contract_infos: vec![],
748            });
749
750        let mut single_contract_input = offer.clone();
751        single_contract_input.contract_info =
752            if let ContractInfo::DisjointContractInfo(d) = offer.contract_info {
753                let mut single = d;
754                single.contract_infos.remove(1);
755                ContractInfo::DisjointContractInfo(single)
756            } else {
757                panic!("Expected disjoint contract info.");
758            };
759
760        for invalid in &[no_contract_input, single_contract_input] {
761            invalid
762                .validate(SECP256K1, 86400 * 7, 86400 * 14)
763                .expect_err("Should not pass validation of invalid offer message.");
764        }
765    }
766}