1
2
3use std::{cmp, fmt, io, iter};
4use std::collections::{HashMap, VecDeque};
5
6use bitcoin::hashes::{sha256, Hash};
7use bitcoin::{
8 taproot, Amount, OutPoint, ScriptBuf, Sequence, TapLeafHash, Transaction, TxIn, TxOut, Weight, Witness
9};
10use bitcoin::secp256k1::{schnorr, Keypair, PublicKey, XOnlyPublicKey};
11use bitcoin::sighash::{self, SighashCache, TapSighash, TapSighashType};
12use secp256k1_musig::musig::{AggregatedNonce, PartialSignature, PublicNonce, SecretNonce};
13
14use bitcoin_ext::{fee, BlockDelta, BlockHeight, TaprootSpendInfoExt, TransactionExt, TxOutExt};
15
16use crate::error::IncorrectSigningKeyError;
17use crate::{musig, scripts, Vtxo, VtxoId, VtxoPolicy, VtxoRequest, SECP};
18use crate::encode::{ProtocolDecodingError, ProtocolEncoding, ReadExt, WriteExt};
19use crate::tree::{self, Tree};
20use crate::vtxo::{self, GenesisItem, GenesisTransition, MaybePreimage};
21
22
23pub type UnlockHash = sha256::Hash;
25
26pub type UnlockPreimage = [u8; 32];
28
29pub const NODE_SPEND_WEIGHT: Weight = Weight::from_wu(140);
31
32pub fn expiry_clause(server_pubkey: PublicKey, expiry_height: BlockHeight) -> ScriptBuf {
34 let pk = server_pubkey.x_only_public_key().0;
35 scripts::timelock_sign(expiry_height, pk)
36}
37
38pub fn unlock_clause(pubkey: XOnlyPublicKey, unlock_hash: UnlockHash) -> ScriptBuf {
42 scripts::hash_and_sign(unlock_hash, pubkey)
43}
44
45pub fn leaf_cosign_taproot(
52 user_pubkey: PublicKey,
53 server_pubkey: PublicKey,
54 expiry_height: BlockHeight,
55 unlock_hash: UnlockHash,
56) -> taproot::TaprootSpendInfo {
57 let agg_pk = musig::combine_keys([user_pubkey, server_pubkey]);
58 taproot::TaprootBuilder::new()
59 .add_leaf(1, expiry_clause(server_pubkey, expiry_height)).unwrap()
60 .add_leaf(1, unlock_clause(agg_pk, unlock_hash)).unwrap()
61 .finalize(&SECP, agg_pk).unwrap()
62}
63
64pub fn cosign_taproot(
66 agg_pk: XOnlyPublicKey,
67 server_pubkey: PublicKey,
68 expiry_height: BlockHeight,
69) -> taproot::TaprootSpendInfo {
70 taproot::TaprootBuilder::new()
71 .add_leaf(0, expiry_clause(server_pubkey, expiry_height)).unwrap()
72 .finalize(&SECP, agg_pk).unwrap()
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub struct VtxoLeafSpec {
77 pub vtxo: VtxoRequest,
79
80 pub cosign_pubkey: Option<PublicKey>,
87
88 pub unlock_hash: UnlockHash,
90}
91
92#[derive(Debug, Clone, Eq, PartialEq)]
94pub struct VtxoTreeSpec {
95 pub vtxos: Vec<VtxoLeafSpec>,
96 pub expiry_height: BlockHeight,
97 pub server_pubkey: PublicKey,
98 pub exit_delta: BlockDelta,
99 pub global_cosign_pubkeys: Vec<PublicKey>,
100}
101
102#[derive(Clone, Copy)]
103enum ChildSpec<'a> {
104 Leaf {
105 spec: &'a VtxoLeafSpec,
106 },
107 Internal {
108 output_value: Amount,
109 agg_pk: XOnlyPublicKey,
110 },
111}
112
113impl VtxoTreeSpec {
114 pub fn new(
115 vtxos: Vec<VtxoLeafSpec>,
116 server_pubkey: PublicKey,
117 expiry_height: BlockHeight,
118 exit_delta: BlockDelta,
119 global_cosign_pubkeys: Vec<PublicKey>,
120 ) -> VtxoTreeSpec {
121 assert_ne!(vtxos.len(), 0);
122 VtxoTreeSpec { vtxos, server_pubkey, expiry_height, exit_delta, global_cosign_pubkeys }
123 }
124
125 pub fn nb_leaves(&self) -> usize {
126 self.vtxos.len()
127 }
128
129 pub fn nb_nodes(&self) -> usize {
130 Tree::nb_nodes_for_leaves(self.nb_leaves())
131 }
132
133 pub fn nb_internal_nodes(&self) -> usize {
134 Tree::nb_nodes_for_leaves(self.nb_leaves()).checked_sub(self.nb_leaves())
135 .expect("tree can't have less nodes than leaves")
136 }
137
138 pub fn iter_vtxos(&self) -> impl Iterator<Item = &VtxoLeafSpec> {
139 self.vtxos.iter()
140 }
141
142 pub fn leaf_idx_of(&self, leaf_spec: &VtxoLeafSpec) -> Option<usize> {
144 self.vtxos.iter().position(|e| e == leaf_spec)
145 }
146
147 pub fn leaf_idx_of_req(&self, vtxo_request: &VtxoRequest) -> Option<usize> {
152 self.vtxos.iter().position(|e| e.vtxo == *vtxo_request)
153 }
154
155 pub fn total_required_value(&self) -> Amount {
160 self.vtxos.iter().map(|d| d.vtxo.amount).sum::<Amount>()
161 }
162
163 pub fn leaf_taproot(
165 &self,
166 user_pubkey: PublicKey,
167 unlock_hash: UnlockHash,
168 ) -> taproot::TaprootSpendInfo {
169 leaf_cosign_taproot(user_pubkey, self.server_pubkey, self.expiry_height, unlock_hash)
170 }
171
172 pub fn internal_taproot(&self, agg_pk: XOnlyPublicKey) -> taproot::TaprootSpendInfo {
174 cosign_taproot(agg_pk, self.server_pubkey, self.expiry_height)
175 }
176
177 pub fn funding_tx_cosign_pubkey(&self) -> XOnlyPublicKey {
181 let keys = self.vtxos.iter()
182 .filter_map(|v| v.cosign_pubkey)
183 .chain(self.global_cosign_pubkeys.iter().copied());
184 musig::combine_keys(keys)
185 }
186
187 pub fn funding_tx_script_pubkey(&self) -> ScriptBuf {
191 let agg_pk = self.funding_tx_cosign_pubkey();
192 self.internal_taproot(agg_pk).script_pubkey()
193 }
194
195 pub fn funding_tx_txout(&self) -> TxOut {
199 TxOut {
200 script_pubkey: self.funding_tx_script_pubkey(),
201 value: self.total_required_value(),
202 }
203 }
204
205 fn node_tx<'a>(
210 &self,
211 children: impl Iterator<Item = ChildSpec<'a>>,
212 ) -> Transaction {
213 Transaction {
214 version: bitcoin::transaction::Version(3),
215 lock_time: bitcoin::absolute::LockTime::ZERO,
216 input: vec![TxIn {
217 previous_output: OutPoint::null(), sequence: Sequence::ZERO,
219 script_sig: ScriptBuf::new(),
220 witness: Witness::new(),
221 }],
222 output: children.map(|child| match child {
223 ChildSpec::Leaf { spec } => {
224 let taproot = self.leaf_taproot(
225 spec.vtxo.policy.user_pubkey(),
226 spec.unlock_hash,
227 );
228 TxOut {
229 script_pubkey: taproot.script_pubkey(),
230 value: spec.vtxo.amount,
231 }
232 },
233 ChildSpec::Internal { output_value, agg_pk } => {
234 let taproot = self.internal_taproot(agg_pk);
235 TxOut {
236 script_pubkey: taproot.script_pubkey(),
237 value: output_value,
238 }
239 },
240 }).chain(Some(fee::fee_anchor())).collect(),
241 }
242 }
243
244 fn leaf_tx(&self, vtxo: &VtxoRequest) -> Transaction {
245 let txout = TxOut {
246 value: vtxo.amount,
247 script_pubkey: vtxo.policy.script_pubkey(self.server_pubkey, self.exit_delta, self.expiry_height),
248 };
249
250 vtxo::create_exit_tx(OutPoint::null(), txout, None)
251 }
252
253 pub fn cosign_agg_pks(&self)
257 -> impl Iterator<Item = XOnlyPublicKey> + iter::DoubleEndedIterator + iter::ExactSizeIterator + '_
258 {
259 Tree::new(self.nb_leaves()).into_iter().map(|node| {
260 if node.is_leaf() {
261 musig::combine_keys([
262 self.vtxos[node.idx()].vtxo.policy.user_pubkey(),
263 self.server_pubkey,
264 ])
265 } else {
266 musig::combine_keys(
267 node.leaves().filter_map(|i| self.vtxos[i].cosign_pubkey)
268 .chain(self.global_cosign_pubkeys.iter().copied())
269 )
270 }
271 })
272 }
273
274 pub fn unsigned_transactions(&self, utxo: OutPoint) -> Vec<Transaction> {
276 let tree = Tree::new(self.nb_leaves());
277
278 let cosign_agg_pks = self.cosign_agg_pks().collect::<Vec<_>>();
279
280 let mut txs = Vec::<Transaction>::with_capacity(tree.nb_nodes());
281 for node in tree.iter() {
282 let tx = if node.is_leaf() {
283 self.leaf_tx(&self.vtxos[node.idx()].vtxo).clone()
284 } else {
285 let mut buf = [None; tree::RADIX];
286 for (idx, child) in node.children().enumerate() {
287 let child = if let Some(spec) = self.vtxos.get(child) {
288 ChildSpec::Leaf { spec }
289 } else {
290 ChildSpec::Internal {
291 output_value: txs[child].output_value(),
292 agg_pk: cosign_agg_pks[child],
293 }
294 };
295 buf[idx] = Some(child);
296 }
297 self.node_tx(buf.iter().filter_map(|x| *x))
298 };
299 txs.push(tx.clone());
300 };
301
302 txs.last_mut().unwrap().input[0].previous_output = utxo;
304 for node in tree.iter().rev() {
305 let txid = txs[node.idx()].compute_txid();
306 for (i, child) in node.children().enumerate() {
307 let point = OutPoint::new(txid, i as u32);
308 txs[child].input[0].previous_output = point;
309 }
310 }
311
312 txs
313 }
314
315 pub fn final_transactions(
319 &self,
320 utxo: OutPoint,
321 internal_signatures: &[schnorr::Signature],
322 ) -> Vec<Transaction> {
323 let mut txs = self.unsigned_transactions(utxo);
324 for (tx, sig) in txs.iter_mut().skip(self.nb_leaves()).zip(internal_signatures) {
325 tx.input[0].witness.push(&sig[..]);
326 }
327 txs
328 }
329
330 pub fn calculate_cosign_agg_nonces(
334 &self,
335 leaf_cosign_nonces: &HashMap<PublicKey, Vec<PublicNonce>>,
336 global_signer_cosign_nonces: &[impl AsRef<[PublicNonce]>],
337 ) -> Result<Vec<AggregatedNonce>, String> {
338 if global_signer_cosign_nonces.len() != self.global_cosign_pubkeys.len() {
339 return Err("missing global signer nonces".into());
340 }
341
342 Tree::new(self.nb_leaves()).iter_internal().enumerate().map(|(idx, node)| {
343 let mut nonces = Vec::new();
344 for pk in node.leaves().filter_map(|i| self.vtxos[i].cosign_pubkey) {
345 nonces.push(leaf_cosign_nonces.get(&pk)
346 .ok_or_else(|| format!("missing nonces for leaf pk {}", pk))?
347 .get(node.internal_level())
350 .ok_or_else(|| format!("not enough nonces for leaf_pk {}", pk))?
351 );
352 }
353 for glob in global_signer_cosign_nonces {
354 nonces.push(glob.as_ref().get(idx).ok_or("not enough global cosign nonces")?);
355 }
356 Ok(musig::nonce_agg(&nonces))
357 }).collect()
358 }
359
360 pub fn into_unsigned_tree(
365 self,
366 utxo: OutPoint,
367 ) -> UnsignedVtxoTree {
368 UnsignedVtxoTree::new(self, utxo)
369 }
370}
371
372#[derive(Debug, Clone)]
376pub struct UnsignedVtxoTree {
377 pub spec: VtxoTreeSpec,
378 pub utxo: OutPoint,
379
380 pub cosign_agg_pks: Vec<XOnlyPublicKey>,
384 pub txs: Vec<Transaction>,
386 pub internal_sighashes: Vec<TapSighash>,
389
390 tree: Tree,
391}
392
393impl UnsignedVtxoTree {
394 pub fn new(
395 spec: VtxoTreeSpec,
396 utxo: OutPoint,
397 ) -> UnsignedVtxoTree {
398 let tree = Tree::new(spec.nb_leaves());
399
400 let cosign_agg_pks = spec.cosign_agg_pks().collect::<Vec<_>>();
401 let txs = spec.unsigned_transactions(utxo);
402
403 let root_txout = spec.funding_tx_txout();
404 let internal_sighashes = tree.iter_internal().map(|node| {
405 let prev = if let Some((parent, sibling_idx))
406 = tree.parent_idx_of_with_sibling_idx(node.idx())
407 {
408 assert!(!node.is_root());
409 &txs[parent].output[sibling_idx]
410 } else {
411 assert!(node.is_root());
412 &root_txout
413 };
414
415 let mut shc = SighashCache::new(&txs[node.idx()]);
416 shc.taproot_key_spend_signature_hash(
417 0, &sighash::Prevouts::All(&[prev]),
419 TapSighashType::Default,
420 ).expect("sighash error")
421 }).collect();
422
423 UnsignedVtxoTree { spec, utxo, txs, internal_sighashes, cosign_agg_pks, tree }
424 }
425
426 pub fn nb_leaves(&self) -> usize {
427 self.tree.nb_leaves()
428 }
429
430 pub fn nb_cosigned_leaves(&self) -> usize {
432 self.spec.vtxos.iter()
433 .filter(|v| v.cosign_pubkey.is_some())
434 .count()
435 }
436
437 pub fn nb_nodes(&self) -> usize {
438 self.tree.nb_nodes()
439 }
440
441 pub fn nb_internal_nodes(&self) -> usize {
442 self.tree.nb_internal_nodes()
443 }
444
445 pub fn cosign_branch(
458 &self,
459 cosign_agg_nonces: &[AggregatedNonce],
460 leaf_idx: usize,
461 cosign_key: &Keypair,
462 cosign_sec_nonces: Vec<SecretNonce>,
463 ) -> Result<Vec<PartialSignature>, IncorrectSigningKeyError> {
464 let req = self.spec.vtxos.get(leaf_idx).expect("leaf idx out of bounds");
465 if Some(cosign_key.public_key()) != req.cosign_pubkey {
466 return Err(IncorrectSigningKeyError {
467 required: req.cosign_pubkey,
468 provided: cosign_key.public_key(),
469 });
470 }
471
472 let mut nonce_iter = cosign_sec_nonces.into_iter().enumerate();
473 let mut ret = Vec::with_capacity(self.tree.root().level() + 1);
474 for node in self.tree.iter_branch(leaf_idx).skip(1) {
476 let sec_nonce = loop {
481 let next = nonce_iter.next().expect("level overflow");
482 if next.0 == node.internal_level() {
483 break next.1;
484 }
485 };
486
487 let cosign_pubkeys = node.leaves()
488 .filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
489 .chain(self.spec.global_cosign_pubkeys.iter().copied());
490 let sighash = self.internal_sighashes[node.internal_idx()];
491
492 let agg_pk = self.cosign_agg_pks[node.idx()];
493 let tweak = self.spec.internal_taproot(agg_pk).tap_tweak().to_byte_array();
494 let sig = musig::partial_sign(
495 cosign_pubkeys,
496 cosign_agg_nonces[node.internal_idx()],
497 &cosign_key,
498 sec_nonce,
499 sighash.to_byte_array(),
500 Some(tweak),
501 None,
502 ).0;
503 ret.push(sig);
504 }
505
506 Ok(ret)
507 }
508
509 pub fn cosign_tree(
515 &self,
516 cosign_agg_nonces: &[AggregatedNonce],
517 keypair: &Keypair,
518 cosign_sec_nonces: Vec<SecretNonce>,
519 ) -> Vec<PartialSignature> {
520 debug_assert_eq!(cosign_agg_nonces.len(), self.nb_internal_nodes());
521 debug_assert_eq!(cosign_sec_nonces.len(), self.nb_internal_nodes());
522
523 let nonces = cosign_sec_nonces.into_iter().zip(cosign_agg_nonces);
524 self.tree.iter_internal().zip(nonces).map(|(node, (sec_nonce, agg_nonce))| {
525 let sighash = self.internal_sighashes[node.internal_idx()];
526
527 let cosign_pubkeys = node.leaves()
528 .filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
529 .chain(self.spec.global_cosign_pubkeys.iter().copied());
530 let agg_pk = self.cosign_agg_pks[node.idx()];
531 debug_assert_eq!(agg_pk, musig::combine_keys(cosign_pubkeys.clone()));
532 let taproot = self.spec.internal_taproot(agg_pk);
533 musig::partial_sign(
534 cosign_pubkeys,
535 *agg_nonce,
536 &keypair,
537 sec_nonce,
538 sighash.to_byte_array(),
539 Some(taproot.tap_tweak().to_byte_array()),
540 None,
541 ).0
542 }).collect()
543 }
544
545 fn verify_internal_node_cosign_partial_sig(
547 &self,
548 node: &tree::Node,
549 pk: PublicKey,
550 agg_nonces: &[AggregatedNonce],
551 part_sig: PartialSignature,
552 pub_nonce: PublicNonce,
553 ) -> Result<(), CosignSignatureError> {
554 debug_assert!(!node.is_leaf());
555
556 let sighash = self.internal_sighashes[node.internal_idx()];
557
558 let key_agg = {
559 let cosign_pubkeys = node.leaves()
560 .filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
561 .chain(self.spec.global_cosign_pubkeys.iter().copied());
562 let agg_pk = self.cosign_agg_pks[node.idx()];
563 let taproot = self.spec.internal_taproot(agg_pk);
564 let taptweak = taproot.tap_tweak().to_byte_array();
565 musig::tweaked_key_agg(cosign_pubkeys, taptweak).0
566 };
567 let agg_nonce = agg_nonces.get(node.internal_idx())
568 .ok_or(CosignSignatureError::NotEnoughNonces)?;
569 let session = musig::Session::new(&key_agg, *agg_nonce, &sighash.to_byte_array());
570 let ok = session.partial_verify(&key_agg, &part_sig, &pub_nonce, musig::pubkey_to(pk));
571 if !ok {
572 return Err(CosignSignatureError::invalid_sig(pk));
573 }
574 Ok(())
575 }
576
577 pub fn verify_branch_cosign_partial_sigs(
582 &self,
583 cosign_agg_nonces: &[AggregatedNonce],
584 request: &VtxoLeafSpec,
585 cosign_pub_nonces: &[PublicNonce],
586 cosign_part_sigs: &[PartialSignature],
587 ) -> Result<(), String> {
588 assert_eq!(cosign_agg_nonces.len(), self.nb_internal_nodes());
589
590 let cosign_pubkey = request.cosign_pubkey.ok_or("no cosign pubkey for request")?;
591 let leaf_idx = self.spec.leaf_idx_of(request).ok_or("request not in tree")?;
592
593 let internal_branch = self.tree.iter_branch(leaf_idx).skip(1);
595
596 match internal_branch.clone().count().cmp(&cosign_part_sigs.len()) {
598 cmp::Ordering::Less => return Err("too few partial signatures".into()),
599 cmp::Ordering::Greater => return Err("too many partial signatures".into()),
600 cmp::Ordering::Equal => {},
601 }
602
603 let mut part_sigs_iter = cosign_part_sigs.iter();
604 let mut pub_nonce_iter = cosign_pub_nonces.iter().enumerate();
605 for node in internal_branch {
606 let pub_nonce = loop {
607 let next = pub_nonce_iter.next().ok_or("not enough pub nonces")?;
608 if next.0 == node.internal_level() {
609 break next.1;
610 }
611 };
612 self.verify_internal_node_cosign_partial_sig(
613 node,
614 cosign_pubkey,
615 cosign_agg_nonces,
616 part_sigs_iter.next().ok_or("not enough sigs")?.clone(),
617 *pub_nonce,
618 ).map_err(|e| format!("part sig verification failed: {}", e))?;
619 }
620
621 Ok(())
622 }
623
624 pub fn verify_global_cosign_partial_sigs(
629 &self,
630 pk: PublicKey,
631 agg_nonces: &[AggregatedNonce],
632 pub_nonces: &[PublicNonce],
633 part_sigs: &[PartialSignature],
634 ) -> Result<(), CosignSignatureError> {
635 for node in self.tree.iter_internal() {
636 let sigs = *part_sigs.get(node.internal_idx())
637 .ok_or_else(|| CosignSignatureError::missing_sig(pk))?;
638 let nonces = *pub_nonces.get(node.internal_idx())
639 .ok_or_else(|| CosignSignatureError::NotEnoughNonces)?;
640 self.verify_internal_node_cosign_partial_sig(node, pk, agg_nonces, sigs, nonces)?;
641 }
642
643 Ok(())
644 }
645
646 pub fn combine_partial_signatures(
655 &self,
656 cosign_agg_nonces: &[AggregatedNonce],
657 branch_part_sigs: &HashMap<PublicKey, Vec<PartialSignature>>,
658 global_signer_part_sigs: &[impl AsRef<[PartialSignature]>],
659 ) -> Result<Vec<schnorr::Signature>, CosignSignatureError> {
660 let mut leaf_part_sigs = branch_part_sigs.iter()
662 .map(|(pk, sigs)| (pk, sigs.iter().collect()))
663 .collect::<HashMap<_, VecDeque<_>>>();
664
665 if global_signer_part_sigs.len() != self.spec.global_cosign_pubkeys.len() {
666 return Err(CosignSignatureError::Invalid(
667 "invalid nb of global cosigner partial signatures",
668 ));
669 }
670 for (pk, sigs) in self.spec.global_cosign_pubkeys.iter().zip(global_signer_part_sigs) {
671 if sigs.as_ref().len() != self.nb_internal_nodes() {
672 return Err(CosignSignatureError::MissingSignature { pk: *pk });
675 }
676 }
677
678 let max_level = match self.tree.root().is_leaf() {
679 true => 0,
680 false => self.tree.root().internal_level(),
681 };
682 self.tree.iter_internal().map(|node| {
683 let mut cosign_pks = Vec::with_capacity(max_level + 1);
684 let mut part_sigs = Vec::with_capacity(max_level + 1);
685 for leaf in node.leaves() {
686 if let Some(cosign_pk) = self.spec.vtxos[leaf].cosign_pubkey {
687 let part_sig = leaf_part_sigs.get_mut(&cosign_pk)
688 .ok_or(CosignSignatureError::missing_sig(cosign_pk))?
689 .pop_front()
690 .ok_or(CosignSignatureError::missing_sig(cosign_pk))?;
691 cosign_pks.push(cosign_pk);
692 part_sigs.push(part_sig);
693 }
694 }
695 cosign_pks.extend(&self.spec.global_cosign_pubkeys);
697 for sigs in global_signer_part_sigs {
698 part_sigs.push(sigs.as_ref().get(node.internal_idx()).expect("checked before"));
699 }
700
701 let agg_pk = self.cosign_agg_pks[node.idx()];
702 let taproot = self.spec.internal_taproot(agg_pk);
703 let agg_nonce = *cosign_agg_nonces.get(node.internal_idx())
704 .ok_or(CosignSignatureError::NotEnoughNonces)?;
705 let sighash = self.internal_sighashes[node.internal_idx()].to_byte_array();
706 let tweak = taproot.tap_tweak().to_byte_array();
707 Ok(musig::combine_partial_signatures(
708 cosign_pks, agg_nonce, sighash, Some(tweak), &part_sigs,
709 ))
710 }).collect()
711 }
712
713 pub fn verify_cosign_sigs(
717 &self,
718 signatures: &[schnorr::Signature],
719 ) -> Result<(), XOnlyPublicKey> {
720 for node in self.tree.iter_internal() {
721 let sighash = self.internal_sighashes[node.internal_idx()];
722 let agg_pk = &self.cosign_agg_pks[node.idx()];
723 let pk = self.spec.internal_taproot(*agg_pk).output_key().to_x_only_public_key();
724 let sig = signatures.get(node.internal_idx()).ok_or_else(|| pk)?;
725 if SECP.verify_schnorr(sig, &sighash.into(), &pk).is_err() {
726 return Err(pk);
727 }
728 }
729 Ok(())
730 }
731
732 pub fn into_signed_tree(
736 self,
737 signatures: Vec<schnorr::Signature>,
738 ) -> SignedVtxoTreeSpec {
739 SignedVtxoTreeSpec {
740 spec: self.spec,
741 utxo: self.utxo,
742 cosign_sigs: signatures,
743 }
744 }
745}
746
747#[derive(PartialEq, Eq, thiserror::Error)]
749pub enum CosignSignatureError {
750 #[error("missing cosign signature from pubkey {pk}")]
751 MissingSignature { pk: PublicKey },
752 #[error("invalid cosign signature from pubkey {pk}")]
753 InvalidSignature { pk: PublicKey },
754 #[error("not enough nonces")]
755 NotEnoughNonces,
756 #[error("invalid cosign signatures: {0}")]
757 Invalid(&'static str),
758}
759
760impl fmt::Debug for CosignSignatureError {
761 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
762 fmt::Display::fmt(self, f)
763 }
764}
765
766impl CosignSignatureError {
767 fn missing_sig(cosign_pk: PublicKey) -> CosignSignatureError {
768 CosignSignatureError::MissingSignature { pk: cosign_pk }
769 }
770 fn invalid_sig(cosign_pk: PublicKey) -> CosignSignatureError {
771 CosignSignatureError::InvalidSignature { pk: cosign_pk }
772 }
773}
774
775#[derive(Debug, Clone, PartialEq)]
777pub struct SignedVtxoTreeSpec {
778 pub spec: VtxoTreeSpec,
779 pub utxo: OutPoint,
780 pub cosign_sigs: Vec<schnorr::Signature>,
782}
783
784impl SignedVtxoTreeSpec {
785 pub fn new(
787 spec: VtxoTreeSpec,
788 utxo: OutPoint,
789 cosign_signatures: Vec<schnorr::Signature>,
790 ) -> SignedVtxoTreeSpec {
791 SignedVtxoTreeSpec { spec, utxo, cosign_sigs: cosign_signatures }
792 }
793
794 pub fn nb_leaves(&self) -> usize {
795 self.spec.nb_leaves()
796 }
797
798 pub fn exit_branch(&self, leaf_idx: usize) -> Vec<Transaction> {
805 let txs = self.all_final_txs();
806 let tree = Tree::new(self.spec.nb_leaves());
807 let mut ret = tree.iter_branch(leaf_idx)
808 .map(|n| txs[n.idx()].clone())
809 .collect::<Vec<_>>();
810 ret.reverse();
811 ret
812 }
813
814 pub fn all_final_txs(&self) -> Vec<Transaction> {
816 self.spec.final_transactions(self.utxo, &self.cosign_sigs)
817 }
818
819 pub fn into_cached_tree(self) -> CachedSignedVtxoTree {
820 CachedSignedVtxoTree {
821 txs: self.all_final_txs(),
822 spec: self,
823 }
824 }
825}
826
827pub struct CachedSignedVtxoTree {
831 pub spec: SignedVtxoTreeSpec,
832 pub txs: Vec<Transaction>,
834}
835
836impl CachedSignedVtxoTree {
837 pub fn exit_branch(&self, leaf_idx: usize) -> Vec<&Transaction> {
841 let tree = Tree::new(self.spec.spec.nb_leaves());
842 let mut ret = tree.iter_branch(leaf_idx)
843 .map(|n| &self.txs[n.idx()])
844 .collect::<Vec<_>>();
845 ret.reverse();
846 ret
847 }
848
849 pub fn nb_leaves(&self) -> usize {
850 self.spec.nb_leaves()
851 }
852
853 pub fn all_final_txs(&self) -> &[Transaction] {
855 &self.txs
856 }
857
858 pub fn build_vtxo(&self, leaf_idx: usize) -> Vtxo {
862 let req = self.spec.spec.vtxos.get(leaf_idx).expect("index is not a leaf");
863 let genesis = {
864 let mut genesis = Vec::new();
865
866 let tree = Tree::new(self.spec.spec.nb_leaves());
867 let mut branch = tree.iter_branch(leaf_idx);
868
869 let leaf_node = branch.next().unwrap();
871 genesis.push(GenesisItem {
872 transition: GenesisTransition::HashLockedCosigned {
873 user_pubkey: req.vtxo.policy.user_pubkey(),
874 signature: None,
875 unlock: MaybePreimage::Hash(req.unlock_hash),
876 },
877 output_idx: 0,
878 other_outputs: vec![],
879 });
880
881 let mut last_node = leaf_node.idx();
883 for node in branch {
884 let transition = GenesisTransition::Cosigned {
885 pubkeys: node.leaves()
886 .filter_map(|i| self.spec.spec.vtxos[i].cosign_pubkey)
887 .chain(self.spec.spec.global_cosign_pubkeys.iter().copied())
888 .collect(),
889 signature: *self.spec.cosign_sigs.get(node.internal_idx())
890 .expect("enough sigs for all nodes"),
891 };
892 let output_idx = node.children().position(|child_idx| last_node == child_idx)
893 .expect("last node should be our child") as u8;
894 let other_outputs = self.txs.get(node.idx()).expect("we have all txs")
895 .output.iter()
896 .enumerate()
897 .filter(|(i, o)| !o.is_p2a_fee_anchor() && *i != output_idx as usize)
898 .map(|(_i, o)| o).cloned().collect();
899 genesis.push(GenesisItem { transition, output_idx, other_outputs });
900 last_node = node.idx();
901 }
902 genesis.reverse();
903
904
905 genesis
906 };
907
908 Vtxo {
909 amount: req.vtxo.amount,
910 expiry_height: self.spec.spec.expiry_height,
911 server_pubkey: self.spec.spec.server_pubkey,
912 exit_delta: self.spec.spec.exit_delta,
913 anchor_point: self.spec.utxo,
914 genesis: genesis,
915 policy: req.vtxo.policy.clone(),
916 point: {
917 let leaf_tx = self.txs.get(leaf_idx).expect("leaf idx exists");
918 OutPoint::new(leaf_tx.compute_txid(), 0)
919 },
920 }
921 }
922
923 pub fn all_vtxos(&self) -> impl Iterator<Item = Vtxo> + ExactSizeIterator + '_ {
925 (0..self.nb_leaves()).map(|idx| self.build_vtxo(idx))
926 }
927}
928
929pub fn hashlocked_leaf_sighash(
931 leaf_tx: &Transaction,
932 user_pubkey: PublicKey,
933 server_pubkey: PublicKey,
934 unlock_hash: UnlockHash,
935 prev_txout: &TxOut,
936) -> TapSighash {
937 let agg_pk = musig::combine_keys([user_pubkey, server_pubkey]);
938 let clause = unlock_clause(agg_pk, unlock_hash);
939 let leaf_hash = TapLeafHash::from_script(&clause, bitcoin::taproot::LeafVersion::TapScript);
940 let mut shc = SighashCache::new(leaf_tx);
941 shc.taproot_script_spend_signature_hash(
942 0, &sighash::Prevouts::All(&[prev_txout]),
944 leaf_hash,
945 TapSighashType::Default,
946 ).expect("sighash error")
947}
948
949fn hashlocked_leaf_sighash_from_vtxo(
955 vtxo: &Vtxo,
956 chain_anchor: &Transaction,
957) -> TapSighash {
958 assert_eq!(chain_anchor.compute_txid(), vtxo.chain_anchor().txid);
959 let last_genesis = vtxo.genesis.last().expect("at least one genesis item");
960 let (user_pubkey, unlock_hash) = match last_genesis.transition {
961 GenesisTransition::HashLockedCosigned { user_pubkey, unlock, .. } => {
962 (user_pubkey, unlock.hash())
963 },
964 _ => panic!("VTXO is not a HashLockedCosigned VTXO")
965 };
966 debug_assert_eq!(user_pubkey, vtxo.user_pubkey());
967
968 let mut preleaf_txout = chain_anchor.output[vtxo.chain_anchor().vout as usize].clone();
970 let mut leaf_tx = None;
971 let mut peekable_iter = vtxo.transactions().peekable();
972 while let Some(item) = peekable_iter.next() {
973 if peekable_iter.peek().is_some() {
976 preleaf_txout = item.tx.output[item.output_idx].clone();
977 }
978
979 if peekable_iter.peek().is_none() {
981 leaf_tx = Some(item.tx);
982 }
983 }
984 let leaf_tx = leaf_tx.expect("at least one tx");
985 hashlocked_leaf_sighash(
986 &leaf_tx, user_pubkey, vtxo.server_pubkey(), unlock_hash, &preleaf_txout,
987 )
988}
989
990#[derive(Debug)]
991pub struct LeafVtxoCosignRequest {
992 pub vtxo_id: VtxoId,
993 pub pub_nonce: musig::PublicNonce,
994}
995
996pub struct LeafVtxoCosignContext<'a> {
997 key: &'a Keypair,
998 pub_nonce: musig::PublicNonce,
999 sec_nonce: musig::SecretNonce,
1000 sighash: TapSighash,
1001}
1002
1003impl<'a> LeafVtxoCosignContext<'a> {
1004 pub fn new(
1009 vtxo: &Vtxo,
1010 chain_anchor: &Transaction,
1011 key: &'a Keypair,
1012 ) -> (Self, LeafVtxoCosignRequest) {
1013 let sighash = hashlocked_leaf_sighash_from_vtxo(&vtxo, chain_anchor);
1014 let (sec_nonce, pub_nonce) = musig::nonce_pair_with_msg(key, &sighash.to_byte_array());
1015 let vtxo_id = vtxo.id();
1016 let req = LeafVtxoCosignRequest { vtxo_id, pub_nonce };
1017 let ret = Self { key, pub_nonce, sec_nonce, sighash };
1018 (ret, req)
1019 }
1020
1021 pub fn finalize(
1023 self,
1024 vtxo: &mut Vtxo,
1025 response: LeafVtxoCosignResponse,
1026 ) -> bool {
1027 let agg_nonce = musig::nonce_agg(&[&self.pub_nonce, &response.public_nonce]);
1028 let (_part_sig, final_sig) = musig::partial_sign(
1029 [vtxo.user_pubkey(), vtxo.server_pubkey()],
1030 agg_nonce,
1031 self.key,
1032 self.sec_nonce,
1033 self.sighash.to_byte_array(),
1034 None,
1035 Some(&[&response.partial_signature]),
1036 );
1037 let final_sig = final_sig.expect("has other sigs");
1038
1039 let pubkey = musig::combine_keys([vtxo.user_pubkey(), vtxo.server_pubkey()]);
1040 debug_assert_eq!(pubkey, leaf_cosign_taproot(
1041 vtxo.user_pubkey(),
1042 vtxo.server_pubkey(),
1043 vtxo.expiry_height(),
1044 vtxo.unlock_hash().expect("checked is hark vtxo"),
1045 ).internal_key());
1046 if SECP.verify_schnorr(&final_sig, &self.sighash.into(), &pubkey).is_err() {
1047 return false;
1048 }
1049
1050 vtxo.provide_unlock_signature(final_sig)
1051 }
1052}
1053
1054#[derive(Debug)]
1055pub struct LeafVtxoCosignResponse {
1056 pub public_nonce: musig::PublicNonce,
1057 pub partial_signature: musig::PartialSignature,
1058}
1059
1060impl LeafVtxoCosignResponse {
1061 pub fn new_cosign(
1063 request: &LeafVtxoCosignRequest,
1064 vtxo: &Vtxo,
1065 chain_anchor: &Transaction,
1066 server_key: &Keypair,
1067 ) -> Self {
1068 debug_assert_eq!(server_key.public_key(), vtxo.server_pubkey());
1069 let sighash = hashlocked_leaf_sighash_from_vtxo(&vtxo, chain_anchor);
1070 let (public_nonce, partial_signature) = musig::deterministic_partial_sign(
1071 server_key,
1072 [vtxo.user_pubkey()],
1073 &[&request.pub_nonce],
1074 sighash.to_byte_array(),
1075 None,
1076 );
1077 Self { public_nonce, partial_signature }
1078 }
1079}
1080
1081pub mod builder {
1082 use std::collections::HashMap;
1089 use std::marker::PhantomData;
1090
1091 use bitcoin::{Amount, OutPoint, ScriptBuf, TxOut};
1092 use bitcoin::hashes::{sha256, Hash};
1093 use bitcoin::secp256k1::{Keypair, PublicKey};
1094 use bitcoin_ext::{BlockDelta, BlockHeight};
1095
1096 use crate::tree::signed::{UnlockHash, UnlockPreimage, VtxoLeafSpec};
1097 use crate::{musig, VtxoRequest};
1098 use crate::error::IncorrectSigningKeyError;
1099
1100 use super::{CosignSignatureError, SignedVtxoTreeSpec, UnsignedVtxoTree, VtxoTreeSpec};
1101
1102 pub mod state {
1103 mod sealed {
1104 pub trait Sealed {}
1106 impl Sealed for super::Preparing {}
1107 impl Sealed for super::CanGenerateNonces {}
1108 impl Sealed for super::ServerCanCosign {}
1109 impl Sealed for super::CanFinish {}
1110 }
1111
1112 pub trait BuilderState: sealed::Sealed {}
1114
1115 pub struct Preparing;
1117 impl BuilderState for Preparing {}
1118
1119 pub struct CanGenerateNonces;
1122 impl BuilderState for CanGenerateNonces {}
1123
1124 pub struct ServerCanCosign;
1126 impl BuilderState for ServerCanCosign {}
1127
1128 pub struct CanFinish;
1131 impl BuilderState for CanFinish {}
1132
1133 pub trait CanSign: BuilderState {}
1136 impl CanSign for ServerCanCosign {}
1137 impl CanSign for CanFinish {}
1138 }
1139
1140 enum BuilderTree {
1142 Spec(VtxoTreeSpec),
1143 Unsigned(UnsignedVtxoTree),
1144 }
1145
1146 impl BuilderTree {
1147 fn unsigned_tree(&self) -> Option<&UnsignedVtxoTree> {
1148 match self {
1149 BuilderTree::Spec(_) => None,
1150 BuilderTree::Unsigned(t) => Some(t),
1151 }
1152 }
1153
1154 fn into_unsigned_tree(self) -> Option<UnsignedVtxoTree> {
1155 match self {
1156 BuilderTree::Spec(_) => None,
1157 BuilderTree::Unsigned(t) => Some(t),
1158 }
1159 }
1160 }
1161
1162 pub struct SignedTreeBuilder<S: state::BuilderState> {
1166 pub expiry_height: BlockHeight,
1167 pub server_pubkey: PublicKey,
1168 pub exit_delta: BlockDelta,
1169 pub cosign_pubkey: PublicKey,
1171 pub unlock_preimage: UnlockPreimage,
1173
1174 tree: BuilderTree,
1175
1176 user_pub_nonces: Vec<musig::PublicNonce>,
1178 user_sec_nonces: Option<Vec<musig::SecretNonce>>,
1181 _state: PhantomData<S>,
1182 }
1183
1184 impl<T: state::BuilderState> SignedTreeBuilder<T> {
1185 fn tree_spec(&self) -> &VtxoTreeSpec {
1186 match self.tree {
1187 BuilderTree::Spec(ref s) => s,
1188 BuilderTree::Unsigned(ref t) => &t.spec,
1189 }
1190 }
1191
1192 pub fn total_required_value(&self) -> Amount {
1194 self.tree_spec().total_required_value()
1195 }
1196
1197 pub fn funding_script_pubkey(&self) -> ScriptBuf {
1199 self.tree_spec().funding_tx_script_pubkey()
1200 }
1201
1202 pub fn funding_txout(&self) -> TxOut {
1204 let spec = self.tree_spec();
1205 TxOut {
1206 value: spec.total_required_value(),
1207 script_pubkey: spec.funding_tx_script_pubkey(),
1208 }
1209 }
1210 }
1211
1212 impl<T: state::CanSign> SignedTreeBuilder<T> {
1213 pub fn user_pub_nonces(&self) -> &[musig::PublicNonce] {
1215 &self.user_pub_nonces
1216 }
1217 }
1218
1219 #[derive(Debug, thiserror::Error)]
1220 #[error("signed VTXO tree builder error: {0}")]
1221 pub struct SignedTreeBuilderError(&'static str);
1222
1223 impl SignedTreeBuilder<state::Preparing> {
1224 pub fn construct_tree_spec(
1226 vtxos: impl IntoIterator<Item = VtxoRequest>,
1227 cosign_pubkey: PublicKey,
1228 unlock_hash: UnlockHash,
1229 expiry_height: BlockHeight,
1230 server_pubkey: PublicKey,
1231 server_cosign_pubkey: PublicKey,
1232 exit_delta: BlockDelta,
1233 ) -> Result<VtxoTreeSpec, SignedTreeBuilderError> {
1234 let reqs = vtxos.into_iter()
1235 .map(|vtxo| VtxoLeafSpec {
1236 vtxo: vtxo,
1237 cosign_pubkey: None,
1238 unlock_hash: unlock_hash,
1239 })
1240 .collect::<Vec<_>>();
1241 if reqs.len() < 2 {
1242 return Err(SignedTreeBuilderError("need to have at least 2 VTXOs in tree"));
1243 }
1244 Ok(VtxoTreeSpec::new(
1245 reqs,
1246 server_pubkey,
1247 expiry_height,
1248 exit_delta,
1249 vec![cosign_pubkey, server_cosign_pubkey],
1252 ))
1253 }
1254
1255 pub fn new(
1257 vtxos: impl IntoIterator<Item = VtxoRequest>,
1258 cosign_pubkey: PublicKey,
1259 unlock_preimage: UnlockPreimage,
1260 expiry_height: BlockHeight,
1261 server_pubkey: PublicKey,
1262 server_cosign_pubkey: PublicKey,
1263 exit_delta: BlockDelta,
1264 ) -> Result<SignedTreeBuilder<state::Preparing>, SignedTreeBuilderError> {
1265 let tree = Self::construct_tree_spec(
1266 vtxos,
1267 cosign_pubkey,
1268 sha256::Hash::hash(&unlock_preimage),
1269 expiry_height,
1270 server_pubkey,
1271 server_cosign_pubkey,
1272 exit_delta,
1273 )?;
1274
1275 Ok(SignedTreeBuilder {
1276 expiry_height, server_pubkey, exit_delta, cosign_pubkey, unlock_preimage,
1277 tree: BuilderTree::Spec(tree),
1278 user_pub_nonces: Vec::new(),
1279 user_sec_nonces: None,
1280 _state: PhantomData,
1281 })
1282 }
1283
1284 pub fn set_utxo(self, utxo: OutPoint) -> SignedTreeBuilder<state::CanGenerateNonces> {
1286 let unsigned_tree = match self.tree {
1287 BuilderTree::Spec(s) => s.into_unsigned_tree(utxo),
1288 BuilderTree::Unsigned(t) => t, };
1290 SignedTreeBuilder {
1291 tree: BuilderTree::Unsigned(unsigned_tree),
1292
1293 expiry_height: self.expiry_height,
1294 server_pubkey: self.server_pubkey,
1295 exit_delta: self.exit_delta,
1296 cosign_pubkey: self.cosign_pubkey,
1297 unlock_preimage: self.unlock_preimage,
1298 user_pub_nonces: self.user_pub_nonces,
1299 user_sec_nonces: self.user_sec_nonces,
1300 _state: PhantomData,
1301 }
1302 }
1303 }
1304
1305 impl SignedTreeBuilder<state::CanGenerateNonces> {
1306 pub fn generate_user_nonces(
1308 self,
1309 cosign_key: &Keypair,
1310 ) -> SignedTreeBuilder<state::CanFinish> {
1311 let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1312
1313 let mut cosign_sec_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1314 let mut cosign_pub_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1315 for sh in &unsigned_tree.internal_sighashes {
1316 let pair = musig::nonce_pair_with_msg(&cosign_key, &sh.to_byte_array());
1317 cosign_sec_nonces.push(pair.0);
1318 cosign_pub_nonces.push(pair.1);
1319 }
1320
1321 SignedTreeBuilder {
1322 user_pub_nonces: cosign_pub_nonces,
1323 user_sec_nonces: Some(cosign_sec_nonces),
1324
1325 expiry_height: self.expiry_height,
1326 server_pubkey: self.server_pubkey,
1327 exit_delta: self.exit_delta,
1328 cosign_pubkey: self.cosign_pubkey,
1329 unlock_preimage: self.unlock_preimage,
1330 tree: self.tree,
1331 _state: PhantomData,
1332 }
1333 }
1334 }
1335
1336 #[derive(Debug, Clone)]
1338 pub struct SignedTreeCosignResponse {
1339 pub pub_nonces: Vec<musig::PublicNonce>,
1340 pub partial_signatures: Vec<musig::PartialSignature>,
1341 }
1342
1343 impl SignedTreeBuilder<state::ServerCanCosign> {
1344 pub fn new_for_cosign(
1346 vtxos: impl IntoIterator<Item = VtxoRequest>,
1347 cosign_pubkey: PublicKey,
1348 unlock_preimage: UnlockPreimage,
1349 expiry_height: BlockHeight,
1350 server_pubkey: PublicKey,
1351 server_cosign_pubkey: PublicKey,
1352 exit_delta: BlockDelta,
1353 utxo: OutPoint,
1354 user_pub_nonces: Vec<musig::PublicNonce>,
1355 ) -> Result<SignedTreeBuilder<state::ServerCanCosign>, SignedTreeBuilderError> {
1356 let unsigned_tree = SignedTreeBuilder::construct_tree_spec(
1357 vtxos,
1358 cosign_pubkey,
1359 sha256::Hash::hash(&unlock_preimage),
1360 expiry_height,
1361 server_pubkey,
1362 server_cosign_pubkey,
1363 exit_delta,
1364 )?.into_unsigned_tree(utxo);
1365
1366 Ok(SignedTreeBuilder {
1367 expiry_height,
1368 server_pubkey,
1369 exit_delta,
1370 cosign_pubkey,
1371 unlock_preimage,
1372 user_pub_nonces,
1373 tree: BuilderTree::Unsigned(unsigned_tree),
1374 user_sec_nonces: None,
1375 _state: PhantomData,
1376 })
1377 }
1378
1379 pub fn server_cosign(&self, server_cosign_key: &Keypair) -> SignedTreeCosignResponse {
1381 let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1382
1383 let mut sec_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1384 let mut pub_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1385 for sh in &unsigned_tree.internal_sighashes {
1386 let pair = musig::nonce_pair_with_msg(&server_cosign_key, &sh.to_byte_array());
1387 sec_nonces.push(pair.0);
1388 pub_nonces.push(pair.1);
1389 }
1390
1391 let agg_nonces = self.user_pub_nonces().iter().zip(&pub_nonces)
1392 .map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1393 .collect::<Vec<_>>();
1394
1395 let sigs = unsigned_tree.cosign_tree(&agg_nonces, &server_cosign_key, sec_nonces);
1396
1397 SignedTreeCosignResponse {
1398 pub_nonces,
1399 partial_signatures: sigs,
1400 }
1401 }
1402 }
1403
1404 impl SignedTreeBuilder<state::CanFinish> {
1405 pub fn verify_cosign_response(
1407 &self,
1408 server_cosign: &SignedTreeCosignResponse,
1409 ) -> Result<(), CosignSignatureError> {
1410 let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1411
1412 let agg_nonces = self.user_pub_nonces().iter()
1413 .zip(&server_cosign.pub_nonces)
1414 .map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1415 .collect::<Vec<_>>();
1416
1417 unsigned_tree.verify_global_cosign_partial_sigs(
1418 *unsigned_tree.spec.global_cosign_pubkeys.get(1).expect("state invariant"),
1419 &agg_nonces,
1420 &server_cosign.pub_nonces,
1421 &server_cosign.partial_signatures,
1422 )
1423 }
1424
1425 pub fn build_tree(
1426 self,
1427 server_cosign: &SignedTreeCosignResponse,
1428 cosign_key: &Keypair,
1429 ) -> Result<SignedVtxoTreeSpec, IncorrectSigningKeyError> {
1430 if cosign_key.public_key() != self.cosign_pubkey {
1431 return Err(IncorrectSigningKeyError {
1432 required: Some(self.cosign_pubkey),
1433 provided: cosign_key.public_key(),
1434 });
1435 }
1436
1437 let agg_nonces = self.user_pub_nonces().iter().zip(&server_cosign.pub_nonces)
1438 .map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1439 .collect::<Vec<_>>();
1440
1441 let unsigned_tree = self.tree.into_unsigned_tree().expect("state invariant");
1442 let sec_nonces = self.user_sec_nonces.expect("state invariant");
1443 let partial_sigs = unsigned_tree.cosign_tree(&agg_nonces, cosign_key, sec_nonces);
1444
1445 debug_assert!(unsigned_tree.verify_global_cosign_partial_sigs(
1446 self.cosign_pubkey,
1447 &agg_nonces,
1448 &self.user_pub_nonces,
1449 &partial_sigs,
1450 ).is_ok(), "produced invalid partial signatures");
1451
1452 let sigs = unsigned_tree.combine_partial_signatures(
1453 &agg_nonces,
1454 &HashMap::new(),
1455 &[&server_cosign.partial_signatures, &partial_sigs],
1456 ).expect("should work with correct cosign signatures");
1457
1458 Ok(unsigned_tree.into_signed_tree(sigs))
1459 }
1460 }
1461}
1462
1463const VTXO_TREE_SPEC_VERSION: u8 = 0x02;
1465
1466impl ProtocolEncoding for VtxoTreeSpec {
1467 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
1468 w.emit_u8(VTXO_TREE_SPEC_VERSION)?;
1469 w.emit_u32(self.expiry_height)?;
1470 self.server_pubkey.encode(w)?;
1471 w.emit_u16(self.exit_delta)?;
1472 w.emit_compact_size(self.global_cosign_pubkeys.len() as u64)?;
1473 for pk in &self.global_cosign_pubkeys {
1474 pk.encode(w)?;
1475 }
1476
1477 w.emit_compact_size(self.vtxos.len() as u64)?;
1478 for vtxo in &self.vtxos {
1479 vtxo.vtxo.policy.encode(w)?;
1480 w.emit_u64(vtxo.vtxo.amount.to_sat())?;
1481 vtxo.cosign_pubkey.encode(w)?;
1482 vtxo.unlock_hash.encode(w)?;
1483 }
1484 Ok(())
1485 }
1486
1487 fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, crate::encode::ProtocolDecodingError> {
1488 let version = r.read_u8()?;
1489
1490 if version == 0x01 {
1492 let expiry_height = r.read_u32()?;
1493 let server_pubkey = PublicKey::decode(r)?;
1494 let exit_delta = r.read_u16()?;
1495 let server_cosign_pk = PublicKey::decode(r)?;
1496
1497 let nb_vtxos = r.read_u32()?;
1498 let mut vtxos = Vec::with_capacity(nb_vtxos as usize);
1499 for _ in 0..nb_vtxos {
1500 vtxos.push(VtxoLeafSpec {
1501 vtxo: VtxoRequest {
1502 policy: VtxoPolicy::decode(r)?,
1503 amount: Amount::from_sat(r.read_u64()?),
1504 },
1505 cosign_pubkey: Some(PublicKey::decode(r)?),
1506 unlock_hash: sha256::Hash::decode(r)?,
1507 });
1508 }
1509
1510 return Ok(VtxoTreeSpec {
1511 vtxos, expiry_height, server_pubkey, exit_delta,
1512 global_cosign_pubkeys: vec![server_cosign_pk],
1513 });
1514 }
1515
1516 if version != VTXO_TREE_SPEC_VERSION {
1517 return Err(ProtocolDecodingError::invalid(format_args!(
1518 "invalid VtxoTreeSpec encoding version byte: {version:#x}",
1519 )));
1520 }
1521
1522 let expiry_height = r.read_u32()?;
1523 let server_pubkey = PublicKey::decode(r)?;
1524 let exit_delta = r.read_u16()?;
1525 let nb_global_signers = r.read_compact_size()?;
1526 let mut global_cosign_pubkeys = Vec::with_capacity(nb_global_signers as usize);
1527 for _ in 0..nb_global_signers {
1528 global_cosign_pubkeys.push(PublicKey::decode(r)?);
1529 }
1530
1531 let nb_vtxos = r.read_compact_size()?;
1532 let mut vtxos = Vec::with_capacity(nb_vtxos as usize);
1533 for _ in 0..nb_vtxos {
1534 vtxos.push(VtxoLeafSpec {
1535 vtxo: VtxoRequest {
1536 policy: VtxoPolicy::decode(r)?,
1537 amount: Amount::from_sat(r.read_u64()?),
1538 },
1539 cosign_pubkey: Option::<PublicKey>::decode(r)?,
1540 unlock_hash: sha256::Hash::decode(r)?,
1541 });
1542 }
1543
1544 Ok(VtxoTreeSpec { vtxos, expiry_height, server_pubkey, exit_delta, global_cosign_pubkeys })
1545 }
1546}
1547
1548const SIGNED_VTXO_TREE_SPEC_VERSION: u8 = 0x01;
1550
1551impl ProtocolEncoding for SignedVtxoTreeSpec {
1552 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
1553 w.emit_u8(SIGNED_VTXO_TREE_SPEC_VERSION)?;
1554 self.spec.encode(w)?;
1555 self.utxo.encode(w)?;
1556 w.emit_u32(self.cosign_sigs.len() as u32)?;
1557 for sig in &self.cosign_sigs {
1558 sig.encode(w)?;
1559 }
1560 Ok(())
1561 }
1562
1563 fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, crate::encode::ProtocolDecodingError> {
1564 let version = r.read_u8()?;
1565 if version != SIGNED_VTXO_TREE_SPEC_VERSION {
1566 return Err(ProtocolDecodingError::invalid(format_args!(
1567 "invalid SignedVtxoTreeSpec encoding version byte: {version:#x}",
1568 )));
1569 }
1570 let spec = VtxoTreeSpec::decode(r)?;
1571 let utxo = OutPoint::decode(r)?;
1572 let nb_cosign_sigs = r.read_u32()?;
1573 let mut cosign_sigs = Vec::with_capacity(nb_cosign_sigs as usize);
1574 for _ in 0..nb_cosign_sigs {
1575 cosign_sigs.push(schnorr::Signature::decode(r)?);
1576 }
1577 Ok(SignedVtxoTreeSpec { spec, utxo, cosign_sigs })
1578 }
1579}
1580
1581
1582#[cfg(test)]
1583mod test {
1584 use std::iter;
1585 use std::collections::HashMap;
1586 use std::str::FromStr;
1587
1588 use bitcoin::hashes::{siphash24, sha256, Hash, HashEngine};
1589 use bitcoin::key::rand::Rng;
1590 use bitcoin::secp256k1::{self, rand, Keypair};
1591 use bitcoin::{absolute, transaction};
1592 use rand::SeedableRng;
1593
1594 use crate::encode;
1595 use crate::encode::test::{encoding_roundtrip, json_roundtrip};
1596 use crate::vtxo::policy::VtxoPolicy;
1597 use crate::tree::signed::builder::SignedTreeBuilder;
1598
1599 use super::*;
1600
1601 fn test_tree_amounts(
1602 tree: &UnsignedVtxoTree,
1603 root_value: Amount,
1604 ) {
1605 let map = tree.txs.iter().map(|tx| (tx.compute_txid(), tx)).collect::<HashMap<_, _>>();
1606
1607 for (idx, tx) in tree.txs.iter().take(tree.txs.len() - 1).enumerate() {
1609 println!("tx #{idx}: {}", bitcoin::consensus::encode::serialize_hex(tx));
1610 let input = tx.input.iter().map(|i| {
1611 let prev = i.previous_output;
1612 map.get(&prev.txid).expect(&format!("tx {} not found", prev.txid))
1613 .output[prev.vout as usize].value
1614 }).sum::<Amount>();
1615 let output = tx.output_value();
1616 assert!(input >= output);
1617 assert_eq!(input, output);
1618 }
1619
1620 let root = tree.txs.last().unwrap();
1622 assert_eq!(root_value, root.output_value());
1623 }
1624
1625 #[test]
1626 fn vtxo_tree() {
1627 let secp = secp256k1::Secp256k1::new();
1628 let mut rand = rand::rngs::StdRng::seed_from_u64(42);
1629 let random_sig = {
1630 let key = Keypair::new(&secp, &mut rand);
1631 let sha = sha256::Hash::from_str("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap();
1632 let msg = secp256k1::Message::from_digest(sha.to_byte_array());
1633 secp.sign_schnorr(&msg, &key)
1634 };
1635
1636 let server_key = Keypair::new(&secp, &mut rand);
1637 let server_cosign_key = Keypair::new(&secp, &mut rand);
1638
1639 struct Req {
1640 key: Keypair,
1641 cosign_key: Keypair,
1642 amount: Amount,
1643 hash: sha256::Hash,
1644 }
1645 impl Req {
1646 fn to_vtxo(&self) -> VtxoLeafSpec {
1647 VtxoLeafSpec {
1648 vtxo: VtxoRequest {
1649 amount: self.amount,
1650 policy: VtxoPolicy::new_pubkey(self.key.public_key()),
1651 },
1652 cosign_pubkey: Some(self.cosign_key.public_key()),
1653 unlock_hash: self.hash,
1654 }
1655 }
1656 }
1657
1658 let nb_leaves = 27;
1659 let reqs = iter::repeat_with(|| Req {
1660 key: Keypair::new(&secp, &mut rand),
1661 cosign_key: Keypair::new(&secp, &mut rand),
1662 amount: Amount::from_sat(100_000),
1663 hash: sha256::Hash::from_byte_array(rand.r#gen()),
1664 }).take(nb_leaves).collect::<Vec<_>>();
1665 let point = "0000000000000000000000000000000000000000000000000000000000000001:1".parse().unwrap();
1666
1667 let spec = VtxoTreeSpec::new(
1668 reqs.iter().map(|r| r.to_vtxo()).collect(),
1669 server_key.public_key(),
1670 101_000,
1671 2016,
1672 vec![server_cosign_key.public_key()],
1673 );
1674 assert_eq!(spec.nb_leaves(), nb_leaves);
1675 assert_eq!(spec.total_required_value().to_sat(), 2700000);
1676 let nb_nodes = spec.nb_nodes();
1677
1678 encoding_roundtrip(&spec);
1679
1680 let unsigned = spec.into_unsigned_tree(point);
1681
1682 test_tree_amounts(&unsigned, unsigned.spec.total_required_value());
1683
1684 let sighashes_hash = {
1685 let mut eng = siphash24::Hash::engine();
1686 unsigned.internal_sighashes.iter().for_each(|h| eng.input(&h[..]));
1687 siphash24::Hash::from_engine(eng)
1688 };
1689 assert_eq!(sighashes_hash.to_string(), "b83a4fe5937a7404");
1690
1691 let signed = unsigned.into_signed_tree(vec![random_sig; nb_nodes]);
1692
1693 encoding_roundtrip(&signed);
1694
1695 #[derive(Debug, PartialEq, Serialize, Deserialize)]
1696 struct JsonSignedVtxoTreeSpec {
1697 #[serde(with = "encode::serde")]
1698 pub spec: SignedVtxoTreeSpec,
1699 }
1700
1701 json_roundtrip(&JsonSignedVtxoTreeSpec { spec: signed.clone() });
1702
1703 for l in 0..nb_leaves {
1704 let exit = signed.exit_branch(l);
1705
1706 let mut iter = exit.iter().enumerate().peekable();
1708 while let Some((i, cur)) = iter.next() {
1709 if let Some((_, next)) = iter.peek() {
1710 assert_eq!(next.input[0].previous_output.txid, cur.compute_txid(), "{}", i);
1711 }
1712 }
1713 }
1714
1715 let cached = signed.into_cached_tree();
1716 for vtxo in cached.all_vtxos() {
1717 encoding_roundtrip(&vtxo);
1718 }
1719 }
1720
1721 #[test]
1722 fn test_tree_builder() {
1723 let expiry = 100_000;
1724 let exit_delta = 24;
1725
1726 let vtxo_key = Keypair::from_str("985247fb0ef008f8043b6be28add87710d42d482433ef287235bfe041ee6cc11").unwrap();
1727 let policy = VtxoPolicy::new_pubkey(vtxo_key.public_key());
1728 let user_cosign_key = Keypair::from_str("5255d132d6ec7d4fc2a41c8f0018bb14343489ddd0344025cc60c7aa2b3fda6a").unwrap();
1729 let user_cosign_pubkey = user_cosign_key.public_key();
1730 println!("user_cosign_pubkey: {}", user_cosign_pubkey);
1731
1732 let server_key = Keypair::from_str("1fb316e653eec61de11c6b794636d230379509389215df1ceb520b65313e5426").unwrap();
1733 let server_pubkey = server_key.public_key();
1734 println!("server_pubkey: {}", server_pubkey);
1735
1736 let server_cosign_key = Keypair::from_str("52a506fbae3b725749d2486afd4761841ec685b841c2967e30f24182c4b02eed").unwrap();
1737 let server_cosign_pubkey = server_cosign_key.public_key();
1738 println!("server_cosign_pubkey: {}", server_cosign_pubkey);
1739
1740 let unlock_preimage = rand::random::<UnlockPreimage>();
1741 let unlock_hash = sha256::Hash::hash(&unlock_preimage);
1742 println!("unlock_hash: {}", unlock_hash);
1743
1744 for nb_vtxos in [2, 3, 4, 5, 10, 50] {
1746 println!("building tree with {} vtxos", nb_vtxos);
1747 let vtxos = (0..nb_vtxos).map(|i| VtxoRequest {
1748 amount: Amount::from_sat(1000 * (i + 1)),
1749 policy: policy.clone(),
1750 }).collect::<Vec<_>>();
1751
1752 let builder = SignedTreeBuilder::new(
1753 vtxos.iter().cloned(), user_cosign_pubkey, unlock_preimage, expiry, server_pubkey,
1754 server_cosign_pubkey, exit_delta,
1755 ).unwrap();
1756
1757 let funding_tx = Transaction {
1758 version: transaction::Version::TWO,
1759 lock_time: absolute::LockTime::ZERO,
1760 input: vec![],
1761 output: vec![builder.funding_txout()],
1762 };
1763 let utxo = OutPoint::new(funding_tx.compute_txid(), 0);
1764 let builder = builder.set_utxo(utxo).generate_user_nonces(&user_cosign_key);
1765 let user_pub_nonces = builder.user_pub_nonces().to_vec();
1766
1767 let cosign = {
1768 let builder = SignedTreeBuilder::new_for_cosign(
1769 vtxos.iter().cloned(), user_cosign_pubkey, unlock_preimage, expiry, server_pubkey,
1770 server_cosign_pubkey, exit_delta, utxo, user_pub_nonces,
1771 ).unwrap();
1772 builder.server_cosign(&server_cosign_key)
1773 };
1774
1775 builder.verify_cosign_response(&cosign).unwrap();
1776 let tree = builder.build_tree(&cosign, &user_cosign_key).unwrap().into_cached_tree();
1777
1778 for mut vtxo in tree.all_vtxos() {
1780 {
1781 let mut with_preimage = vtxo.clone();
1783 assert!(with_preimage.provide_unlock_preimage(unlock_preimage));
1784 assert!(with_preimage.validate(&funding_tx).is_err());
1785 }
1786
1787 let (ctx, req) = LeafVtxoCosignContext::new(&vtxo, &funding_tx, &vtxo_key);
1788 let cosign = LeafVtxoCosignResponse::new_cosign(&req, &vtxo, &funding_tx, &server_key);
1789 assert!(ctx.finalize(&mut vtxo, cosign));
1790
1791 assert!(vtxo.validate(&funding_tx).is_err());
1793
1794 assert!(vtxo.provide_unlock_preimage(unlock_preimage));
1795
1796 println!("vtxo debug: {:#?}", vtxo);
1797 println!("vtxo hex: {}", vtxo.serialize_hex());
1798 vtxo.validate(&funding_tx).expect("should be value");
1799 }
1800 }
1801 }
1802}