1#![deny(missing_docs)]
56
57use std::collections::BTreeMap;
58
59use bitcoin::hashes::{sha256, Hash, HashEngine};
60use bitcoin::key::{TapTweak, TweakedPublicKey};
61use bitcoin::secp256k1::{
62 schnorr::Signature, Keypair, Message, Parity, PublicKey, Scalar, SecretKey, XOnlyPublicKey,
63};
64use bitcoin::sighash::{Prevouts, SighashCache, TapSighashType};
65use bitcoin::taproot::{LeafVersion, TapLeafHash, TapNodeHash};
66use bitcoin::{
67 absolute::LockTime, transaction::Version, Amount, OutPoint, ScriptBuf, Sequence, Transaction,
68 TxIn, TxOut, Witness,
69};
70use sidestr_core::block::secp;
71use sidestr_core::federation::NUMS_X;
72use sidestr_core::sighash::{
73 key_path_hash_type, key_path_sighash, unified_taproot_sighash, SighashRules, UnifiedTaproot,
74};
75
76pub mod protocol;
77pub mod route;
78
79pub const DUST: u64 = 330;
81pub const DEFAULT_FEE: u64 = 300;
83pub const DEFAULT_DELAY: u16 = 6;
85pub const MIN_DELAY: u16 = 3;
87pub const MAX_DELAY: u16 = 144;
89pub const MIN_FEE: u64 = 100;
91pub const MAX_FEE: u64 = 10_000;
93pub const MAX_EXPIRY: u32 = 500_000_000;
95
96const SIGHASH_ALL: u8 = 0x01;
97const INPUT_SEQUENCE: u32 = 0xffff_fffd;
98const LOCKTIME_SEQUENCE: u32 = 0xffff_fffe;
99
100#[derive(Debug, thiserror::Error)]
102pub enum Error {
103 #[error("fee must be between {MIN_FEE} and {MAX_FEE} sats")]
105 Fee,
106 #[error("delay must be between {MIN_DELAY} and {MAX_DELAY} blocks")]
108 Delay,
109 #[error("HTLC expiry must be below {MAX_EXPIRY}")]
111 Expiry,
112 #[error("channel state accounts for {state} sats but funding holds {funding} sats")]
114 Unbalanced {
115 state: u64,
117 funding: u64,
119 },
120 #[error("channel amount overflow")]
122 AmountOverflow,
123 #[error("nothing to pay out")]
125 NothingToPay,
126 #[error("HTLCs in flight: settle or fail them first")]
128 HtlcsInFlight,
129 #[error("the output is not worth claiming after the fee")]
131 DustClaim,
132 #[error("the commitment has no {0} output")]
134 MissingOutput(&'static str),
135 #[error("an HTLC success claim needs its 32-byte preimage")]
137 MissingPreimage,
138 #[error("script number out of range: {0}")]
140 ScriptNumber(u32),
141 #[error("NUMS derivation failed")]
143 Nums,
144 #[error("signature hash failed: {0}")]
146 Sighash(&'static str),
147 #[error("both funding signatures are needed")]
149 MissingSignature,
150 #[error("invalid leaf signature")]
152 InvalidSignature,
153 #[error("bad revocation key")]
155 RevocationKey,
156 #[error(transparent)]
158 Secp(#[from] bitcoin::secp256k1::Error),
159}
160
161pub type Result<T> = std::result::Result<T, Error>;
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
166pub enum Side {
167 A,
169 B,
171}
172
173impl Side {
174 pub const fn other(self) -> Self {
176 match self {
177 Self::A => Self::B,
178 Self::B => Self::A,
179 }
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub struct Balances {
186 pub a: Amount,
188 pub b: Amount,
190}
191
192impl Balances {
193 fn get(self, side: Side) -> Amount {
194 match side {
195 Side::A => self.a,
196 Side::B => self.b,
197 }
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub struct RevocationKeys {
204 pub a: XOnlyPublicKey,
206 pub b: XOnlyPublicKey,
208}
209
210impl RevocationKeys {
211 fn get(self, side: Side) -> XOnlyPublicKey {
212 match side {
213 Side::A => self.a,
214 Side::B => self.b,
215 }
216 }
217}
218
219#[derive(Debug, Clone, PartialEq, Eq)]
221pub struct Htlc {
222 pub id: u64,
224 pub from: Side,
226 pub amount: Amount,
228 pub payment_hash: [u8; 32],
230 pub expiry: u32,
232}
233
234#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct ChannelState {
237 pub balances: Balances,
239 pub revocation: RevocationKeys,
241 pub htlcs: Vec<Htlc>,
243}
244
245#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct LeafSpend {
248 pub script: ScriptBuf,
250 pub leaf_hash: TapLeafHash,
252 pub control_block: Vec<u8>,
254 pub csv: bool,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq)]
260pub struct FundingScript {
261 pub internal_key: XOnlyPublicKey,
263 pub output_key: TweakedPublicKey,
265 pub script_pubkey: ScriptBuf,
267 pub signers: [XOnlyPublicKey; 2],
269 pub leaf: LeafSpend,
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
275pub struct ToLocalScript {
276 pub output_key: TweakedPublicKey,
278 pub script_pubkey: ScriptBuf,
280 pub delayed: LeafSpend,
282 pub revocation: LeafSpend,
284}
285
286#[derive(Debug, Clone, PartialEq, Eq)]
288pub struct HtlcScript {
289 pub output_key: TweakedPublicKey,
291 pub script_pubkey: ScriptBuf,
293 pub success: LeafSpend,
295 pub timeout: LeafSpend,
297 pub revocation: LeafSpend,
299}
300
301#[derive(Debug, Clone, Copy, PartialEq, Eq)]
303pub enum OutputKind {
304 ToLocal,
306 ToRemote,
308 Htlc,
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
315pub struct CommitmentHtlc {
316 pub htlc: Htlc,
318 pub vout: u32,
320 pub scripts: HtlcScript,
322 pub offered_by_owner: bool,
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
328pub struct Commitment {
329 pub tx: Transaction,
331 pub to_local: ToLocalScript,
333 pub kinds: Vec<OutputKind>,
335 pub htlcs: Vec<CommitmentHtlc>,
337 pub local_value: Amount,
339 pub remote_value: Amount,
341 pub to_local_vout: Option<u32>,
343 pub number: u64,
345 pub owner: Side,
347 pub delay: u16,
349}
350
351#[derive(Debug, Clone, Copy, PartialEq, Eq)]
354pub struct LeafSignature([u8; 65]);
355
356impl LeafSignature {
357 pub fn from_slice(bytes: &[u8]) -> Result<Self> {
359 let bytes: [u8; 65] = bytes.try_into().map_err(|_| Error::InvalidSignature)?;
360 Signature::from_slice(&bytes[..64]).map_err(|_| Error::InvalidSignature)?;
361 Ok(Self(bytes))
362 }
363
364 pub fn as_bytes(&self) -> &[u8; 65] {
366 &self.0
367 }
368
369 pub fn to_hex(self) -> String {
371 hex_bytes(&self.0)
372 }
373
374 pub fn from_hex(text: &str) -> Result<Self> {
376 if text.len() != 130 {
377 return Err(Error::InvalidSignature);
378 }
379 let mut bytes = [0u8; 65];
380 decode_hex_into(text, &mut bytes).ok_or(Error::InvalidSignature)?;
381 Self::from_slice(&bytes)
382 }
383}
384
385#[derive(Debug, Clone, Copy, PartialEq, Eq)]
390pub struct PopSignature([u8; 64]);
391
392impl PopSignature {
393 pub fn from_bytes(bytes: [u8; 64]) -> Self {
395 Self(bytes)
396 }
397
398 pub fn as_bytes(&self) -> &[u8; 64] {
400 &self.0
401 }
402
403 pub fn to_hex(self) -> String {
405 hex_bytes(&self.0)
406 }
407
408 pub fn from_hex(text: &str) -> Result<Self> {
410 let mut bytes = [0u8; 64];
411 decode_hex_into(text, &mut bytes).ok_or(Error::InvalidSignature)?;
412 Ok(Self(bytes))
413 }
414}
415
416#[derive(Debug, Clone, PartialEq, Eq)]
418pub struct Channel {
419 pub keys: [XOnlyPublicKey; 2],
421 pub funding_outpoint: OutPoint,
423 pub funding_value: Amount,
425 pub funding_script: FundingScript,
427 pub fee: Amount,
429 pub delay: u16,
431}
432
433impl Channel {
434 pub fn new(
436 a: XOnlyPublicKey,
437 b: XOnlyPublicKey,
438 funding_outpoint: OutPoint,
439 funding_value: Amount,
440 fee: Amount,
441 delay: u16,
442 ) -> Result<Self> {
443 if !(MIN_FEE..=MAX_FEE).contains(&fee.to_sat()) {
444 return Err(Error::Fee);
445 }
446 if !(MIN_DELAY..=MAX_DELAY).contains(&delay) {
447 return Err(Error::Delay);
448 }
449 Ok(Self {
450 keys: [a, b],
451 funding_outpoint,
452 funding_value,
453 funding_script: funding_script(a, b)?,
454 fee,
455 delay,
456 })
457 }
458
459 pub fn key(&self, side: Side) -> XOnlyPublicKey {
461 match side {
462 Side::A => self.keys[0],
463 Side::B => self.keys[1],
464 }
465 }
466
467 pub fn funding_prevout(&self) -> TxOut {
469 TxOut {
470 value: self.funding_value,
471 script_pubkey: self.funding_script.script_pubkey.clone(),
472 }
473 }
474
475 pub fn commitment(&self, number: u64, owner: Side, state: &ChannelState) -> Result<Commitment> {
477 self.check_state(state)?;
478 let remote_side = owner.other();
479 let owner_pub = self.key(owner);
480 let remote_pub = self.key(remote_side);
481 let local = payout_after_fee(state.balances.get(owner), owner, self.fee);
482 let remote = payout_after_fee(state.balances.get(remote_side), remote_side, self.fee);
483 let to_local = to_local_script(owner_pub, state.revocation.get(owner), self.delay)?;
484 let mut output = Vec::new();
485 let mut kinds = Vec::new();
486 let mut to_local_vout = None;
487 if local.to_sat() >= DUST {
488 to_local_vout = Some(output.len() as u32);
489 output.push(TxOut {
490 value: local,
491 script_pubkey: to_local.script_pubkey.clone(),
492 });
493 kinds.push(OutputKind::ToLocal);
494 }
495 if remote.to_sat() >= DUST {
496 output.push(TxOut {
497 value: remote,
498 script_pubkey: to_remote_script(remote_pub),
499 });
500 kinds.push(OutputKind::ToRemote);
501 }
502
503 let mut sorted = state.htlcs.clone();
504 sorted.sort_by_key(|h| h.id);
505 let mut htlcs = Vec::new();
506 for htlc in sorted {
507 if htlc.expiry >= MAX_EXPIRY {
508 return Err(Error::Expiry);
509 }
510 if htlc.amount.to_sat() < DUST {
511 continue;
512 }
513 let offered_by_owner = htlc.from == owner;
514 let scripts = htlc_script(HtlcScriptArgs {
515 owner_pub,
516 remote_pub,
517 revocation_pub: state.revocation.get(owner),
518 delay: self.delay,
519 payment_hash: htlc.payment_hash,
520 expiry: htlc.expiry,
521 offered_by_owner,
522 })?;
523 let vout = output.len() as u32;
524 output.push(TxOut {
525 value: htlc.amount,
526 script_pubkey: scripts.script_pubkey.clone(),
527 });
528 kinds.push(OutputKind::Htlc);
529 htlcs.push(CommitmentHtlc {
530 htlc,
531 vout,
532 scripts,
533 offered_by_owner,
534 });
535 }
536 if output.is_empty() {
537 return Err(Error::NothingToPay);
538 }
539 Ok(Commitment {
540 tx: one_input_tx(
541 self.funding_outpoint,
542 INPUT_SEQUENCE,
543 LockTime::ZERO,
544 output,
545 ),
546 to_local,
547 kinds,
548 htlcs,
549 local_value: local,
550 remote_value: remote,
551 to_local_vout,
552 number,
553 owner,
554 delay: self.delay,
555 })
556 }
557
558 pub fn cooperative_close(&self, state: &ChannelState) -> Result<Transaction> {
560 if !state.htlcs.is_empty() {
561 return Err(Error::HtlcsInFlight);
562 }
563 self.check_state(state)?;
564 let mut output = Vec::new();
565 let a = payout_after_fee(state.balances.a, Side::A, self.fee);
566 if a.to_sat() >= DUST {
567 output.push(TxOut {
568 value: a,
569 script_pubkey: to_remote_script(self.keys[0]),
570 });
571 }
572 if state.balances.b.to_sat() >= DUST {
573 output.push(TxOut {
574 value: state.balances.b,
575 script_pubkey: to_remote_script(self.keys[1]),
576 });
577 }
578 if output.is_empty() {
579 return Err(Error::NothingToPay);
580 }
581 Ok(one_input_tx(
582 self.funding_outpoint,
583 INPUT_SEQUENCE,
584 LockTime::ZERO,
585 output,
586 ))
587 }
588
589 pub fn sign_funding(
591 &self,
592 tx: &Transaction,
593 key: &SecretKey,
594 rules: SighashRules,
595 aux: &[u8; 32],
596 ) -> Result<LeafSignature> {
597 sign_leaf(
598 tx,
599 0,
600 &[self.funding_prevout()],
601 self.funding_script.leaf.leaf_hash,
602 key,
603 rules,
604 aux,
605 )
606 }
607
608 pub fn verify_funding(
610 &self,
611 tx: &Transaction,
612 pubkey: &XOnlyPublicKey,
613 signature: &LeafSignature,
614 rules: SighashRules,
615 ) -> bool {
616 verify_leaf_signature(
617 tx,
618 0,
619 &[self.funding_prevout()],
620 self.funding_script.leaf.leaf_hash,
621 pubkey,
622 signature,
623 rules,
624 )
625 }
626
627 pub fn funding_witness(
630 &self,
631 signatures: &BTreeMap<XOnlyPublicKey, LeafSignature>,
632 ) -> Result<Witness> {
633 let mut slots = self
634 .funding_script
635 .signers
636 .iter()
637 .map(|key| {
638 signatures
639 .get(key)
640 .map(|signature| signature.as_bytes().to_vec())
641 .ok_or(Error::MissingSignature)
642 })
643 .collect::<Result<Vec<_>>>()?;
644 slots.reverse();
645 slots.push(self.funding_script.leaf.script.to_bytes());
646 slots.push(self.funding_script.leaf.control_block.clone());
647 Ok(Witness::from_slice(&slots))
648 }
649
650 fn check_state(&self, state: &ChannelState) -> Result<()> {
651 let total = state.htlcs.iter().try_fold(
652 state
653 .balances
654 .a
655 .to_sat()
656 .checked_add(state.balances.b.to_sat())
657 .ok_or(Error::AmountOverflow)?,
658 |sum, htlc| {
659 sum.checked_add(htlc.amount.to_sat())
660 .ok_or(Error::AmountOverflow)
661 },
662 )?;
663 if total != self.funding_value.to_sat() {
664 return Err(Error::Unbalanced {
665 state: total,
666 funding: self.funding_value.to_sat(),
667 });
668 }
669 Ok(())
670 }
671}
672
673#[derive(Debug, Clone, Copy, PartialEq, Eq)]
675pub struct HtlcScriptArgs {
676 pub owner_pub: XOnlyPublicKey,
678 pub remote_pub: XOnlyPublicKey,
680 pub revocation_pub: XOnlyPublicKey,
682 pub delay: u16,
684 pub payment_hash: [u8; 32],
686 pub expiry: u32,
688 pub offered_by_owner: bool,
690}
691
692pub fn internal_key() -> Result<XOnlyPublicKey> {
695 let tweak = tagged_sha256(b"hitch/nums", b"hitch");
696 let mut encoded = [0u8; 33];
697 encoded[0] = 0x02;
698 encoded[1..].copy_from_slice(&NUMS_X);
699 let point = PublicKey::from_slice(&encoded)?;
700 let scalar = Scalar::from_be_bytes(tweak).map_err(|_| Error::Nums)?;
701 Ok(point.add_exp_tweak(secp(), &scalar)?.x_only_public_key().0)
702}
703
704pub fn even_secret(secret: &SecretKey) -> SecretKey {
708 let (_, parity) = secret.x_only_public_key(secp());
709 if parity == Parity::Odd {
710 secret.negate()
711 } else {
712 *secret
713 }
714}
715
716pub fn revocation_pub(point: XOnlyPublicKey, scalar: &SecretKey) -> Result<XOnlyPublicKey> {
741 let lifted = point.public_key(Parity::Even);
742 let tweak = Scalar::from(even_secret(scalar));
743 let combined = lifted
744 .add_exp_tweak(secp(), &tweak)
745 .map_err(|_| Error::RevocationKey)?;
746 Ok(combined.x_only_public_key().0)
747}
748
749pub fn revocation_key(basepoint_secret: &SecretKey, per_state: &SecretKey) -> Result<SecretKey> {
754 even_secret(basepoint_secret)
755 .add_tweak(&Scalar::from(even_secret(per_state)))
756 .map_err(|_| Error::RevocationKey)
757}
758
759fn pop_message(point: &XOnlyPublicKey, context: &str) -> [u8; 32] {
760 let mut message = Vec::with_capacity(context.len() + 32);
761 message.extend_from_slice(context.as_bytes());
762 message.extend_from_slice(&point.serialize());
763 tagged_sha256(b"hitch/pop", &message)
764}
765
766pub fn pop_sign(secret: &SecretKey, context: &str, aux: &[u8; 32]) -> PopSignature {
774 let keypair = Keypair::from_secret_key(secp(), secret);
775 let point = keypair.x_only_public_key().0;
776 let message = Message::from_digest(pop_message(&point, context));
777 let signature = secp().sign_schnorr_with_aux_rand(&message, &keypair, aux);
778 let mut bytes = [0u8; 64];
779 bytes.copy_from_slice(signature.as_ref());
780 PopSignature(bytes)
781}
782
783pub fn pop_verify(point: &XOnlyPublicKey, signature: &PopSignature, context: &str) -> bool {
786 let Ok(signature) = Signature::from_slice(&signature.0) else {
787 return false;
788 };
789 let message = Message::from_digest(pop_message(point, context));
790 secp().verify_schnorr(&signature, &message, point).is_ok()
791}
792
793pub fn preimage_in(tx: &Transaction, payment_hash: &[u8; 32]) -> Option<[u8; 32]> {
798 tx.input
799 .iter()
800 .flat_map(|input| input.witness.iter())
801 .filter(|item| item.len() == 32)
802 .find(|item| sha256::Hash::hash(item).to_byte_array() == *payment_hash)
803 .map(|item| item.try_into().expect("a 32-byte witness item"))
804}
805
806pub fn funding_script(a: XOnlyPublicKey, b: XOnlyPublicKey) -> Result<FundingScript> {
809 let mut signers = [a, b];
810 signers.sort_by_key(XOnlyPublicKey::serialize);
811 let mut bytes = Vec::with_capacity(70);
812 push_key(&mut bytes, signers[0]);
813 bytes.push(0xac); push_key(&mut bytes, signers[1]);
815 bytes.extend_from_slice(&[0xba, 0x52, 0x9c]); let script = ScriptBuf::from_bytes(bytes);
817 let leaf_hash = TapLeafHash::from_script(&script, LeafVersion::TapScript);
818 let internal_key = internal_key()?;
819 let (output_key, parity, script_pubkey) = taproot_output(internal_key, leaf_hash.into());
820 Ok(FundingScript {
821 internal_key,
822 output_key,
823 script_pubkey,
824 signers,
825 leaf: LeafSpend {
826 script,
827 leaf_hash,
828 control_block: control_block(internal_key, parity, &[]),
829 csv: false,
830 },
831 })
832}
833
834pub fn to_local_script(
837 owner_pub: XOnlyPublicKey,
838 revocation_pub: XOnlyPublicKey,
839 delay: u16,
840) -> Result<ToLocalScript> {
841 let mut delayed = Vec::new();
842 push_num(&mut delayed, u32::from(delay))?;
843 delayed.extend_from_slice(&[0xb2, 0x75]); push_key(&mut delayed, owner_pub);
845 delayed.push(0xac);
846 let delayed = ScriptBuf::from_bytes(delayed);
847
848 let mut revocation = Vec::new();
849 push_key(&mut revocation, revocation_pub);
850 revocation.push(0xac);
851 let revocation = ScriptBuf::from_bytes(revocation);
852
853 let delayed_hash = TapLeafHash::from_script(&delayed, LeafVersion::TapScript);
854 let revocation_hash = TapLeafHash::from_script(&revocation, LeafVersion::TapScript);
855 let root = TapNodeHash::from_node_hashes(delayed_hash.into(), revocation_hash.into());
856 let internal_key = internal_key()?;
857 let (output_key, parity, script_pubkey) = taproot_output(internal_key, root);
858 Ok(ToLocalScript {
859 output_key,
860 script_pubkey,
861 delayed: LeafSpend {
862 script: delayed,
863 leaf_hash: delayed_hash,
864 control_block: control_block(internal_key, parity, &[revocation_hash.into()]),
865 csv: true,
866 },
867 revocation: LeafSpend {
868 script: revocation,
869 leaf_hash: revocation_hash,
870 control_block: control_block(internal_key, parity, &[delayed_hash.into()]),
871 csv: false,
872 },
873 })
874}
875
876pub fn to_remote_script(pubkey: XOnlyPublicKey) -> ScriptBuf {
878 let mut script = Vec::with_capacity(34);
879 script.extend_from_slice(&[0x51, 0x20]);
880 script.extend_from_slice(&pubkey.serialize());
881 ScriptBuf::from_bytes(script)
882}
883
884pub fn htlc_script(args: HtlcScriptArgs) -> Result<HtlcScript> {
887 if args.expiry >= MAX_EXPIRY {
888 return Err(Error::Expiry);
889 }
890 let mut success = Vec::new();
891 success.extend_from_slice(&[0xa8, 0x20]); success.extend_from_slice(&args.payment_hash);
893 success.push(0x88); if !args.offered_by_owner {
895 push_num(&mut success, u32::from(args.delay))?;
896 success.extend_from_slice(&[0xb2, 0x75]); }
898 push_key(
899 &mut success,
900 if args.offered_by_owner {
901 args.remote_pub
902 } else {
903 args.owner_pub
904 },
905 );
906 success.push(0xac);
907 let success = ScriptBuf::from_bytes(success);
908
909 let mut timeout = Vec::new();
910 push_num(&mut timeout, args.expiry)?;
911 timeout.extend_from_slice(&[0xb1, 0x75]); if args.offered_by_owner {
913 push_num(&mut timeout, u32::from(args.delay))?;
914 timeout.extend_from_slice(&[0xb2, 0x75]); }
916 push_key(
917 &mut timeout,
918 if args.offered_by_owner {
919 args.owner_pub
920 } else {
921 args.remote_pub
922 },
923 );
924 timeout.push(0xac);
925 let timeout = ScriptBuf::from_bytes(timeout);
926
927 let mut revocation = Vec::new();
928 push_key(&mut revocation, args.revocation_pub);
929 revocation.push(0xac);
930 let revocation = ScriptBuf::from_bytes(revocation);
931
932 let success_hash = TapLeafHash::from_script(&success, LeafVersion::TapScript);
933 let timeout_hash = TapLeafHash::from_script(&timeout, LeafVersion::TapScript);
934 let revocation_hash = TapLeafHash::from_script(&revocation, LeafVersion::TapScript);
935 let inner = TapNodeHash::from_node_hashes(success_hash.into(), timeout_hash.into());
936 let root = TapNodeHash::from_node_hashes(inner, revocation_hash.into());
937 let internal_key = internal_key()?;
938 let (output_key, parity, script_pubkey) = taproot_output(internal_key, root);
939 Ok(HtlcScript {
940 output_key,
941 script_pubkey,
942 success: LeafSpend {
943 script: success,
944 leaf_hash: success_hash,
945 control_block: control_block(
946 internal_key,
947 parity,
948 &[timeout_hash.into(), revocation_hash.into()],
949 ),
950 csv: !args.offered_by_owner,
951 },
952 timeout: LeafSpend {
953 script: timeout,
954 leaf_hash: timeout_hash,
955 control_block: control_block(
956 internal_key,
957 parity,
958 &[success_hash.into(), revocation_hash.into()],
959 ),
960 csv: args.offered_by_owner,
961 },
962 revocation: LeafSpend {
963 script: revocation,
964 leaf_hash: revocation_hash,
965 control_block: control_block(internal_key, parity, &[inner]),
966 csv: false,
967 },
968 })
969}
970
971pub fn script_path_sighash(
973 tx: &Transaction,
974 input_index: usize,
975 prevouts: &[TxOut],
976 leaf_hash: TapLeafHash,
977 rules: SighashRules,
978) -> Result<([u8; 32], u8)> {
979 let hash_type = SIGHASH_ALL
980 | if rules == SighashRules::KnotsUnified {
981 sidestr_core::sighash::SIGHASH_UNIFIED
982 } else {
983 0
984 };
985 let leaf_hash_bytes = leaf_hash.to_byte_array();
986 let message = match rules {
987 SighashRules::KnotsUnified => unified_taproot_sighash(
988 tx,
989 input_index,
990 prevouts,
991 hash_type,
992 None,
993 UnifiedTaproot::ScriptPath {
994 leaf_hash: &leaf_hash_bytes,
995 codesep_pos: u32::MAX,
996 },
997 )
998 .map_err(Error::Sighash)?,
999 SighashRules::Bip341 => SighashCache::new(tx)
1000 .taproot_script_spend_signature_hash(
1001 input_index,
1002 &Prevouts::All(prevouts),
1003 leaf_hash,
1004 TapSighashType::All,
1005 )
1006 .map_err(|_| Error::Sighash("taproot script-path sighash"))?
1007 .to_byte_array(),
1008 };
1009 Ok((message, hash_type))
1010}
1011
1012pub fn sign_leaf(
1014 tx: &Transaction,
1015 input_index: usize,
1016 prevouts: &[TxOut],
1017 leaf_hash: TapLeafHash,
1018 key: &SecretKey,
1019 rules: SighashRules,
1020 aux: &[u8; 32],
1021) -> Result<LeafSignature> {
1022 let (message, hash_type) = script_path_sighash(tx, input_index, prevouts, leaf_hash, rules)?;
1023 let keypair = Keypair::from_secret_key(secp(), key);
1024 let signature =
1025 secp().sign_schnorr_with_aux_rand(&Message::from_digest(message), &keypair, aux);
1026 let mut wire = [0u8; 65];
1027 wire[..64].copy_from_slice(signature.as_ref());
1028 wire[64] = hash_type;
1029 Ok(LeafSignature(wire))
1030}
1031
1032pub fn verify_leaf_signature(
1034 tx: &Transaction,
1035 input_index: usize,
1036 prevouts: &[TxOut],
1037 leaf_hash: TapLeafHash,
1038 pubkey: &XOnlyPublicKey,
1039 signature: &LeafSignature,
1040 rules: SighashRules,
1041) -> bool {
1042 let Ok((message, hash_type)) = script_path_sighash(tx, input_index, prevouts, leaf_hash, rules)
1043 else {
1044 return false;
1045 };
1046 if signature.0[64] != hash_type {
1047 return false;
1048 }
1049 let Ok(signature) = Signature::from_slice(&signature.0[..64]) else {
1050 return false;
1051 };
1052 secp()
1053 .verify_schnorr(&signature, &Message::from_digest(message), pubkey)
1054 .is_ok()
1055}
1056
1057#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1059pub enum SweepPath {
1060 Delayed,
1062 Revocation,
1064}
1065
1066pub fn sweep_to_local(
1069 commitment: &Commitment,
1070 path: SweepPath,
1071 destination: ScriptBuf,
1072 fee: Amount,
1073 key: &SecretKey,
1074 rules: SighashRules,
1075 aux: &[u8; 32],
1076) -> Result<Transaction> {
1077 let vout = commitment
1078 .to_local_vout
1079 .ok_or(Error::MissingOutput("to_local"))?;
1080 let value = commitment.local_value;
1081 let output_value = claim_value(value, fee)?;
1082 let (leaf, sequence) = match path {
1083 SweepPath::Delayed => (&commitment.to_local.delayed, u32::from(commitment.delay)),
1084 SweepPath::Revocation => (&commitment.to_local.revocation, INPUT_SEQUENCE),
1085 };
1086 let mut tx = one_input_tx(
1087 OutPoint {
1088 txid: commitment.tx.compute_txid(),
1089 vout,
1090 },
1091 sequence,
1092 LockTime::ZERO,
1093 vec![TxOut {
1094 value: output_value,
1095 script_pubkey: destination,
1096 }],
1097 );
1098 let prevouts = [TxOut {
1099 value,
1100 script_pubkey: commitment.to_local.script_pubkey.clone(),
1101 }];
1102 let signature = sign_leaf(&tx, 0, &prevouts, leaf.leaf_hash, key, rules, aux)?;
1103 tx.input[0].witness = Witness::from_slice(&[
1104 signature.as_bytes().as_slice(),
1105 leaf.script.as_bytes(),
1106 &leaf.control_block,
1107 ]);
1108 Ok(tx)
1109}
1110
1111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1113pub enum HtlcClaimPath {
1114 Success,
1116 Timeout,
1118 Revocation,
1120}
1121
1122#[derive(Debug, Clone, PartialEq, Eq)]
1124pub struct HtlcClaim {
1125 pub path: HtlcClaimPath,
1127 pub destination: ScriptBuf,
1129 pub fee: Amount,
1131 pub preimage: Option<[u8; 32]>,
1133}
1134
1135pub fn claim_htlc(
1137 commitment: &Commitment,
1138 htlc: &CommitmentHtlc,
1139 claim: HtlcClaim,
1140 key: &SecretKey,
1141 rules: SighashRules,
1142 aux: &[u8; 32],
1143) -> Result<Transaction> {
1144 let (leaf, lock_time) = match claim.path {
1145 HtlcClaimPath::Success => (&htlc.scripts.success, LockTime::ZERO),
1146 HtlcClaimPath::Timeout => (
1147 &htlc.scripts.timeout,
1148 LockTime::from_consensus(htlc.htlc.expiry),
1149 ),
1150 HtlcClaimPath::Revocation => (&htlc.scripts.revocation, LockTime::ZERO),
1151 };
1152 let sequence = if claim.path == HtlcClaimPath::Revocation {
1153 INPUT_SEQUENCE
1154 } else if leaf.csv {
1155 u32::from(commitment.delay)
1156 } else {
1157 LOCKTIME_SEQUENCE
1158 };
1159 let output_value = claim_value(htlc.htlc.amount, claim.fee)?;
1160 let mut tx = one_input_tx(
1161 OutPoint {
1162 txid: commitment.tx.compute_txid(),
1163 vout: htlc.vout,
1164 },
1165 sequence,
1166 lock_time,
1167 vec![TxOut {
1168 value: output_value,
1169 script_pubkey: claim.destination,
1170 }],
1171 );
1172 let prevouts = [TxOut {
1173 value: htlc.htlc.amount,
1174 script_pubkey: htlc.scripts.script_pubkey.clone(),
1175 }];
1176 let signature = sign_leaf(&tx, 0, &prevouts, leaf.leaf_hash, key, rules, aux)?;
1177 let mut witness = vec![signature.as_bytes().to_vec()];
1178 if claim.path == HtlcClaimPath::Success {
1179 witness.push(claim.preimage.ok_or(Error::MissingPreimage)?.to_vec());
1180 }
1181 witness.push(leaf.script.to_bytes());
1182 witness.push(leaf.control_block.clone());
1183 tx.input[0].witness = Witness::from_slice(&witness);
1184 Ok(tx)
1185}
1186
1187pub fn key_path_spend(
1189 outpoint: OutPoint,
1190 prevout: TxOut,
1191 destination: ScriptBuf,
1192 fee: Amount,
1193 key: &SecretKey,
1194 rules: SighashRules,
1195 aux: &[u8; 32],
1196) -> Result<Transaction> {
1197 let output_value = claim_value(prevout.value, fee)?;
1198 let mut tx = one_input_tx(
1199 outpoint,
1200 INPUT_SEQUENCE,
1201 LockTime::ZERO,
1202 vec![TxOut {
1203 value: output_value,
1204 script_pubkey: destination,
1205 }],
1206 );
1207 let hash_type = key_path_hash_type(rules);
1208 let (message, returned_type) =
1209 key_path_sighash(&tx, 0, &[prevout], rules).map_err(Error::Sighash)?;
1210 debug_assert_eq!(hash_type, returned_type);
1211 let keypair = Keypair::from_secret_key(secp(), key);
1212 let signature =
1213 secp().sign_schnorr_with_aux_rand(&Message::from_digest(message), &keypair, aux);
1214 let mut wire = signature.as_ref().to_vec();
1215 wire.push(hash_type);
1216 tx.input[0].witness = Witness::from_slice(&[wire]);
1217 Ok(tx)
1218}
1219
1220fn payout_after_fee(balance: Amount, side: Side, fee: Amount) -> Amount {
1221 if side == Side::A {
1222 Amount::from_sat(balance.to_sat().saturating_sub(fee.to_sat()))
1223 } else {
1224 balance
1225 }
1226}
1227
1228fn claim_value(value: Amount, fee: Amount) -> Result<Amount> {
1229 let value = value
1230 .to_sat()
1231 .checked_sub(fee.to_sat())
1232 .filter(|value| *value >= DUST)
1233 .ok_or(Error::DustClaim)?;
1234 Ok(Amount::from_sat(value))
1235}
1236
1237fn tagged_sha256(tag: &[u8], message: &[u8]) -> [u8; 32] {
1238 let tag_hash = sha256::Hash::hash(tag).to_byte_array();
1239 let mut engine = sha256::Hash::engine();
1240 engine.input(&tag_hash);
1241 engine.input(&tag_hash);
1242 engine.input(message);
1243 sha256::Hash::from_engine(engine).to_byte_array()
1244}
1245
1246fn hex_bytes(bytes: &[u8]) -> String {
1247 const HEX: &[u8; 16] = b"0123456789abcdef";
1248 let mut out = String::with_capacity(bytes.len() * 2);
1249 for byte in bytes {
1250 out.push(HEX[usize::from(byte >> 4)] as char);
1251 out.push(HEX[usize::from(byte & 0x0f)] as char);
1252 }
1253 out
1254}
1255
1256fn decode_hex_into(text: &str, out: &mut [u8]) -> Option<()> {
1257 if text.len() != out.len() * 2 {
1258 return None;
1259 }
1260 for (target, pair) in out.iter_mut().zip(text.as_bytes().chunks_exact(2)) {
1261 *target = (hex_nibble(pair[0])? << 4) | hex_nibble(pair[1])?;
1262 }
1263 Some(())
1264}
1265
1266fn hex_nibble(byte: u8) -> Option<u8> {
1267 match byte {
1268 b'0'..=b'9' => Some(byte - b'0'),
1269 b'a'..=b'f' => Some(byte - b'a' + 10),
1270 _ => None,
1271 }
1272}
1273
1274fn taproot_output(
1275 internal_key: XOnlyPublicKey,
1276 root: TapNodeHash,
1277) -> (TweakedPublicKey, Parity, ScriptBuf) {
1278 let (output_key, parity) = internal_key.tap_tweak(secp(), Some(root));
1279 let mut script = Vec::with_capacity(34);
1280 script.extend_from_slice(&[0x51, 0x20]);
1281 script.extend_from_slice(&output_key.serialize());
1282 (output_key, parity, ScriptBuf::from_bytes(script))
1283}
1284
1285fn control_block(
1286 internal_key: XOnlyPublicKey,
1287 parity: Parity,
1288 merkle_path: &[TapNodeHash],
1289) -> Vec<u8> {
1290 let mut control = Vec::with_capacity(33 + merkle_path.len() * 32);
1291 control.push(LeafVersion::TapScript.to_consensus() | u8::from(parity));
1292 control.extend_from_slice(&internal_key.serialize());
1293 for node in merkle_path {
1294 control.extend_from_slice(node.as_ref());
1295 }
1296 control
1297}
1298
1299fn push_key(script: &mut Vec<u8>, key: XOnlyPublicKey) {
1300 script.push(0x20);
1301 script.extend_from_slice(&key.serialize());
1302}
1303
1304fn push_num(script: &mut Vec<u8>, n: u32) -> Result<()> {
1305 if n > 0x7fff_ffff {
1306 return Err(Error::ScriptNumber(n));
1307 }
1308 if n == 0 {
1309 script.push(0x00);
1310 return Ok(());
1311 }
1312 if n <= 16 {
1313 script.push(0x50 + n as u8);
1314 return Ok(());
1315 }
1316 let mut value = n;
1317 let mut bytes = Vec::new();
1318 while value > 0 {
1319 bytes.push((value & 0xff) as u8);
1320 value >>= 8;
1321 }
1322 if bytes.last().is_some_and(|byte| byte & 0x80 != 0) {
1323 bytes.push(0);
1324 }
1325 script.push(bytes.len() as u8);
1326 script.extend_from_slice(&bytes);
1327 Ok(())
1328}
1329
1330fn one_input_tx(
1331 outpoint: OutPoint,
1332 sequence: u32,
1333 lock_time: LockTime,
1334 output: Vec<TxOut>,
1335) -> Transaction {
1336 Transaction {
1337 version: Version::TWO,
1338 lock_time,
1339 input: vec![TxIn {
1340 previous_output: outpoint,
1341 script_sig: ScriptBuf::new(),
1342 sequence: Sequence::from_consensus(sequence),
1343 witness: Witness::new(),
1344 }],
1345 output,
1346 }
1347}
1348
1349#[cfg(test)]
1350mod tests {
1351 use super::*;
1352 use bitcoin::consensus::encode::{deserialize, serialize};
1353 use bitcoin::hashes::Hash;
1354 use bitcoin::Txid;
1355
1356 fn key(byte: u8) -> SecretKey {
1357 SecretKey::from_slice(&[byte; 32]).unwrap()
1358 }
1359
1360 fn public(key: &SecretKey) -> XOnlyPublicKey {
1361 Keypair::from_secret_key(secp(), key).x_only_public_key().0
1362 }
1363
1364 fn channel() -> (Channel, SecretKey, SecretKey) {
1365 let a = key(0x11);
1366 let b = key(0x22);
1367 let channel = Channel::new(
1368 public(&a),
1369 public(&b),
1370 OutPoint {
1371 txid: Txid::from_byte_array([0xab; 32]),
1372 vout: 1,
1373 },
1374 Amount::from_sat(100_000),
1375 Amount::from_sat(DEFAULT_FEE),
1376 DEFAULT_DELAY,
1377 )
1378 .unwrap();
1379 (channel, a, b)
1380 }
1381
1382 #[test]
1383 fn scripts_match_hitch_golden_vectors() {
1384 let (channel, a, b) = channel();
1385 assert_eq!(
1386 internal_key().unwrap().to_string(),
1387 "257ea3139d352eb3705808452e5268c6b932af03a03e09bfe9017bb8be3b3ece"
1388 );
1389 assert_eq!(
1390 channel.funding_script.script_pubkey.to_hex_string(),
1391 "51201c17bc9ec453913ba2e233ea77bab2fb84e8091fd232568b3eef046717dbed72"
1392 );
1393 assert_eq!(
1394 channel.funding_script.leaf.script.to_hex_string(),
1395 "20466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27ac204f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaba529c"
1396 );
1397 let local = to_local_script(public(&a), public(&b), 6).unwrap();
1398 assert_eq!(
1399 local.script_pubkey.to_hex_string(),
1400 "5120cc7c54c2ace9c757d114cc18cb05b3865580ced8004519f72dbb104fdbc7bbc1"
1401 );
1402 assert_eq!(
1403 local.delayed.script.to_hex_string(),
1404 "56b275204f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac"
1405 );
1406 let htlc = htlc_script(HtlcScriptArgs {
1407 owner_pub: public(&a),
1408 remote_pub: public(&b),
1409 revocation_pub: public(&b),
1410 delay: 6,
1411 payment_hash: [0xab; 32],
1412 expiry: 152_200,
1413 offered_by_owner: true,
1414 })
1415 .unwrap();
1416 assert_eq!(
1417 htlc.script_pubkey.to_hex_string(),
1418 "5120cf2924765a12e11e9fed368d5251a591d40159e95c9d4da98066232542956a20"
1419 );
1420 assert_eq!(
1421 htlc.success.script.to_hex_string(),
1422 "a820abababababababababababababababababababababababababababababababab8820466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27ac"
1423 );
1424 assert_eq!(
1425 htlc.timeout.script.to_hex_string(),
1426 "03885202b17556b275204f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac"
1427 );
1428 }
1429
1430 #[test]
1431 fn commitments_and_close_match_hitch_shape() {
1432 let (channel, _, _) = channel();
1433 let state = ChannelState {
1434 balances: Balances {
1435 a: Amount::from_sat(60_000),
1436 b: Amount::from_sat(40_000),
1437 },
1438 revocation: RevocationKeys {
1439 a: public(&key(0x33)),
1440 b: public(&key(0x44)),
1441 },
1442 htlcs: vec![],
1443 };
1444 let a = channel.commitment(1, Side::A, &state).unwrap();
1445 let b = channel.commitment(1, Side::B, &state).unwrap();
1446 assert_eq!(a.kinds, [OutputKind::ToLocal, OutputKind::ToRemote]);
1447 assert_eq!(a.local_value, Amount::from_sat(59_700));
1448 assert_eq!(a.remote_value, Amount::from_sat(40_000));
1449 assert_eq!(b.local_value, Amount::from_sat(40_000));
1450 assert_eq!(b.remote_value, Amount::from_sat(59_700));
1451 assert_ne!(a.tx.compute_txid(), b.tx.compute_txid());
1452 let close = channel.cooperative_close(&state).unwrap();
1453 assert_eq!(close.output.len(), 2);
1454 assert_eq!(
1455 close.output.iter().map(|o| o.value.to_sat()).sum::<u64>(),
1456 99_700
1457 );
1458 let encoded = serialize(&a.tx);
1459 let decoded: Transaction = deserialize(&encoded).unwrap();
1460 assert_eq!(decoded.compute_txid(), a.tx.compute_txid());
1461 }
1462
1463 #[test]
1464 fn both_sighash_families_sign_the_funding_leaf() {
1465 let (channel, a, b) = channel();
1466 let state = ChannelState {
1467 balances: Balances {
1468 a: Amount::from_sat(60_000),
1469 b: Amount::from_sat(40_000),
1470 },
1471 revocation: RevocationKeys {
1472 a: public(&key(0x33)),
1473 b: public(&key(0x44)),
1474 },
1475 htlcs: vec![],
1476 };
1477 for rules in [SighashRules::Bip341, SighashRules::KnotsUnified] {
1478 let mut commitment = channel.commitment(1, Side::A, &state).unwrap();
1479 let sig_a = channel
1480 .sign_funding(&commitment.tx, &a, rules, &[0; 32])
1481 .unwrap();
1482 let sig_b = channel
1483 .sign_funding(&commitment.tx, &b, rules, &[0; 32])
1484 .unwrap();
1485 assert!(channel.verify_funding(&commitment.tx, &public(&a), &sig_a, rules));
1486 assert!(channel.verify_funding(&commitment.tx, &public(&b), &sig_b, rules));
1487 assert!(!channel.verify_funding(&commitment.tx, &public(&a), &sig_b, rules));
1488 let signatures = BTreeMap::from([(public(&a), sig_a), (public(&b), sig_b)]);
1489 commitment.tx.input[0].witness = channel.funding_witness(&signatures).unwrap();
1490 assert_eq!(commitment.tx.input[0].witness.len(), 4);
1491 let expected_type = if rules == SighashRules::KnotsUnified {
1492 0x21
1493 } else {
1494 0x01
1495 };
1496 assert_eq!(commitment.tx.input[0].witness[0][64], expected_type);
1497 assert_eq!(commitment.tx.input[0].witness[1][64], expected_type);
1498 }
1499 }
1500
1501 #[test]
1502 fn every_htlc_claim_path_has_the_reference_sequence_and_locktime() {
1503 let (channel, a, b) = channel();
1504 let preimage = [0x55; 32];
1505 let state = ChannelState {
1506 balances: Balances {
1507 a: Amount::from_sat(40_000),
1508 b: Amount::from_sat(40_000),
1509 },
1510 revocation: RevocationKeys {
1511 a: public(&key(0x33)),
1512 b: public(&key(0x44)),
1513 },
1514 htlcs: vec![Htlc {
1515 id: 1,
1516 from: Side::A,
1517 amount: Amount::from_sat(20_000),
1518 payment_hash: sha256::Hash::hash(&preimage).to_byte_array(),
1519 expiry: 152_200,
1520 }],
1521 };
1522 let owner_a = channel.commitment(2, Side::A, &state).unwrap();
1523 let owner_b = channel.commitment(2, Side::B, &state).unwrap();
1524 let destination_b = to_remote_script(public(&b));
1525 let success_a = claim_htlc(
1526 &owner_a,
1527 &owner_a.htlcs[0],
1528 HtlcClaim {
1529 path: HtlcClaimPath::Success,
1530 destination: destination_b.clone(),
1531 fee: Amount::from_sat(200),
1532 preimage: Some(preimage),
1533 },
1534 &b,
1535 SighashRules::KnotsUnified,
1536 &[0; 32],
1537 )
1538 .unwrap();
1539 assert_eq!(
1540 success_a.input[0].sequence.to_consensus_u32(),
1541 LOCKTIME_SEQUENCE
1542 );
1543 assert_eq!(success_a.lock_time, LockTime::ZERO);
1544
1545 let timeout_a = claim_htlc(
1546 &owner_a,
1547 &owner_a.htlcs[0],
1548 HtlcClaim {
1549 path: HtlcClaimPath::Timeout,
1550 destination: to_remote_script(public(&a)),
1551 fee: Amount::from_sat(200),
1552 preimage: None,
1553 },
1554 &a,
1555 SighashRules::KnotsUnified,
1556 &[0; 32],
1557 )
1558 .unwrap();
1559 assert_eq!(timeout_a.input[0].sequence.to_consensus_u32(), 6);
1560 assert_eq!(timeout_a.lock_time.to_consensus_u32(), 152_200);
1561
1562 let success_b = claim_htlc(
1563 &owner_b,
1564 &owner_b.htlcs[0],
1565 HtlcClaim {
1566 path: HtlcClaimPath::Success,
1567 destination: destination_b,
1568 fee: Amount::from_sat(200),
1569 preimage: Some(preimage),
1570 },
1571 &b,
1572 SighashRules::KnotsUnified,
1573 &[0; 32],
1574 )
1575 .unwrap();
1576 assert_eq!(success_b.input[0].sequence.to_consensus_u32(), 6);
1577 let revocation = claim_htlc(
1578 &owner_a,
1579 &owner_a.htlcs[0],
1580 HtlcClaim {
1581 path: HtlcClaimPath::Revocation,
1582 destination: to_remote_script(public(&b)),
1583 fee: Amount::from_sat(200),
1584 preimage: None,
1585 },
1586 &key(0x33),
1587 SighashRules::KnotsUnified,
1588 &[0; 32],
1589 )
1590 .unwrap();
1591 assert_eq!(
1592 revocation.input[0].sequence.to_consensus_u32(),
1593 INPUT_SEQUENCE
1594 );
1595 }
1596
1597 #[test]
1599 fn two_party_revocation_key_matches_hitch_golden_vectors() {
1600 let r = key(0x33);
1601 let s = key(0x44);
1602 let p1 = revocation_pub(public(&r), &s).unwrap();
1603 let p2 = revocation_pub(public(&s), &r).unwrap();
1604 assert_eq!(p1, p2, "R + s·G = S + r·G");
1605 assert_eq!(
1606 p1.to_string(),
1607 "4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"
1608 );
1609 let combined = revocation_key(&r, &s).unwrap();
1610 assert_eq!(public(&combined), p1);
1611
1612 let a = key(0x12);
1615 let b = key(0x23);
1616 let channel = Channel::new(
1617 public(&a),
1618 public(&b),
1619 OutPoint {
1620 txid: Txid::from_byte_array([0xab; 32]),
1621 vout: 1,
1622 },
1623 Amount::from_sat(100_000),
1624 Amount::from_sat(DEFAULT_FEE),
1625 DEFAULT_DELAY,
1626 )
1627 .unwrap();
1628 let local = to_local_script(public(&a), p1, 6).unwrap();
1629 let state = ChannelState {
1630 balances: Balances {
1631 a: Amount::from_sat(60_000),
1632 b: Amount::from_sat(40_000),
1633 },
1634 revocation: RevocationKeys { a: p1, b: p1 },
1635 htlcs: vec![],
1636 };
1637 let commitment = channel.commitment(1, Side::A, &state).unwrap();
1638 assert_eq!(commitment.to_local, local);
1639 let prevouts = [TxOut {
1640 value: commitment.local_value,
1641 script_pubkey: local.script_pubkey.clone(),
1642 }];
1643 for rules in [SighashRules::Bip341, SighashRules::KnotsUnified] {
1644 let both = sweep_to_local(
1645 &commitment,
1646 SweepPath::Revocation,
1647 to_remote_script(public(&b)),
1648 Amount::from_sat(200),
1649 &combined,
1650 rules,
1651 &[0; 32],
1652 )
1653 .unwrap();
1654 let sig = LeafSignature::from_slice(&both.input[0].witness[0]).unwrap();
1655 assert!(verify_leaf_signature(
1656 &both,
1657 0,
1658 &prevouts,
1659 local.revocation.leaf_hash,
1660 &p1,
1661 &sig,
1662 rules
1663 ));
1664 for alone in [s, r, a] {
1666 let one = sweep_to_local(
1667 &commitment,
1668 SweepPath::Revocation,
1669 to_remote_script(public(&b)),
1670 Amount::from_sat(200),
1671 &alone,
1672 rules,
1673 &[0; 32],
1674 )
1675 .unwrap();
1676 let sig = LeafSignature::from_slice(&one.input[0].witness[0]).unwrap();
1677 assert!(!verify_leaf_signature(
1678 &one,
1679 0,
1680 &prevouts,
1681 local.revocation.leaf_hash,
1682 &p1,
1683 &sig,
1684 rules
1685 ));
1686 }
1687 }
1688 }
1689
1690 #[test]
1694 fn commitment_and_close_txids_match_hitch_golden_vectors() {
1695 let (channel, _, _) = channel();
1696 let p1 = revocation_pub(public(&key(0x33)), &key(0x44)).unwrap();
1697 let revocation = RevocationKeys { a: p1, b: p1 };
1698 let state = ChannelState {
1699 balances: Balances {
1700 a: Amount::from_sat(60_000),
1701 b: Amount::from_sat(40_000),
1702 },
1703 revocation,
1704 htlcs: vec![],
1705 };
1706 assert_eq!(
1707 channel
1708 .commitment(1, Side::A, &state)
1709 .unwrap()
1710 .tx
1711 .compute_txid()
1712 .to_string(),
1713 "138fe22589d87f2f363907ad877a25f73d20d5415f233c7dcc0dbd9ac2f03c13"
1714 );
1715 assert_eq!(
1716 channel
1717 .commitment(1, Side::B, &state)
1718 .unwrap()
1719 .tx
1720 .compute_txid()
1721 .to_string(),
1722 "56ae7f0fb0b450356387a552da8a952eae772286320e2a94f36ee7c07ce05152"
1723 );
1724 let with_htlc = ChannelState {
1725 balances: Balances {
1726 a: Amount::from_sat(40_000),
1727 b: Amount::from_sat(40_000),
1728 },
1729 revocation,
1730 htlcs: vec![Htlc {
1731 id: 1,
1732 from: Side::A,
1733 amount: Amount::from_sat(20_000),
1734 payment_hash: [0xab; 32],
1735 expiry: 152_200,
1736 }],
1737 };
1738 assert_eq!(
1739 channel
1740 .commitment(2, Side::A, &with_htlc)
1741 .unwrap()
1742 .tx
1743 .compute_txid()
1744 .to_string(),
1745 "4cd9bf1f440e682fcfae5d1bb0e0dd11ff9ce71d738d9ad60e8b6f3e4fe30110"
1746 );
1747 assert_eq!(
1748 channel
1749 .cooperative_close(&state)
1750 .unwrap()
1751 .compute_txid()
1752 .to_string(),
1753 "5a31ac748e2e364b1c11f21b32b85d8e539dbf9baa9f4a8c41c596a41065d1b7"
1754 );
1755 }
1756
1757 #[test]
1758 fn htlc_revocation_leaf_needs_both_secrets_and_preimage_is_read_back() {
1759 let (channel, a, b) = channel();
1760 let r = key(0x33);
1761 let s = key(0x44);
1762 let p1 = revocation_pub(public(&r), &s).unwrap();
1763 let preimage = [0x55; 32];
1764 let payment_hash = sha256::Hash::hash(&preimage).to_byte_array();
1765 let state = ChannelState {
1766 balances: Balances {
1767 a: Amount::from_sat(40_000),
1768 b: Amount::from_sat(40_000),
1769 },
1770 revocation: RevocationKeys { a: p1, b: p1 },
1771 htlcs: vec![Htlc {
1772 id: 1,
1773 from: Side::B,
1774 amount: Amount::from_sat(20_000),
1775 payment_hash,
1776 expiry: 152_200,
1777 }],
1778 };
1779 let commitment = channel.commitment(2, Side::A, &state).unwrap();
1780 let htlc = &commitment.htlcs[0];
1781 let prevouts = [TxOut {
1782 value: htlc.htlc.amount,
1783 script_pubkey: htlc.scripts.script_pubkey.clone(),
1784 }];
1785 let claim = |key: &SecretKey, path, preimage| {
1786 claim_htlc(
1787 &commitment,
1788 htlc,
1789 HtlcClaim {
1790 path,
1791 destination: to_remote_script(public(&b)),
1792 fee: Amount::from_sat(200),
1793 preimage,
1794 },
1795 key,
1796 SighashRules::KnotsUnified,
1797 &[0; 32],
1798 )
1799 .unwrap()
1800 };
1801 let verifies = |tx: &Transaction, leaf: TapLeafHash, pubkey: XOnlyPublicKey| {
1802 let sig = LeafSignature::from_slice(&tx.input[0].witness[0]).unwrap();
1803 verify_leaf_signature(
1804 tx,
1805 0,
1806 &prevouts,
1807 leaf,
1808 &pubkey,
1809 &sig,
1810 SighashRules::KnotsUnified,
1811 )
1812 };
1813 let leaf = htlc.scripts.revocation.leaf_hash;
1814 let combined = revocation_key(&r, &s).unwrap();
1815 assert!(verifies(
1816 &claim(&combined, HtlcClaimPath::Revocation, None),
1817 leaf,
1818 p1
1819 ));
1820 assert!(!verifies(
1821 &claim(&s, HtlcClaimPath::Revocation, None),
1822 leaf,
1823 p1
1824 ));
1825
1826 let success = claim(&a, HtlcClaimPath::Success, Some(preimage));
1829 assert_eq!(preimage_in(&success, &payment_hash), Some(preimage));
1830 let timeout = claim(&b, HtlcClaimPath::Timeout, None);
1831 assert_eq!(preimage_in(&timeout, &payment_hash), None);
1832 }
1833
1834 #[test]
1835 fn proof_of_possession_binds_the_point_and_its_place() {
1836 let secret = key(0x51);
1837 let point = public(&secret);
1838 let proof = pop_sign(&secret, "abababababababab/a/base", &[7; 32]);
1839 assert!(pop_verify(&point, &proof, "abababababababab/a/base"));
1840 assert!(!pop_verify(&point, &proof, "abababababababab/a/0"));
1841 assert!(!pop_verify(
1842 &public(&key(0x52)),
1843 &proof,
1844 "abababababababab/a/base"
1845 ));
1846 let forged = pop_sign(&key(0x54), "abababababababab/a/base", &[7; 32]);
1848 assert!(!pop_verify(&point, &forged, "abababababababab/a/base"));
1849 assert_eq!(PopSignature::from_hex(&proof.to_hex()).unwrap(), proof);
1850 assert!(PopSignature::from_hex("ab").is_err());
1851 }
1852
1853 #[test]
1854 fn even_secret_lifts_to_the_even_point() {
1855 for byte in 1..=40u8 {
1856 let k = key(byte);
1857 let even = even_secret(&k);
1858 assert_eq!(public(&even), public(&k));
1859 assert_eq!(even.x_only_public_key(secp()).1, Parity::Even);
1860 }
1861 }
1862
1863 #[test]
1864 fn state_value_and_policy_bounds_are_checked() {
1865 let (channel, _, _) = channel();
1866 let bad = ChannelState {
1867 balances: Balances {
1868 a: Amount::from_sat(100_001),
1869 b: Amount::ZERO,
1870 },
1871 revocation: RevocationKeys {
1872 a: channel.keys[0],
1873 b: channel.keys[1],
1874 },
1875 htlcs: vec![],
1876 };
1877 assert!(matches!(
1878 channel.commitment(0, Side::A, &bad),
1879 Err(Error::Unbalanced { .. })
1880 ));
1881 assert!(Channel::new(
1882 channel.keys[0],
1883 channel.keys[1],
1884 channel.funding_outpoint,
1885 channel.funding_value,
1886 Amount::from_sat(99),
1887 6,
1888 )
1889 .is_err());
1890 }
1891}