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 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;
32
33#[cfg(any(test, feature = "use-serde"))]
34pub mod serde_utils;
35
36use std::fmt::Display;
37
38use crate::ser_impls::{read_ecdsa_adaptor_signature, write_ecdsa_adaptor_signature};
39use bitcoin::{consensus::Decodable, OutPoint, Transaction};
40use bitcoin::{Amount, ScriptBuf};
41use channel::{
42 AcceptChannel, CollaborativeCloseOffer, OfferChannel, Reject, RenewAccept, RenewConfirm,
43 RenewFinalize, RenewOffer, RenewRevoke, SettleAccept, SettleConfirm, SettleFinalize,
44 SettleOffer, SignChannel,
45};
46use contract_msgs::ContractInfo;
47use dlc::{Error, TxInputInfo};
48use lightning::ln::msgs::DecodeError;
49use lightning::ln::wire::Type;
50use lightning::util::ser::{Readable, Writeable, Writer};
51use secp256k1_zkp::Verification;
52use secp256k1_zkp::{ecdsa::Signature, EcdsaAdaptorSignature, PublicKey, Secp256k1};
53use segmentation::{SegmentChunk, SegmentStart};
54
55macro_rules! impl_type {
56 ($const_name: ident, $type_name: ident, $type_val: expr) => {
57 pub const $const_name: u16 = $type_val;
59
60 impl Type for $type_name {
61 fn type_id(&self) -> u16 {
62 $const_name
63 }
64 }
65 };
66}
67
68impl_type!(OFFER_TYPE, OfferDlc, 42778);
69impl_type!(ACCEPT_TYPE, AcceptDlc, 42780);
70impl_type!(SIGN_TYPE, SignDlc, 42782);
71impl_type!(OFFER_CHANNEL_TYPE, OfferChannel, 43000);
72impl_type!(ACCEPT_CHANNEL_TYPE, AcceptChannel, 43002);
73impl_type!(SIGN_CHANNEL_TYPE, SignChannel, 43004);
74impl_type!(SETTLE_CHANNEL_OFFER_TYPE, SettleOffer, 43006);
75impl_type!(SETTLE_CHANNEL_ACCEPT_TYPE, SettleAccept, 43008);
76impl_type!(SETTLE_CHANNEL_CONFIRM_TYPE, SettleConfirm, 43010);
77impl_type!(SETTLE_CHANNEL_FINALIZE_TYPE, SettleFinalize, 43012);
78impl_type!(RENEW_CHANNEL_OFFER_TYPE, RenewOffer, 43014);
79impl_type!(RENEW_CHANNEL_ACCEPT_TYPE, RenewAccept, 43016);
80impl_type!(RENEW_CHANNEL_CONFIRM_TYPE, RenewConfirm, 43018);
81impl_type!(RENEW_CHANNEL_FINALIZE_TYPE, RenewFinalize, 43020);
82impl_type!(RENEW_CHANNEL_REVOKE_TYPE, RenewRevoke, 43026);
83impl_type!(
84 COLLABORATIVE_CLOSE_OFFER_TYPE,
85 CollaborativeCloseOffer,
86 43022
87);
88impl_type!(REJECT, Reject, 43024);
89
90#[derive(Clone, Debug, PartialEq, Eq)]
91#[cfg_attr(
92 feature = "use-serde",
93 derive(serde::Serialize, serde::Deserialize),
94 serde(rename_all = "camelCase")
95)]
96pub struct FundingInput {
99 pub input_serial_id: u64,
101 #[cfg_attr(
102 feature = "use-serde",
103 serde(
104 serialize_with = "crate::serde_utils::serialize_hex",
105 deserialize_with = "crate::serde_utils::deserialize_hex_string"
106 )
107 )]
108 pub prev_tx: Vec<u8>,
110 pub prev_tx_vout: u32,
112 pub sequence: u32,
114 pub max_witness_len: u16,
116 pub redeem_script: ScriptBuf,
118}
119
120impl_dlc_writeable!(FundingInput, {
121 (input_serial_id, writeable),
122 (prev_tx, vec),
123 (prev_tx_vout, writeable),
124 (sequence, writeable),
125 (max_witness_len, writeable),
126 (redeem_script, writeable)
127});
128
129impl From<&FundingInput> for TxInputInfo {
130 fn from(funding_input: &FundingInput) -> TxInputInfo {
131 TxInputInfo {
132 outpoint: OutPoint {
133 txid: Transaction::consensus_decode(&mut funding_input.prev_tx.as_slice())
134 .expect("Transaction Decode Error")
135 .compute_txid(),
136 vout: funding_input.prev_tx_vout,
137 },
138 max_witness_len: (funding_input.max_witness_len as usize),
139 redeem_script: funding_input.redeem_script.clone(),
140 serial_id: funding_input.input_serial_id,
141 }
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq)]
146#[cfg_attr(
147 feature = "use-serde",
148 derive(serde::Serialize, serde::Deserialize),
149 serde(rename_all = "camelCase")
150)]
151pub struct CetAdaptorSignature {
153 pub signature: EcdsaAdaptorSignature,
155}
156
157impl_dlc_writeable!(CetAdaptorSignature, {
158 (signature, { cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature })
159});
160
161#[derive(Clone, Debug, PartialEq, Eq)]
162#[cfg_attr(
163 feature = "use-serde",
164 derive(serde::Serialize, serde::Deserialize),
165 serde(rename_all = "camelCase")
166)]
167pub struct CetAdaptorSignatures {
169 pub ecdsa_adaptor_signatures: Vec<CetAdaptorSignature>,
171}
172
173impl From<&[EcdsaAdaptorSignature]> for CetAdaptorSignatures {
174 fn from(signatures: &[EcdsaAdaptorSignature]) -> Self {
175 CetAdaptorSignatures {
176 ecdsa_adaptor_signatures: signatures
177 .iter()
178 .map(|x| CetAdaptorSignature { signature: *x })
179 .collect(),
180 }
181 }
182}
183
184impl From<&CetAdaptorSignatures> for Vec<EcdsaAdaptorSignature> {
185 fn from(signatures: &CetAdaptorSignatures) -> Vec<EcdsaAdaptorSignature> {
186 signatures
187 .ecdsa_adaptor_signatures
188 .iter()
189 .map(|x| x.signature)
190 .collect::<Vec<_>>()
191 }
192}
193
194impl_dlc_writeable!(CetAdaptorSignatures, { (ecdsa_adaptor_signatures, vec) });
195
196#[derive(Clone, Debug, PartialEq, Eq)]
197#[cfg_attr(
198 feature = "use-serde",
199 derive(serde::Serialize, serde::Deserialize),
200 serde(rename_all = "camelCase")
201)]
202pub struct FundingSignature {
204 pub witness_elements: Vec<WitnessElement>,
206}
207
208impl_dlc_writeable!(FundingSignature, { (witness_elements, vec) });
209
210#[derive(Clone, Debug, PartialEq, Eq)]
211#[cfg_attr(
212 feature = "use-serde",
213 derive(serde::Serialize, serde::Deserialize),
214 serde(rename_all = "camelCase")
215)]
216pub struct FundingSignatures {
219 pub funding_signatures: Vec<FundingSignature>,
221}
222
223impl_dlc_writeable!(FundingSignatures, { (funding_signatures, vec) });
224
225#[derive(Clone, Debug, PartialEq, Eq)]
226#[cfg_attr(
227 feature = "use-serde",
228 derive(serde::Serialize, serde::Deserialize),
229 serde(rename_all = "camelCase")
230)]
231pub struct WitnessElement {
233 #[cfg_attr(
234 feature = "use-serde",
235 serde(
236 serialize_with = "crate::serde_utils::serialize_hex",
237 deserialize_with = "crate::serde_utils::deserialize_hex_string"
238 )
239 )]
240 pub witness: Vec<u8>,
242}
243
244impl_dlc_writeable!(WitnessElement, { (witness, vec) });
245
246#[derive(Clone, Debug, PartialEq, Eq)]
247#[cfg_attr(
248 feature = "use-serde",
249 derive(serde::Serialize, serde::Deserialize),
250 serde(rename_all = "camelCase")
251)]
252pub enum NegotiationFields {
254 Single(SingleNegotiationFields),
256 Disjoint(DisjointNegotiationFields),
258}
259
260impl_dlc_writeable_enum!(NegotiationFields, (0, Single), (1, Disjoint);;;);
261
262#[derive(Clone, Debug, PartialEq, Eq)]
263#[cfg_attr(
264 feature = "use-serde",
265 derive(serde::Serialize, serde::Deserialize),
266 serde(rename_all = "camelCase")
267)]
268pub struct SingleNegotiationFields {
270 rounding_intervals: contract_msgs::RoundingIntervals,
272}
273
274impl_dlc_writeable!(SingleNegotiationFields, { (rounding_intervals, writeable) });
275
276#[derive(Clone, Debug, PartialEq, Eq)]
277#[cfg_attr(
278 feature = "use-serde",
279 derive(serde::Serialize, serde::Deserialize),
280 serde(rename_all = "camelCase")
281)]
282pub struct DisjointNegotiationFields {
284 negotiation_fields: Vec<NegotiationFields>,
286}
287
288impl_dlc_writeable!(DisjointNegotiationFields, { (negotiation_fields, vec) });
289
290#[derive(Clone, Debug, PartialEq)]
291#[cfg_attr(
292 feature = "use-serde",
293 derive(serde::Serialize, serde::Deserialize),
294 serde(rename_all = "camelCase")
295)]
296pub struct OfferDlc {
300 pub protocol_version: u32,
302 pub contract_flags: u8,
304 #[cfg_attr(
305 feature = "use-serde",
306 serde(
307 serialize_with = "crate::serde_utils::serialize_hex",
308 deserialize_with = "crate::serde_utils::deserialize_hex_array"
309 )
310 )]
311 pub chain_hash: [u8; 32],
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 temporary_contract_id: [u8; 32],
322 pub contract_info: ContractInfo,
324 pub funding_pubkey: PublicKey,
326 pub payout_spk: ScriptBuf,
328 pub payout_serial_id: u64,
330 pub offer_collateral: Amount,
332 pub funding_inputs: Vec<FundingInput>,
334 pub change_spk: ScriptBuf,
336 pub change_serial_id: u64,
338 pub fund_output_serial_id: u64,
340 pub fee_rate_per_vb: u64,
342 pub cet_locktime: u32,
344 pub refund_locktime: u32,
346}
347
348impl OfferDlc {
349 pub fn get_total_collateral(&self) -> Amount {
351 match &self.contract_info {
352 ContractInfo::SingleContractInfo(single) => single.total_collateral,
353 ContractInfo::DisjointContractInfo(disjoint) => disjoint.total_collateral,
354 }
355 }
356
357 pub fn validate<C: Verification>(
359 &self,
360 secp: &Secp256k1<C>,
361 min_timeout_interval: u32,
362 max_timeout_interval: u32,
363 ) -> Result<(), Error> {
364 match &self.contract_info {
365 ContractInfo::SingleContractInfo(s) => s.contract_info.oracle_info.validate(secp)?,
366 ContractInfo::DisjointContractInfo(d) => {
367 if d.contract_infos.len() < 2 {
368 return Err(Error::InvalidArgument);
369 }
370
371 for c in &d.contract_infos {
372 c.oracle_info.validate(secp)?;
373 }
374 }
375 }
376
377 let closest_maturity_date = self.contract_info.get_closest_maturity_date();
378 let valid_dates = self.cet_locktime <= closest_maturity_date
379 && closest_maturity_date + min_timeout_interval <= self.refund_locktime
380 && self.refund_locktime <= closest_maturity_date + max_timeout_interval;
381 if !valid_dates {
382 return Err(Error::InvalidArgument);
383 }
384
385 Ok(())
386 }
387}
388
389impl_dlc_writeable!(OfferDlc, {
390 (protocol_version, writeable),
391 (contract_flags, writeable),
392 (chain_hash, writeable),
393 (temporary_contract_id, writeable),
394 (contract_info, writeable),
395 (funding_pubkey, writeable),
396 (payout_spk, writeable),
397 (payout_serial_id, writeable),
398 (offer_collateral, writeable),
399 (funding_inputs, vec),
400 (change_spk, writeable),
401 (change_serial_id, writeable),
402 (fund_output_serial_id, writeable),
403 (fee_rate_per_vb, writeable),
404 (cet_locktime, writeable),
405 (refund_locktime, writeable)
406});
407
408#[derive(Clone, Debug, PartialEq, Eq)]
413#[cfg_attr(
414 feature = "use-serde",
415 derive(serde::Serialize, serde::Deserialize),
416 serde(rename_all = "camelCase")
417)]
418pub struct AcceptDlc {
419 pub protocol_version: u32,
421 #[cfg_attr(
422 feature = "use-serde",
423 serde(
424 serialize_with = "crate::serde_utils::serialize_hex",
425 deserialize_with = "crate::serde_utils::deserialize_hex_array"
426 )
427 )]
428 pub temporary_contract_id: [u8; 32],
430 pub accept_collateral: Amount,
432 pub funding_pubkey: PublicKey,
434 pub payout_spk: ScriptBuf,
436 pub payout_serial_id: u64,
438 pub funding_inputs: Vec<FundingInput>,
440 pub change_spk: ScriptBuf,
442 pub change_serial_id: u64,
444 pub cet_adaptor_signatures: CetAdaptorSignatures,
446 pub refund_signature: Signature,
448 pub negotiation_fields: Option<NegotiationFields>,
450}
451
452impl_dlc_writeable!(AcceptDlc, {
453 (protocol_version, writeable),
454 (temporary_contract_id, writeable),
455 (accept_collateral, writeable),
456 (funding_pubkey, writeable),
457 (payout_spk, writeable),
458 (payout_serial_id, writeable),
459 (funding_inputs, vec),
460 (change_spk, writeable),
461 (change_serial_id, writeable),
462 (cet_adaptor_signatures, writeable),
463 (refund_signature, writeable),
464 (negotiation_fields, option)
465});
466
467#[derive(Clone, Debug, PartialEq, Eq)]
470#[cfg_attr(
471 feature = "use-serde",
472 derive(serde::Serialize, serde::Deserialize),
473 serde(rename_all = "camelCase")
474)]
475pub struct SignDlc {
476 pub protocol_version: u32,
478 #[cfg_attr(
479 feature = "use-serde",
480 serde(
481 serialize_with = "crate::serde_utils::serialize_hex",
482 deserialize_with = "crate::serde_utils::deserialize_hex_array"
483 )
484 )]
485 pub contract_id: [u8; 32],
487 pub cet_adaptor_signatures: CetAdaptorSignatures,
489 pub refund_signature: Signature,
491 pub funding_signatures: FundingSignatures,
493}
494
495impl_dlc_writeable!(SignDlc, {
496 (protocol_version, writeable),
497 (contract_id, writeable),
498 (cet_adaptor_signatures, writeable),
499 (refund_signature, writeable),
500 (funding_signatures, writeable)
501});
502
503#[allow(missing_docs)]
504#[derive(Debug, Clone)]
505pub enum Message {
506 Offer(OfferDlc),
507 Accept(AcceptDlc),
508 Sign(SignDlc),
509 OfferChannel(OfferChannel),
510 AcceptChannel(AcceptChannel),
511 SignChannel(SignChannel),
512 SettleOffer(SettleOffer),
513 SettleAccept(SettleAccept),
514 SettleConfirm(SettleConfirm),
515 SettleFinalize(SettleFinalize),
516 RenewOffer(RenewOffer),
517 RenewAccept(RenewAccept),
518 RenewConfirm(RenewConfirm),
519 RenewFinalize(RenewFinalize),
520 RenewRevoke(RenewRevoke),
521 CollaborativeCloseOffer(CollaborativeCloseOffer),
522 Reject(Reject),
523}
524
525macro_rules! impl_type_writeable_for_enum {
526 ($type_name: ident, {$($variant_name: ident),*}) => {
527 impl Type for $type_name {
528 fn type_id(&self) -> u16 {
529 match self {
530 $($type_name::$variant_name(v) => v.type_id(),)*
531 }
532 }
533 }
534
535 impl Writeable for $type_name {
536 fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::lightning::io::Error> {
537 match self {
538 $($type_name::$variant_name(v) => v.write(writer),)*
539 }
540 }
541 }
542 };
543}
544
545impl_type_writeable_for_enum!(Message,
546{
547 Offer,
548 Accept,
549 Sign,
550 OfferChannel,
551 AcceptChannel,
552 SignChannel,
553 SettleOffer,
554 SettleAccept,
555 SettleConfirm,
556 SettleFinalize,
557 RenewOffer,
558 RenewAccept,
559 RenewConfirm,
560 RenewFinalize,
561 RenewRevoke,
562 CollaborativeCloseOffer,
563 Reject
564});
565
566#[derive(Debug, Clone)]
567pub enum WireMessage {
569 Message(Message),
571 SegmentStart(SegmentStart),
573 SegmentChunk(SegmentChunk),
575}
576
577impl Display for WireMessage {
578 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579 let name = match self {
580 Self::Message(_) => "Message",
581 Self::SegmentStart(_) => "SegmentStart",
582 Self::SegmentChunk(_) => "SegmentChunk",
583 };
584 f.write_str(name)
585 }
586}
587
588impl_type_writeable_for_enum!(WireMessage, { Message, SegmentStart, SegmentChunk });
589
590#[cfg(test)]
591mod tests {
592 use secp256k1_zkp::SECP256K1;
593
594 use super::*;
595
596 macro_rules! roundtrip_test {
597 ($type: ty, $input: ident) => {
598 let msg: $type = serde_json::from_str(&$input).unwrap();
599 test_roundtrip(msg);
600 };
601 }
602
603 fn test_roundtrip<T: Writeable + Readable + PartialEq + std::fmt::Debug>(msg: T) {
604 let mut buf = Vec::new();
605 msg.write(&mut buf).expect("Error writing message");
606 let mut cursor = lightning::io::Cursor::new(buf);
607 let deser = Readable::read(&mut cursor).expect("Error reading message");
608 assert_eq!(msg, deser);
609 }
610
611 #[test]
612 fn offer_msg_roundtrip() {
613 let input = include_str!("./test_inputs/offer_msg.json");
614 roundtrip_test!(OfferDlc, input);
615 }
616
617 #[test]
618 fn accept_msg_roundtrip() {
619 let input = include_str!("./test_inputs/accept_msg.json");
620 roundtrip_test!(AcceptDlc, input);
621 }
622
623 #[test]
624 fn sign_msg_roundtrip() {
625 let input = include_str!("./test_inputs/sign_msg.json");
626 roundtrip_test!(SignDlc, input);
627 }
628
629 #[test]
630 fn valid_offer_message_passes_validation() {
631 let input = include_str!("./test_inputs/offer_msg.json");
632 let valid_offer: OfferDlc = serde_json::from_str(input).unwrap();
633 valid_offer
634 .validate(SECP256K1, 86400 * 7, 86400 * 14)
635 .expect("to validate valid offer messages.");
636 }
637
638 #[test]
639 fn invalid_offer_messages_fail_validation() {
640 let input = include_str!("./test_inputs/offer_msg.json");
641 let offer: OfferDlc = serde_json::from_str(input).unwrap();
642
643 let mut invalid_maturity = offer.clone();
644 invalid_maturity.cet_locktime += 3;
645
646 let mut too_short_timeout = offer.clone();
647 too_short_timeout.refund_locktime -= 100;
648
649 let mut too_long_timeout = offer;
650 too_long_timeout.refund_locktime -= 100;
651
652 for invalid in &[invalid_maturity, too_short_timeout, too_long_timeout] {
653 invalid
654 .validate(SECP256K1, 86400 * 7, 86400 * 14)
655 .expect_err("Should not pass validation of invalid offer message.");
656 }
657 }
658
659 #[test]
660 fn disjoint_contract_offer_messages_fail_validation() {
661 let input = include_str!("./test_inputs/offer_msg_disjoint.json");
662 let offer: OfferDlc = serde_json::from_str(input).unwrap();
663
664 let mut no_contract_input = offer.clone();
665 no_contract_input.contract_info =
666 ContractInfo::DisjointContractInfo(contract_msgs::DisjointContractInfo {
667 total_collateral: Amount::ONE_BTC,
668 contract_infos: vec![],
669 });
670
671 let mut single_contract_input = offer.clone();
672 single_contract_input.contract_info =
673 if let ContractInfo::DisjointContractInfo(d) = offer.contract_info {
674 let mut single = d;
675 single.contract_infos.remove(1);
676 ContractInfo::DisjointContractInfo(single)
677 } else {
678 panic!("Expected disjoint contract info.");
679 };
680
681 for invalid in &[no_contract_input, single_contract_input] {
682 invalid
683 .validate(SECP256K1, 86400 * 7, 86400 * 14)
684 .expect_err("Should not pass validation of invalid offer message.");
685 }
686 }
687}