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 }
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#[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 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 pub temporary_contract_id: [u8; 32],
439 pub accept_collateral: Amount,
441 pub funding_pubkey: PublicKey,
443 pub payout_spk: ScriptBuf,
445 pub payout_serial_id: u64,
447 pub funding_inputs: Vec<FundingInput>,
449 pub change_spk: ScriptBuf,
451 pub change_serial_id: u64,
453 pub cet_adaptor_signatures: CetAdaptorSignatures,
455 pub refund_signature: Signature,
457 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#[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 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 pub contract_id: [u8; 32],
496 pub cet_adaptor_signatures: CetAdaptorSignatures,
498 pub refund_signature: Signature,
500 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#[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 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 pub contract_id: [u8; 32],
531 pub close_signature: Signature,
533 pub accept_payout: Amount,
535 pub fee_rate_per_vb: u64,
537 pub fund_input_serial_id: u64,
539 pub funding_inputs: Vec<FundingInput>,
541 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#[allow(clippy::large_enum_variant)]
624pub enum WireMessage {
625 Message(Message),
627 SegmentStart(SegmentStart),
629 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}