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                }
379
380                for c in &d.contract_infos {
381                    c.oracle_info.validate(secp)?;
382                }
383            }
384        }
385
386        let closest_maturity_date = self.contract_info.get_closest_maturity_date();
387        let valid_dates = self.cet_locktime <= closest_maturity_date
388            && closest_maturity_date + min_timeout_interval <= self.refund_locktime
389            && self.refund_locktime <= closest_maturity_date + max_timeout_interval;
390        if !valid_dates {
391            return Err(Error::InvalidArgument);
392        }
393
394        Ok(())
395    }
396}
397
398impl_dlc_writeable!(OfferDlc, OFFER_TYPE, {
399        (protocol_version, writeable),
400        (contract_flags, writeable),
401        (chain_hash, writeable),
402        (temporary_contract_id, writeable),
403        (contract_info, writeable),
404        (funding_pubkey, writeable),
405        (payout_spk, writeable),
406        (payout_serial_id, writeable),
407        (offer_collateral, writeable),
408        (funding_inputs, vec),
409        (change_spk, writeable),
410        (change_serial_id, writeable),
411        (fund_output_serial_id, writeable),
412        (fee_rate_per_vb, writeable),
413        (cet_locktime, writeable),
414        (refund_locktime, writeable)
415});
416
417/// Contains information about a party wishing to accept a DLC offer. The contained
418/// information is sufficient for the offering party to re-build the set of
419/// transactions representing the contract and its terms, and guarantees the offering
420/// party that they can safely provide signatures for their funding input.
421#[derive(Clone, Debug, PartialEq, Eq)]
422#[cfg_attr(
423    feature = "use-serde",
424    derive(serde::Serialize, serde::Deserialize),
425    serde(rename_all = "camelCase")
426)]
427pub struct AcceptDlc {
428    /// The version of the protocol used by the peer.
429    pub protocol_version: u32,
430    #[cfg_attr(
431        feature = "use-serde",
432        serde(
433            serialize_with = "crate::serde_utils::serialize_hex",
434            deserialize_with = "crate::serde_utils::deserialize_hex_array"
435        )
436    )]
437    /// The temporary contract id for the contract.
438    pub temporary_contract_id: [u8; 32],
439    /// The collateral input by the accept party.
440    pub accept_collateral: Amount,
441    /// The public key of the accept party to be used to lock the collateral.
442    pub funding_pubkey: PublicKey,
443    /// The SPK where the accept party will receive their payout.
444    pub payout_spk: ScriptBuf,
445    /// Serial id to order CET outputs.
446    pub payout_serial_id: u64,
447    /// Inputs used by the accept party to fund the contract.
448    pub funding_inputs: Vec<FundingInput>,
449    /// The SPK where the accept party will receive their change.
450    pub change_spk: ScriptBuf,
451    /// Serial id to order funding transaction outputs.
452    pub change_serial_id: u64,
453    /// The set of adaptor signatures from the accept party.
454    pub cet_adaptor_signatures: CetAdaptorSignatures,
455    /// The refund signature of the accept party.
456    pub refund_signature: Signature,
457    /// The negotiation fields from the accept party.
458    pub negotiation_fields: Option<NegotiationFields>,
459}
460
461impl_dlc_writeable!(AcceptDlc, ACCEPT_TYPE, {
462    (protocol_version, writeable),
463    (temporary_contract_id, writeable),
464    (accept_collateral, writeable),
465    (funding_pubkey, writeable),
466    (payout_spk, writeable),
467    (payout_serial_id, writeable),
468    (funding_inputs, vec),
469    (change_spk, writeable),
470    (change_serial_id, writeable),
471    (cet_adaptor_signatures, writeable),
472    (refund_signature, writeable),
473    (negotiation_fields, option)
474});
475
476/// Contains all the required signatures for the DLC transactions from the offering
477/// party.
478#[derive(Clone, Debug, PartialEq, Eq)]
479#[cfg_attr(
480    feature = "use-serde",
481    derive(serde::Serialize, serde::Deserialize),
482    serde(rename_all = "camelCase")
483)]
484pub struct SignDlc {
485    /// The version of the protocol used by the peer.
486    pub protocol_version: u32,
487    #[cfg_attr(
488        feature = "use-serde",
489        serde(
490            serialize_with = "crate::serde_utils::serialize_hex",
491            deserialize_with = "crate::serde_utils::deserialize_hex_array"
492        )
493    )]
494    /// The id of the contract referred to by this message.
495    pub contract_id: [u8; 32],
496    /// The set of adaptor signatures from the offer party.
497    pub cet_adaptor_signatures: CetAdaptorSignatures,
498    /// The refund signature from the offer party.
499    pub refund_signature: Signature,
500    /// The set of funding signatures from the offer party.
501    pub funding_signatures: FundingSignatures,
502}
503
504impl_dlc_writeable!(SignDlc, SIGN_TYPE, {
505    (protocol_version, writeable),
506    (contract_id, writeable),
507    (cet_adaptor_signatures, writeable),
508    (refund_signature, writeable),
509    (funding_signatures, writeable)
510});
511
512/// Contains information about a party wishing to close a DLC contract.
513#[derive(Clone, Debug, PartialEq, Eq)]
514#[cfg_attr(
515    feature = "use-serde",
516    derive(serde::Serialize, serde::Deserialize),
517    serde(rename_all = "camelCase")
518)]
519pub struct CloseDlc {
520    /// The version of the protocol used by the peer.
521    pub protocol_version: u32,
522    #[cfg_attr(
523        feature = "use-serde",
524        serde(
525            serialize_with = "crate::serde_utils::serialize_hex",
526            deserialize_with = "crate::serde_utils::deserialize_hex_array"
527        )
528    )]
529    /// The id of the contract to close.
530    pub contract_id: [u8; 32],
531    /// The signature for the closing transaction.
532    pub close_signature: Signature,
533    /// The payout amount for the accept party in satoshis.
534    pub accept_payout: Amount,
535    /// The fee rate to use to compute transaction fees for this contract.
536    pub fee_rate_per_vb: u64,
537    /// Serial id for the funding input.
538    pub fund_input_serial_id: u64,
539    /// The funding inputs to use.
540    pub funding_inputs: Vec<FundingInput>,
541    /// The funding signatures.
542    pub funding_signatures: FundingSignatures,
543}
544
545impl_dlc_writeable!(CloseDlc, CLOSE_TYPE, {
546    (protocol_version, writeable),
547    (contract_id, writeable),
548    (close_signature, writeable),
549    (accept_payout, writeable),
550    (fee_rate_per_vb, writeable),
551    (fund_input_serial_id, writeable),
552    (funding_inputs, vec),
553    (funding_signatures, writeable)
554});
555
556#[allow(missing_docs)]
557#[derive(Debug, Clone)]
558pub enum Message {
559    Offer(OfferDlc),
560    Accept(AcceptDlc),
561    Sign(SignDlc),
562    Close(CloseDlc),
563    OfferChannel(OfferChannel),
564    AcceptChannel(AcceptChannel),
565    SignChannel(SignChannel),
566    SettleOffer(SettleOffer),
567    SettleAccept(SettleAccept),
568    SettleConfirm(SettleConfirm),
569    SettleFinalize(SettleFinalize),
570    RenewOffer(RenewOffer),
571    RenewAccept(RenewAccept),
572    RenewConfirm(RenewConfirm),
573    RenewFinalize(RenewFinalize),
574    RenewRevoke(RenewRevoke),
575    CollaborativeCloseOffer(CollaborativeCloseOffer),
576    Reject(Reject),
577}
578
579macro_rules! impl_type_writeable_for_enum {
580    ($type_name: ident, {$($variant_name: ident),*}) => {
581       impl Type for $type_name {
582           fn type_id(&self) -> u16 {
583               match self {
584                   $($type_name::$variant_name(v) => v.type_id(),)*
585               }
586           }
587       }
588
589       impl Writeable for $type_name {
590            fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::lightning::io::Error> {
591                match self {
592                   $($type_name::$variant_name(v) => v.write(writer),)*
593                }
594            }
595       }
596    };
597}
598
599impl_type_writeable_for_enum!(Message,
600{
601    Offer,
602    Accept,
603    Sign,
604    Close,
605    OfferChannel,
606    AcceptChannel,
607    SignChannel,
608    SettleOffer,
609    SettleAccept,
610    SettleConfirm,
611    SettleFinalize,
612    RenewOffer,
613    RenewAccept,
614    RenewConfirm,
615    RenewFinalize,
616    RenewRevoke,
617    CollaborativeCloseOffer,
618    Reject
619});
620
621#[derive(Debug, Clone)]
622/// Wrapper for DLC related message and segmentation related messages.
623#[allow(clippy::large_enum_variant)]
624pub enum WireMessage {
625    /// Message related to establishment of a DLC contract.
626    Message(Message),
627    /// Message indicating an incoming segmented message.
628    SegmentStart(SegmentStart),
629    /// Message providing a chunk of a segmented message.
630    SegmentChunk(SegmentChunk),
631}
632
633impl Display for WireMessage {
634    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
635        let name = match self {
636            Self::Message(_) => "Message",
637            Self::SegmentStart(_) => "SegmentStart",
638            Self::SegmentChunk(_) => "SegmentChunk",
639        };
640        f.write_str(name)
641    }
642}
643
644impl_type_writeable_for_enum!(WireMessage, { Message, SegmentStart, SegmentChunk });
645
646#[cfg(test)]
647mod tests {
648    use secp256k1_zkp::SECP256K1;
649
650    use super::*;
651
652    macro_rules! roundtrip_test {
653        ($type: ty, $input: ident) => {
654            let msg: $type = serde_json::from_str(&$input).unwrap();
655            test_roundtrip(msg);
656        };
657    }
658
659    fn test_roundtrip<T: Writeable + Readable + PartialEq + std::fmt::Debug>(msg: T) {
660        let mut buf = Vec::new();
661        msg.write(&mut buf).expect("Error writing message");
662        let mut cursor = lightning::io::Cursor::new(buf);
663        let deser = Readable::read(&mut cursor).expect("Error reading message");
664        assert_eq!(msg, deser);
665    }
666
667    #[test]
668    fn offer_msg_roundtrip() {
669        let input = include_str!("./test_inputs/offer_msg.json");
670        roundtrip_test!(OfferDlc, input);
671    }
672
673    #[test]
674    fn accept_msg_roundtrip() {
675        let input = include_str!("./test_inputs/accept_msg.json");
676        roundtrip_test!(AcceptDlc, input);
677    }
678
679    #[test]
680    fn sign_msg_roundtrip() {
681        let input = include_str!("./test_inputs/sign_msg.json");
682        roundtrip_test!(SignDlc, input);
683    }
684
685    #[test]
686    fn close_msg_roundtrip() {
687        let input = include_str!("./test_inputs/close_msg.json");
688        roundtrip_test!(CloseDlc, input);
689    }
690
691    #[test]
692    fn valid_offer_message_passes_validation() {
693        let input = include_str!("./test_inputs/offer_msg.json");
694        let valid_offer: OfferDlc = serde_json::from_str(input).unwrap();
695        valid_offer
696            .validate(SECP256K1, 86400 * 7, 86400 * 14)
697            .expect("to validate valid offer messages.");
698    }
699
700    #[test]
701    fn valid_offer_message_passes_with_dlc_input() {
702        let input = include_str!("./test_inputs/offer_msg_with_dlc_input.json");
703        let valid_offer: OfferDlc = serde_json::from_str(input).unwrap();
704
705        for input in &valid_offer.funding_inputs {
706            assert!(input.dlc_input.is_some());
707        }
708        valid_offer
709            .validate(SECP256K1, 86400 * 7, 86400 * 14)
710            .expect("to validate valid offer messages.");
711    }
712
713    #[test]
714    fn invalid_offer_messages_fail_validation() {
715        let input = include_str!("./test_inputs/offer_msg.json");
716        let offer: OfferDlc = serde_json::from_str(input).unwrap();
717
718        let mut invalid_maturity = offer.clone();
719        invalid_maturity.cet_locktime += 3;
720
721        let mut too_short_timeout = offer.clone();
722        too_short_timeout.refund_locktime -= 100;
723
724        let mut too_long_timeout = offer;
725        too_long_timeout.refund_locktime -= 100;
726
727        for invalid in &[invalid_maturity, too_short_timeout, too_long_timeout] {
728            invalid
729                .validate(SECP256K1, 86400 * 7, 86400 * 14)
730                .expect_err("Should not pass validation of invalid offer message.");
731        }
732    }
733
734    #[test]
735    fn disjoint_contract_offer_messages_fail_validation() {
736        let input = include_str!("./test_inputs/offer_msg_disjoint.json");
737        let offer: OfferDlc = serde_json::from_str(input).unwrap();
738
739        let mut no_contract_input = offer.clone();
740        no_contract_input.contract_info =
741            ContractInfo::DisjointContractInfo(contract_msgs::DisjointContractInfo {
742                total_collateral: Amount::ONE_BTC,
743                contract_infos: vec![],
744            });
745
746        let mut single_contract_input = offer.clone();
747        single_contract_input.contract_info =
748            if let ContractInfo::DisjointContractInfo(d) = offer.contract_info {
749                let mut single = d;
750                single.contract_infos.remove(1);
751                ContractInfo::DisjointContractInfo(single)
752            } else {
753                panic!("Expected disjoint contract info.");
754            };
755
756        for invalid in &[no_contract_input, single_contract_input] {
757            invalid
758                .validate(SECP256K1, 86400 * 7, 86400 * 14)
759                .expect_err("Should not pass validation of invalid offer message.");
760        }
761    }
762}