1#![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)]
64pub struct DlcInput {
66 pub local_fund_pubkey: PublicKey,
68 pub remote_fund_pubkey: PublicKey,
70 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)]
86pub struct FundingInput {
89 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 pub prev_tx: Vec<u8>,
100 pub prev_tx_vout: u32,
102 pub sequence: u32,
104 pub max_witness_len: u16,
106 pub redeem_script: ScriptBuf,
108 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)]
160pub struct CetAdaptorSignature {
162 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)]
176pub struct CetAdaptorSignatures {
178 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)]
211pub struct FundingSignature {
213 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)]
225pub struct FundingSignatures {
228 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)]
240pub 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 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)]
261pub enum NegotiationFields {
263 Single(SingleNegotiationFields),
265 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)]
277pub struct SingleNegotiationFields {
279 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)]
291pub struct DisjointNegotiationFields {
293 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)]
305pub struct OfferDlc {
309 pub protocol_version: u32,
311 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 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 pub temporary_contract_id: [u8; 32],
331 pub contract_info: ContractInfo,
333 pub funding_pubkey: PublicKey,
335 pub payout_spk: ScriptBuf,
337 pub payout_serial_id: u64,
339 pub offer_collateral: Amount,
341 pub funding_inputs: Vec<FundingInput>,
343 pub change_spk: ScriptBuf,
345 pub change_serial_id: u64,
347 pub fund_output_serial_id: u64,
349 pub fee_rate_per_vb: u64,
351 pub cet_locktime: u32,
353 pub refund_locktime: u32,
355}
356
357impl OfferDlc {
358 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 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#[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 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 pub temporary_contract_id: [u8; 32],
443 pub accept_collateral: Amount,
445 pub funding_pubkey: PublicKey,
447 pub payout_spk: ScriptBuf,
449 pub payout_serial_id: u64,
451 pub funding_inputs: Vec<FundingInput>,
453 pub change_spk: ScriptBuf,
455 pub change_serial_id: u64,
457 pub cet_adaptor_signatures: CetAdaptorSignatures,
459 pub refund_signature: Signature,
461 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#[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 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 pub contract_id: [u8; 32],
500 pub cet_adaptor_signatures: CetAdaptorSignatures,
502 pub refund_signature: Signature,
504 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#[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 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 pub contract_id: [u8; 32],
535 pub close_signature: Signature,
537 pub accept_payout: Amount,
539 pub fee_rate_per_vb: u64,
541 pub fund_input_serial_id: u64,
543 pub funding_inputs: Vec<FundingInput>,
545 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#[allow(clippy::large_enum_variant)]
628pub enum WireMessage {
629 Message(Message),
631 SegmentStart(SegmentStart),
633 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}