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, 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, SECP, SignedVtxoRequest, Vtxo, VtxoPolicy, VtxoRequest};
18use crate::encode::{ProtocolDecodingError, ProtocolEncoding, ReadExt, WriteExt};
19use crate::tree::{self, Tree};
20use crate::vtxo::{self, GenesisItem, GenesisTransition};
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(
47 agg_pk: XOnlyPublicKey,
48 server_pubkey: PublicKey,
49 expiry_height: BlockHeight,
50 unlock_hash: UnlockHash,
51) -> taproot::TaprootSpendInfo {
52 taproot::TaprootBuilder::new()
53 .add_leaf(1, expiry_clause(server_pubkey, expiry_height)).unwrap()
54 .add_leaf(1, unlock_clause(agg_pk, unlock_hash)).unwrap()
55 .finalize(&SECP, agg_pk).unwrap()
56}
57
58pub fn cosign_taproot(
59 agg_pk: XOnlyPublicKey,
60 server_pubkey: PublicKey,
61 expiry_height: BlockHeight,
62) -> taproot::TaprootSpendInfo {
63 taproot::TaprootBuilder::new()
64 .add_leaf(0, expiry_clause(server_pubkey, expiry_height)).unwrap()
65 .finalize(&SECP, agg_pk).unwrap()
66}
67
68#[derive(Debug, Clone, Eq, PartialEq)]
70pub struct VtxoTreeSpec {
71 pub vtxos: Vec<SignedVtxoRequest>,
72 pub expiry_height: BlockHeight,
73 pub server_pubkey: PublicKey,
74 pub exit_delta: BlockDelta,
75 pub global_cosign_pubkeys: Vec<PublicKey>,
76}
77
78impl VtxoTreeSpec {
79 pub fn new(
80 vtxos: Vec<SignedVtxoRequest>,
81 server_pubkey: PublicKey,
82 expiry_height: BlockHeight,
83 exit_delta: BlockDelta,
84 global_cosign_pubkeys: Vec<PublicKey>,
85 ) -> VtxoTreeSpec {
86 assert_ne!(vtxos.len(), 0);
87 VtxoTreeSpec { vtxos, server_pubkey, expiry_height, exit_delta, global_cosign_pubkeys }
88 }
89
90 pub fn nb_leaves(&self) -> usize {
91 self.vtxos.len()
92 }
93
94 pub fn nb_nodes(&self) -> usize {
95 Tree::nb_nodes_for_leaves(self.nb_leaves())
96 }
97
98 pub fn iter_vtxos(&self) -> impl Iterator<Item = &SignedVtxoRequest> {
99 self.vtxos.iter()
100 }
101
102 pub fn leaf_idx_of(&self, vtxo_request: &SignedVtxoRequest) -> Option<usize> {
104 self.vtxos.iter().position(|e| e == vtxo_request)
105 }
106
107 pub fn total_required_value(&self) -> Amount {
112 self.vtxos.iter().map(|d| d.vtxo.amount).sum::<Amount>()
113 }
114
115 pub fn cosign_taproot(&self, agg_pk: XOnlyPublicKey) -> taproot::TaprootSpendInfo {
117 cosign_taproot(agg_pk, self.server_pubkey, self.expiry_height)
118 }
119
120 pub fn funding_tx_cosign_pubkey(&self) -> XOnlyPublicKey {
124 let keys = self.vtxos.iter()
125 .filter_map(|v| v.cosign_pubkey)
126 .chain(self.global_cosign_pubkeys.iter().copied());
127 musig::combine_keys(keys)
128 }
129
130 pub fn funding_tx_script_pubkey(&self) -> ScriptBuf {
134 let agg_pk = self.funding_tx_cosign_pubkey();
135 self.cosign_taproot(agg_pk).script_pubkey()
136 }
137
138 pub fn funding_tx_txout(&self) -> TxOut {
142 TxOut {
143 script_pubkey: self.funding_tx_script_pubkey(),
144 value: self.total_required_value(),
145 }
146 }
147
148 fn node_tx<'a>(&self, children: impl Iterator<Item=(&'a Transaction, &'a XOnlyPublicKey)>) -> Transaction {
149 Transaction {
150 version: bitcoin::transaction::Version(3),
151 lock_time: bitcoin::absolute::LockTime::ZERO,
152 input: vec![TxIn {
153 previous_output: OutPoint::null(), sequence: Sequence::ZERO,
155 script_sig: ScriptBuf::new(),
156 witness: Witness::new(),
157 }],
158 output: children.map(|(tx, agg_pk)| {
159 let taproot = self.cosign_taproot(*agg_pk);
160 TxOut {
161 script_pubkey: taproot.script_pubkey(),
162 value: tx.output_value(),
163 }
164 }).chain(Some(fee::fee_anchor())).collect(),
165 }
166 }
167
168 fn leaf_tx(&self, vtxo: &VtxoRequest) -> Transaction {
169 let txout = TxOut {
170 value: vtxo.amount,
171 script_pubkey: vtxo.policy.script_pubkey(self.server_pubkey, self.exit_delta, self.expiry_height),
172 };
173
174 vtxo::create_exit_tx(OutPoint::null(), txout, None)
175 }
176
177 pub fn cosign_agg_pks(&self)
181 -> impl Iterator<Item = XOnlyPublicKey> + iter::DoubleEndedIterator + iter::ExactSizeIterator + '_
182 {
183 Tree::new(self.nb_leaves()).into_iter().map(|node| {
184 musig::combine_keys(
185 node.leaves().filter_map(|i| self.vtxos[i].cosign_pubkey)
186 .chain(self.global_cosign_pubkeys.iter().copied())
187 )
188 })
189 }
190
191 pub fn unsigned_transactions(&self, utxo: OutPoint) -> Vec<Transaction> {
193 let tree = Tree::new(self.nb_leaves());
194
195 let cosign_agg_pks = self.cosign_agg_pks().collect::<Vec<_>>();
196
197 let mut txs = Vec::with_capacity(tree.nb_nodes());
198 for node in tree.iter() {
199 let tx = if node.is_leaf() {
200 self.leaf_tx(&self.vtxos[node.idx()].vtxo).clone()
201 } else {
202 let mut buf = [None; tree::RADIX];
203 for (idx, child) in node.children().enumerate() {
204 buf[idx] = Some((&txs[child], &cosign_agg_pks[child]));
205 }
206 self.node_tx(buf.iter().filter_map(|x| *x))
207 };
208 txs.push(tx.clone());
209 };
210
211 txs.last_mut().unwrap().input[0].previous_output = utxo;
213 for node in tree.iter().rev() {
214 let txid = txs[node.idx()].compute_txid();
215 for (i, child) in node.children().enumerate() {
216 let point = OutPoint::new(txid, i as u32);
217 txs[child].input[0].previous_output = point;
218 }
219 }
220
221 txs
222 }
223
224 pub fn signed_transactions(
226 &self,
227 utxo: OutPoint,
228 signatures: &[schnorr::Signature],
229 ) -> Vec<Transaction> {
230 let mut txs = self.unsigned_transactions(utxo);
231 for (tx, sig) in txs.iter_mut().zip(signatures) {
232 tx.input[0].witness.push(&sig[..]);
233 }
234 txs
235 }
236
237 pub fn calculate_cosign_agg_nonces(
241 &self,
242 leaf_cosign_nonces: &HashMap<PublicKey, Vec<PublicNonce>>,
243 global_signer_cosign_nonces: &[impl AsRef<[PublicNonce]>],
244 ) -> Result<Vec<AggregatedNonce>, String> {
245 if global_signer_cosign_nonces.len() != self.global_cosign_pubkeys.len() {
246 return Err("missing global signer nonces".into());
247 }
248
249 Tree::new(self.nb_leaves()).iter().enumerate().map(|(idx, node)| {
250 let mut nonces = Vec::new();
251 for pk in node.leaves().filter_map(|i| self.vtxos[i].cosign_pubkey) {
252 nonces.push(leaf_cosign_nonces.get(&pk)
253 .ok_or_else(|| format!("missing nonces for leaf pk {}", pk))?
254 .get(node.level())
257 .ok_or_else(|| format!("not enough nonces for leaf_pk {}", pk))?
258 );
259 }
260 for glob in global_signer_cosign_nonces {
261 nonces.push(glob.as_ref().get(idx).ok_or("not enough global cosign nonces")?);
262 }
263 Ok(musig::nonce_agg(&nonces))
264 }).collect()
265 }
266
267 pub fn into_unsigned_tree(
272 self,
273 utxo: OutPoint,
274 ) -> UnsignedVtxoTree {
275 UnsignedVtxoTree::new(self, utxo)
276 }
277}
278
279#[derive(Debug, Clone)]
283pub struct UnsignedVtxoTree {
284 pub spec: VtxoTreeSpec,
285 pub utxo: OutPoint,
286
287 pub cosign_agg_pks: Vec<XOnlyPublicKey>,
291 pub txs: Vec<Transaction>,
293 pub sighashes: Vec<TapSighash>,
295
296 tree: Tree,
297}
298
299impl UnsignedVtxoTree {
300 pub fn new(
301 spec: VtxoTreeSpec,
302 utxo: OutPoint,
303 ) -> UnsignedVtxoTree {
304 let tree = Tree::new(spec.nb_leaves());
305
306 let cosign_agg_pks = spec.cosign_agg_pks().collect::<Vec<_>>();
307 let txs = spec.unsigned_transactions(utxo);
308
309 let root_txout = spec.funding_tx_txout();
310 let sighashes = tree.iter().map(|node| {
311 let prev = if let Some((parent, sibling_idx)) = tree.parent_idx_of_with_sibling_idx(node.idx()) {
312 assert!(!node.is_root());
313 &txs[parent].output[sibling_idx]
314 } else {
315 assert!(node.is_root());
316 &root_txout
317 };
318 SighashCache::new(&txs[node.idx()]).taproot_key_spend_signature_hash(
319 0, &sighash::Prevouts::All(&[prev]),
321 TapSighashType::Default,
322 ).expect("sighash error")
323 }).collect();
324
325 UnsignedVtxoTree { spec, utxo, txs, sighashes, cosign_agg_pks, tree }
326 }
327
328 pub fn nb_leaves(&self) -> usize {
329 self.tree.nb_leaves()
330 }
331
332 pub fn nb_nodes(&self) -> usize {
333 self.tree.nb_nodes()
334 }
335
336 pub fn cosign_branch(
349 &self,
350 cosign_agg_nonces: &[AggregatedNonce],
351 leaf_idx: usize,
352 cosign_key: &Keypair,
353 cosign_sec_nonces: Vec<SecretNonce>,
354 ) -> Result<Vec<PartialSignature>, IncorrectSigningKeyError> {
355 let req = self.spec.vtxos.get(leaf_idx).expect("leaf idx out of bounds");
356 if Some(cosign_key.public_key()) != req.cosign_pubkey {
357 return Err(IncorrectSigningKeyError {
358 required: req.cosign_pubkey,
359 provided: cosign_key.public_key(),
360 });
361 }
362
363 let mut nonce_iter = cosign_sec_nonces.into_iter().enumerate();
364 let mut ret = Vec::with_capacity(self.tree.root().level() + 1);
365 for node in self.tree.iter_branch(leaf_idx) {
366 let sec_nonce = loop {
371 let next = nonce_iter.next().expect("level overflow");
372 if next.0 == node.level() {
373 break next.1;
374 }
375 };
376
377 let cosign_pubkeys = node.leaves().filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
378 .chain(self.spec.global_cosign_pubkeys.iter().copied());
379 let sighash = self.sighashes[node.idx()];
380
381 let agg_pk = self.cosign_agg_pks[node.idx()];
382 let sig = musig::partial_sign(
383 cosign_pubkeys,
384 cosign_agg_nonces[node.idx()],
385 &cosign_key,
386 sec_nonce,
387 sighash.to_byte_array(),
388 Some(self.spec.cosign_taproot(agg_pk).tap_tweak().to_byte_array()),
389 None,
390 ).0;
391 ret.push(sig);
392 }
393
394 Ok(ret)
395 }
396
397 pub fn cosign_tree(
403 &self,
404 cosign_agg_nonces: &[AggregatedNonce],
405 keypair: &Keypair,
406 cosign_sec_nonces: Vec<SecretNonce>,
407 ) -> Vec<PartialSignature> {
408 assert_eq!(cosign_agg_nonces.len(), self.nb_nodes());
409 assert_eq!(cosign_sec_nonces.len(), self.nb_nodes());
410
411 self.tree.iter().zip(cosign_sec_nonces.into_iter()).map(|(node, sec_nonce)| {
412 let cosign_pubkeys = node.leaves().filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
413 .chain(self.spec.global_cosign_pubkeys.iter().copied());
414 let sighash = self.sighashes[node.idx()];
415
416 let agg_pk = self.cosign_agg_pks[node.idx()];
417 debug_assert_eq!(agg_pk, musig::combine_keys(cosign_pubkeys.clone()));
418 musig::partial_sign(
419 cosign_pubkeys,
420 cosign_agg_nonces[node.idx()],
421 &keypair,
422 sec_nonce,
423 sighash.to_byte_array(),
424 Some(self.spec.cosign_taproot(agg_pk).tap_tweak().to_byte_array()),
425 None,
426 ).0
427 }).collect()
428 }
429
430 fn verify_node_cosign_partial_sig(
432 &self,
433 node: &tree::Node,
434 pk: PublicKey,
435 agg_nonces: &[AggregatedNonce],
436 part_sig: PartialSignature,
437 pub_nonce: PublicNonce,
438 ) -> Result<(), CosignSignatureError> {
439 let cosign_pubkeys = node.leaves().filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
440 .chain(self.spec.global_cosign_pubkeys.iter().copied());
441 let sighash = self.sighashes[node.idx()];
442
443 let taptweak = self.spec.cosign_taproot(self.cosign_agg_pks[node.idx()]).tap_tweak();
444 let key_agg = musig::tweaked_key_agg(cosign_pubkeys, taptweak.to_byte_array()).0;
445 let session = musig::Session::new(
446 &key_agg,
447 *agg_nonces.get(node.idx()).ok_or(CosignSignatureError::NotEnoughNonces)?,
448 &sighash.to_byte_array(),
449 );
450 let ok = session.partial_verify(&key_agg, &part_sig, &pub_nonce, musig::pubkey_to(pk));
451 if !ok {
452 return Err(CosignSignatureError::invalid_sig(pk));
453 }
454 Ok(())
455 }
456
457 pub fn verify_branch_cosign_partial_sigs(
461 &self,
462 cosign_agg_nonces: &[AggregatedNonce],
463 request: &SignedVtxoRequest,
464 cosign_pub_nonces: &[PublicNonce],
465 cosign_part_sigs: &[PartialSignature],
466 ) -> Result<(), String> {
467 assert_eq!(cosign_agg_nonces.len(), self.nb_nodes());
468
469 let cosign_pubkey = request.cosign_pubkey.ok_or("no cosign pubkey for request")?;
470 let leaf_idx = self.spec.leaf_idx_of(request).ok_or("request not in tree")?;
471 match self.tree.iter_branch(leaf_idx).count().cmp(&cosign_part_sigs.len()) {
473 cmp::Ordering::Less => return Err("too few partial signatures".into()),
474 cmp::Ordering::Greater => return Err("too many partial signatures".into()),
475 cmp::Ordering::Equal => {},
476 }
477
478 let mut part_sigs_iter = cosign_part_sigs.iter();
479 let mut pub_nonce_iter = cosign_pub_nonces.iter().enumerate();
480 for node in self.tree.iter_branch(leaf_idx) {
481 let pub_nonce = loop {
482 let next = pub_nonce_iter.next().ok_or("not enough pub nonces")?;
483 if next.0 == node.level() {
484 break next.1;
485 }
486 };
487 self.verify_node_cosign_partial_sig(
488 node,
489 cosign_pubkey,
490 cosign_agg_nonces,
491 part_sigs_iter.next().ok_or("not enough sigs")?.clone(),
492 *pub_nonce,
493 ).map_err(|e| format!("part sig verification failed: {}", e))?;
494 }
495
496 Ok(())
497 }
498
499 pub fn verify_global_cosign_partial_sigs(
503 &self,
504 pk: PublicKey,
505 agg_nonces: &[AggregatedNonce],
506 pub_nonces: &[PublicNonce],
507 part_sigs: &[PartialSignature],
508 ) -> Result<(), CosignSignatureError> {
509 for node in self.tree.iter() {
510 self.verify_node_cosign_partial_sig(
511 node,
512 pk,
513 agg_nonces,
514 *part_sigs.get(node.idx()).ok_or_else(|| CosignSignatureError::missing_sig(pk))?,
515 *pub_nonces.get(node.idx()).ok_or_else(|| CosignSignatureError::NotEnoughNonces)?,
516 )?;
517 }
518
519 Ok(())
520 }
521
522 pub fn combine_partial_signatures(
528 &self,
529 cosign_agg_nonces: &[AggregatedNonce],
530 leaf_part_sigs: &HashMap<PublicKey, Vec<PartialSignature>>,
531 global_signer_part_sigs: &[impl AsRef<[PartialSignature]>],
532 ) -> Result<Vec<schnorr::Signature>, CosignSignatureError> {
533 let mut leaf_part_sigs = leaf_part_sigs.iter()
535 .map(|(pk, sigs)| (pk, sigs.iter().collect()))
536 .collect::<HashMap<_, VecDeque<_>>>();
537
538 if global_signer_part_sigs.len() != self.spec.global_cosign_pubkeys.len() {
539 return Err(CosignSignatureError::Invalid(
540 "invalid nb of global cosigner partial signatures",
541 ));
542 }
543 for (pk, sigs) in self.spec.global_cosign_pubkeys.iter().zip(global_signer_part_sigs) {
544 if sigs.as_ref().len() != self.nb_nodes() {
545 return Err(CosignSignatureError::MissingSignature { pk: *pk });
546 }
547 }
548
549 let max_level = self.tree.root().level();
550 self.tree.iter().enumerate().map(|(idx, node)| {
551 let mut cosign_pks = Vec::with_capacity(max_level + 1);
552 let mut part_sigs = Vec::with_capacity(max_level + 1);
553 for leaf in node.leaves() {
554 if let Some(cosign_pk) = self.spec.vtxos[leaf].cosign_pubkey {
555 let part_sig = leaf_part_sigs.get_mut(&cosign_pk)
556 .ok_or(CosignSignatureError::missing_sig(cosign_pk))?
557 .pop_front()
558 .ok_or(CosignSignatureError::missing_sig(cosign_pk))?;
559 cosign_pks.push(cosign_pk);
560 part_sigs.push(part_sig);
561 }
562 }
563 cosign_pks.extend(&self.spec.global_cosign_pubkeys);
565 for sigs in global_signer_part_sigs {
566 part_sigs.push(sigs.as_ref().get(idx).expect("checked before"));
567 }
568
569 let agg_pk = self.cosign_agg_pks[node.idx()];
570 Ok(musig::combine_partial_signatures(
571 cosign_pks,
572 *cosign_agg_nonces.get(node.idx()).ok_or(CosignSignatureError::NotEnoughNonces)?,
573 self.sighashes[node.idx()].to_byte_array(),
574 Some(self.spec.cosign_taproot(agg_pk).tap_tweak().to_byte_array()),
575 &part_sigs
576 ))
577 }).collect()
578 }
579
580 pub fn verify_cosign_sigs(
584 &self,
585 signatures: &[schnorr::Signature],
586 ) -> Result<(), XOnlyPublicKey> {
587 for node in self.tree.iter() {
588 let sighash = self.sighashes[node.idx()];
589 let agg_pk = &self.cosign_agg_pks[node.idx()];
590 let pk = self.spec.cosign_taproot(*agg_pk).output_key().to_x_only_public_key();
591 let sig = signatures.get(node.idx()).ok_or_else(|| pk)?;
592 if SECP.verify_schnorr(sig, &sighash.into(), &pk).is_err() {
593 return Err(pk);
594 }
595 }
596 Ok(())
597 }
598
599 pub fn into_signed_tree(
603 self,
604 signatures: Vec<schnorr::Signature>,
605 ) -> SignedVtxoTreeSpec {
606 SignedVtxoTreeSpec {
607 spec: self.spec,
608 utxo: self.utxo,
609 cosign_sigs: signatures,
610 }
611 }
612}
613
614#[derive(PartialEq, Eq, thiserror::Error)]
616pub enum CosignSignatureError {
617 #[error("missing cosign signature from pubkey {pk}")]
618 MissingSignature { pk: PublicKey },
619 #[error("invalid cosign signature from pubkey {pk}")]
620 InvalidSignature { pk: PublicKey },
621 #[error("not enough nonces")]
622 NotEnoughNonces,
623 #[error("invalid cosign signatures: {0}")]
624 Invalid(&'static str),
625}
626
627impl fmt::Debug for CosignSignatureError {
628 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
629 fmt::Display::fmt(self, f)
630 }
631}
632
633impl CosignSignatureError {
634 fn missing_sig(cosign_pk: PublicKey) -> CosignSignatureError {
635 CosignSignatureError::MissingSignature { pk: cosign_pk }
636 }
637 fn invalid_sig(cosign_pk: PublicKey) -> CosignSignatureError {
638 CosignSignatureError::InvalidSignature { pk: cosign_pk }
639 }
640}
641
642#[derive(Debug, Clone, PartialEq)]
644pub struct SignedVtxoTreeSpec {
645 pub spec: VtxoTreeSpec,
646 pub utxo: OutPoint,
647 pub cosign_sigs: Vec<schnorr::Signature>,
649}
650
651impl SignedVtxoTreeSpec {
652 pub fn new(
654 spec: VtxoTreeSpec,
655 utxo: OutPoint,
656 signatures: Vec<schnorr::Signature>,
657 ) -> SignedVtxoTreeSpec {
658 SignedVtxoTreeSpec { spec, utxo, cosign_sigs: signatures }
659 }
660
661 pub fn nb_leaves(&self) -> usize {
662 self.spec.nb_leaves()
663 }
664
665 pub fn exit_branch(&self, leaf_idx: usize) -> Option<Vec<Transaction>> {
667 let txs = self.all_signed_txs();
668
669 if leaf_idx >= self.spec.nb_leaves() {
670 return None;
671 }
672
673 let tree = Tree::new(self.spec.nb_leaves());
674 let mut ret = tree.iter_branch(leaf_idx)
675 .map(|n| txs[n.idx()].clone())
676 .collect::<Vec<_>>();
677 ret.reverse();
678 Some(ret)
679 }
680
681 pub fn all_signed_txs(&self) -> Vec<Transaction> {
683 self.spec.signed_transactions(self.utxo, &self.cosign_sigs)
684 }
685
686 pub fn into_cached_tree(self) -> CachedSignedVtxoTree {
687 CachedSignedVtxoTree {
688 txs: self.all_signed_txs(),
689 spec: self,
690 }
691 }
692}
693
694pub struct CachedSignedVtxoTree {
698 pub spec: SignedVtxoTreeSpec,
699 pub txs: Vec<Transaction>,
701}
702
703impl CachedSignedVtxoTree {
704 pub fn exit_branch(&self, leaf_idx: usize) -> Option<Vec<&Transaction>> {
706 if leaf_idx >= self.spec.spec.nb_leaves() {
707 return None;
708 }
709
710 let tree = Tree::new(self.spec.spec.nb_leaves());
711 let mut ret = tree.iter_branch(leaf_idx)
712 .map(|n| &self.txs[n.idx()])
713 .collect::<Vec<_>>();
714 ret.reverse();
715 Some(ret)
716 }
717
718 pub fn nb_leaves(&self) -> usize {
719 self.spec.nb_leaves()
720 }
721
722 pub fn all_signed_txs(&self) -> &[Transaction] {
724 &self.txs
725 }
726
727 pub fn build_vtxo(&self, leaf_idx: usize) -> Option<Vtxo> {
729 let req = self.spec.spec.vtxos.get(leaf_idx)?;
730 let genesis = {
731 let mut genesis = Vec::new();
732
733 let mut last_node = None;
734 let tree = Tree::new(self.spec.spec.nb_leaves());
735 for node in tree.iter_branch(leaf_idx) {
736 let transition = GenesisTransition::Cosigned {
737 pubkeys: node.leaves().filter_map(|i| self.spec.spec.vtxos[i].cosign_pubkey)
738 .chain(self.spec.spec.global_cosign_pubkeys.iter().copied())
739 .collect(),
740 signature: self.spec.cosign_sigs.get(node.idx()).cloned()
741 .expect("enough sigs for all nodes"),
742 };
743 let output_idx = {
744 if let Some(last) = last_node {
745 node.children().position(|child_idx| last == child_idx)
746 .expect("last node should be our child") as u8
747 } else {
748 0
750 }
751 };
752 let other_outputs = self.txs.get(node.idx()).expect("we have all txs")
753 .output.iter().enumerate()
754 .filter(|(i, o)| !o.is_p2a_fee_anchor() && *i != output_idx as usize)
755 .map(|(_i, o)| o).cloned().collect();
756 genesis.push(GenesisItem { transition, output_idx, other_outputs });
757 last_node = Some(node.idx());
758 }
759 genesis.reverse();
760 genesis
761 };
762
763 Some(Vtxo {
764 amount: req.vtxo.amount,
765 expiry_height: self.spec.spec.expiry_height,
766 server_pubkey: self.spec.spec.server_pubkey,
767 exit_delta: self.spec.spec.exit_delta,
768 anchor_point: self.spec.utxo,
769 genesis: genesis,
770 policy: req.vtxo.policy.clone(),
771 point: {
772 let leaf_tx = self.txs.get(leaf_idx).expect("leaf idx exists");
773 OutPoint::new(leaf_tx.compute_txid(), 0)
774 },
775 })
776 }
777
778 pub fn all_vtxos(&self) -> impl Iterator<Item = Vtxo> + ExactSizeIterator + '_ {
782 (0..self.nb_leaves()).map(|idx| self.build_vtxo(idx).unwrap())
783 }
784}
785
786pub mod builder {
787 use std::collections::HashMap;
794 use std::marker::PhantomData;
795
796 use bitcoin::{Amount, OutPoint, ScriptBuf, TxOut};
797 use bitcoin::hashes::Hash;
798 use bitcoin::secp256k1::{Keypair, PublicKey};
799 use bitcoin_ext::{BlockDelta, BlockHeight};
800
801 use crate::{musig, SignedVtxoRequest, VtxoRequest};
802 use crate::error::IncorrectSigningKeyError;
803
804 use super::{CosignSignatureError, SignedVtxoTreeSpec, UnsignedVtxoTree, VtxoTreeSpec};
805
806 pub mod state {
807 mod sealed {
808 pub trait Sealed {}
810 impl Sealed for super::Preparing {}
811 impl Sealed for super::CanGenerateNonces {}
812 impl Sealed for super::ServerCanCosign {}
813 impl Sealed for super::CanFinish {}
814 }
815
816 pub trait BuilderState: sealed::Sealed {}
818
819 pub struct Preparing;
821 impl BuilderState for Preparing {}
822
823 pub struct CanGenerateNonces;
826 impl BuilderState for CanGenerateNonces {}
827
828 pub struct ServerCanCosign;
830 impl BuilderState for ServerCanCosign {}
831
832 pub struct CanFinish;
835 impl BuilderState for CanFinish {}
836
837 pub trait CanSign: BuilderState {}
840 impl CanSign for ServerCanCosign {}
841 impl CanSign for CanFinish {}
842 }
843
844 enum BuilderTree {
846 Spec(VtxoTreeSpec),
847 Unsigned(UnsignedVtxoTree),
848 }
849
850 impl BuilderTree {
851 fn unsigned_tree(&self) -> Option<&UnsignedVtxoTree> {
852 match self {
853 BuilderTree::Spec(_) => None,
854 BuilderTree::Unsigned(t) => Some(t),
855 }
856 }
857 fn into_unsigned_tree(self) -> Option<UnsignedVtxoTree> {
858 match self {
859 BuilderTree::Spec(_) => None,
860 BuilderTree::Unsigned(t) => Some(t),
861 }
862 }
863 }
864
865 pub struct SignedTreeBuilder<S: state::BuilderState> {
869 pub expiry_height: BlockHeight,
870 pub server_pubkey: PublicKey,
871 pub exit_delta: BlockDelta,
872 pub cosign_pubkey: PublicKey,
874
875 tree: BuilderTree,
876
877 user_pub_nonces: Vec<musig::PublicNonce>,
879 user_sec_nonces: Option<Vec<musig::SecretNonce>>,
882 _state: PhantomData<S>,
883 }
884
885 impl<T: state::BuilderState> SignedTreeBuilder<T> {
886 fn tree_spec(&self) -> &VtxoTreeSpec {
887 match self.tree {
888 BuilderTree::Spec(ref s) => s,
889 BuilderTree::Unsigned(ref t) => &t.spec,
890 }
891 }
892
893 pub fn total_required_value(&self) -> Amount {
895 self.tree_spec().total_required_value()
896 }
897
898 pub fn funding_script_pubkey(&self) -> ScriptBuf {
900 self.tree_spec().funding_tx_script_pubkey()
901 }
902
903 pub fn funding_txout(&self) -> TxOut {
905 let spec = self.tree_spec();
906 TxOut {
907 value: spec.total_required_value(),
908 script_pubkey: spec.funding_tx_script_pubkey(),
909 }
910 }
911 }
912
913 impl<T: state::CanSign> SignedTreeBuilder<T> {
914 pub fn user_pub_nonces(&self) -> &[musig::PublicNonce] {
916 assert!(!self.user_pub_nonces.is_empty(), "state invariant");
917 &self.user_pub_nonces
918 }
919 }
920
921 impl SignedTreeBuilder<state::Preparing> {
922 pub fn construct_tree_spec(
924 vtxos: impl IntoIterator<Item = VtxoRequest>,
925 cosign_pubkey: PublicKey,
926 expiry_height: BlockHeight,
927 server_pubkey: PublicKey,
928 server_cosign_pubkey: PublicKey,
929 exit_delta: BlockDelta,
930 ) -> VtxoTreeSpec {
931 let reqs = vtxos.into_iter()
932 .map(|vtxo| SignedVtxoRequest { cosign_pubkey: None, vtxo })
933 .collect::<Vec<_>>();
934 VtxoTreeSpec::new(
935 reqs,
936 server_pubkey,
937 expiry_height,
938 exit_delta,
939 vec![cosign_pubkey, server_cosign_pubkey],
942 )
943 }
944
945 pub fn new(
947 vtxos: impl IntoIterator<Item = VtxoRequest>,
948 cosign_pubkey: PublicKey,
949 expiry_height: BlockHeight,
950 server_pubkey: PublicKey,
951 server_cosign_pubkey: PublicKey,
952 exit_delta: BlockDelta,
953 ) -> SignedTreeBuilder<state::Preparing> {
954 let tree = Self::construct_tree_spec(
955 vtxos,
956 cosign_pubkey,
957 expiry_height,
958 server_pubkey,
959 server_cosign_pubkey,
960 exit_delta,
961 );
962
963 SignedTreeBuilder {
964 expiry_height, server_pubkey, exit_delta, cosign_pubkey,
965 tree: BuilderTree::Spec(tree),
966 user_pub_nonces: Vec::new(),
967 user_sec_nonces: None,
968 _state: PhantomData,
969 }
970 }
971
972 pub fn set_utxo(self, utxo: OutPoint) -> SignedTreeBuilder<state::CanGenerateNonces> {
974 let unsigned_tree = match self.tree {
975 BuilderTree::Spec(s) => s.into_unsigned_tree(utxo),
976 BuilderTree::Unsigned(t) => t, };
978 SignedTreeBuilder {
979 tree: BuilderTree::Unsigned(unsigned_tree),
980
981 expiry_height: self.expiry_height,
982 server_pubkey: self.server_pubkey,
983 exit_delta: self.exit_delta,
984 cosign_pubkey: self.cosign_pubkey,
985 user_pub_nonces: self.user_pub_nonces,
986 user_sec_nonces: self.user_sec_nonces,
987 _state: PhantomData,
988 }
989 }
990 }
991
992 impl SignedTreeBuilder<state::CanGenerateNonces> {
993 pub fn generate_user_nonces(
995 self,
996 cosign_key: &Keypair,
997 ) -> SignedTreeBuilder<state::CanFinish> {
998 let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
999
1000 let mut cosign_sec_nonces = Vec::with_capacity(unsigned_tree.sighashes.len());
1001 let mut cosign_pub_nonces = Vec::with_capacity(unsigned_tree.sighashes.len());
1002 for sh in &unsigned_tree.sighashes {
1003 let pair = musig::nonce_pair_with_msg(&cosign_key, &sh.to_byte_array());
1004 cosign_sec_nonces.push(pair.0);
1005 cosign_pub_nonces.push(pair.1);
1006 }
1007
1008 SignedTreeBuilder {
1009 user_pub_nonces: cosign_pub_nonces,
1010 user_sec_nonces: Some(cosign_sec_nonces),
1011
1012 expiry_height: self.expiry_height,
1013 server_pubkey: self.server_pubkey,
1014 exit_delta: self.exit_delta,
1015 cosign_pubkey: self.cosign_pubkey,
1016 tree: self.tree,
1017 _state: PhantomData,
1018 }
1019 }
1020 }
1021
1022 #[derive(Debug, Clone)]
1024 pub struct SignedTreeCosignResponse {
1025 pub pub_nonces: Vec<musig::PublicNonce>,
1026 pub partial_signatures: Vec<musig::PartialSignature>,
1027 }
1028
1029 impl SignedTreeBuilder<state::ServerCanCosign> {
1030 pub fn new_for_cosign(
1032 vtxos: impl IntoIterator<Item = VtxoRequest>,
1033 cosign_pubkey: PublicKey,
1034 expiry_height: BlockHeight,
1035 server_pubkey: PublicKey,
1036 server_cosign_pubkey: PublicKey,
1037 exit_delta: BlockDelta,
1038 utxo: OutPoint,
1039 user_pub_nonces: Vec<musig::PublicNonce>,
1040 ) -> SignedTreeBuilder<state::ServerCanCosign> {
1041 let unsigned_tree = SignedTreeBuilder::construct_tree_spec(
1042 vtxos,
1043 cosign_pubkey,
1044 expiry_height,
1045 server_pubkey,
1046 server_cosign_pubkey,
1047 exit_delta,
1048 ).into_unsigned_tree(utxo);
1049
1050 SignedTreeBuilder {
1051 expiry_height, server_pubkey, exit_delta, cosign_pubkey, user_pub_nonces,
1052 tree: BuilderTree::Unsigned(unsigned_tree),
1053 user_sec_nonces: None,
1054 _state: PhantomData,
1055 }
1056 }
1057
1058 pub fn server_cosign(&self, server_cosign_key: &Keypair) -> SignedTreeCosignResponse {
1060 let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1061
1062 let mut sec_nonces = Vec::with_capacity(unsigned_tree.sighashes.len());
1063 let mut pub_nonces = Vec::with_capacity(unsigned_tree.sighashes.len());
1064 for sh in &unsigned_tree.sighashes {
1065 let pair = musig::nonce_pair_with_msg(&server_cosign_key, &sh.to_byte_array());
1066 sec_nonces.push(pair.0);
1067 pub_nonces.push(pair.1);
1068 }
1069
1070 let agg_nonces = self.user_pub_nonces().iter().zip(&pub_nonces)
1071 .map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1072 .collect::<Vec<_>>();
1073
1074 let sigs = unsigned_tree.cosign_tree(&agg_nonces, &server_cosign_key, sec_nonces);
1075
1076 SignedTreeCosignResponse {
1077 pub_nonces,
1078 partial_signatures: sigs,
1079 }
1080 }
1081 }
1082
1083 impl SignedTreeBuilder<state::CanFinish> {
1084 pub fn verify_cosign_response(
1086 &self,
1087 server_cosign: &SignedTreeCosignResponse,
1088 ) -> Result<(), CosignSignatureError> {
1089 let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1090
1091 let agg_nonces = self.user_pub_nonces().iter()
1092 .zip(&server_cosign.pub_nonces)
1093 .map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1094 .collect::<Vec<_>>();
1095
1096 unsigned_tree.verify_global_cosign_partial_sigs(
1097 *unsigned_tree.spec.global_cosign_pubkeys.get(1).expect("state invariant"),
1098 &agg_nonces,
1099 &server_cosign.pub_nonces,
1100 &server_cosign.partial_signatures,
1101 )
1102 }
1103
1104 pub fn build_tree(
1105 self,
1106 server_cosign: &SignedTreeCosignResponse,
1107 cosign_key: &Keypair,
1108 ) -> Result<SignedVtxoTreeSpec, IncorrectSigningKeyError> {
1109 if cosign_key.public_key() != self.cosign_pubkey {
1110 return Err(IncorrectSigningKeyError {
1111 required: Some(self.cosign_pubkey),
1112 provided: cosign_key.public_key(),
1113 });
1114 }
1115
1116 let agg_nonces = self.user_pub_nonces().iter().zip(&server_cosign.pub_nonces)
1117 .map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1118 .collect::<Vec<_>>();
1119
1120 let unsigned_tree = self.tree.into_unsigned_tree().expect("state invariant");
1121 let sec_nonces = self.user_sec_nonces.expect("state invariant");
1122 let partial_sigs = unsigned_tree.cosign_tree(&agg_nonces, cosign_key, sec_nonces);
1123
1124 debug_assert!(unsigned_tree.verify_global_cosign_partial_sigs(
1125 self.cosign_pubkey,
1126 &agg_nonces,
1127 &self.user_pub_nonces,
1128 &partial_sigs,
1129 ).is_ok(), "produced invalid partial signatures");
1130
1131 let sigs = unsigned_tree.combine_partial_signatures(
1132 &agg_nonces,
1133 &HashMap::new(),
1134 &[&server_cosign.partial_signatures, &partial_sigs],
1135 ).expect("should work with correct cosign signatures");
1136
1137 Ok(unsigned_tree.into_signed_tree(sigs))
1138 }
1139 }
1140}
1141
1142const VTXO_TREE_SPEC_VERSION: u8 = 0x02;
1144
1145impl ProtocolEncoding for VtxoTreeSpec {
1146 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
1147 w.emit_u8(VTXO_TREE_SPEC_VERSION)?;
1148 w.emit_u32(self.expiry_height)?;
1149 self.server_pubkey.encode(w)?;
1150 w.emit_u16(self.exit_delta)?;
1151 w.emit_compact_size(self.global_cosign_pubkeys.len() as u64)?;
1152 for pk in &self.global_cosign_pubkeys {
1153 pk.encode(w)?;
1154 }
1155
1156 w.emit_compact_size(self.vtxos.len() as u64)?;
1157 for vtxo in &self.vtxos {
1158 vtxo.vtxo.policy.encode(w)?;
1159 w.emit_u64(vtxo.vtxo.amount.to_sat())?;
1160 if let Some(pk) = vtxo.cosign_pubkey {
1161 pk.encode(w)?;
1162 } else {
1163 w.emit_u8(0)?;
1164 }
1165 }
1166 Ok(())
1167 }
1168
1169 fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, crate::encode::ProtocolDecodingError> {
1170 let version = r.read_u8()?;
1171
1172 if version == 0x01 {
1174 let expiry_height = r.read_u32()?;
1175 let server_pubkey = PublicKey::decode(r)?;
1176 let exit_delta = r.read_u16()?;
1177 let server_cosign_pk = PublicKey::decode(r)?;
1178
1179 let nb_vtxos = r.read_u32()?;
1180 let mut vtxos = Vec::with_capacity(nb_vtxos as usize);
1181 for _ in 0..nb_vtxos {
1182 let output = VtxoPolicy::decode(r)?;
1183 let amount = Amount::from_sat(r.read_u64()?);
1184 let cosign_pk = PublicKey::decode(r)?;
1185 vtxos.push(SignedVtxoRequest {
1186 vtxo: VtxoRequest { policy: output, amount },
1187 cosign_pubkey: Some(cosign_pk),
1188 });
1189 }
1190
1191 return Ok(VtxoTreeSpec {
1192 vtxos, expiry_height, server_pubkey, exit_delta,
1193 global_cosign_pubkeys: vec![server_cosign_pk],
1194 });
1195 }
1196
1197 if version != VTXO_TREE_SPEC_VERSION {
1198 return Err(ProtocolDecodingError::invalid(format_args!(
1199 "invalid VtxoTreeSpec encoding version byte: {version:#x}",
1200 )));
1201 }
1202
1203 let expiry_height = r.read_u32()?;
1204 let server_pubkey = PublicKey::decode(r)?;
1205 let exit_delta = r.read_u16()?;
1206 let nb_global_signers = r.read_compact_size()?;
1207 let mut global_cosign_pubkeys = Vec::with_capacity(nb_global_signers as usize);
1208 for _ in 0..nb_global_signers {
1209 global_cosign_pubkeys.push(PublicKey::decode(r)?);
1210 }
1211
1212 let nb_vtxos = r.read_compact_size()?;
1213 let mut vtxos = Vec::with_capacity(nb_vtxos as usize);
1214 for _ in 0..nb_vtxos {
1215 let output = VtxoPolicy::decode(r)?;
1216 let amount = Amount::from_sat(r.read_u64()?);
1217 let cosign_pubkey = Option::<PublicKey>::decode(r)?;
1218 vtxos.push(SignedVtxoRequest { vtxo: VtxoRequest { policy: output, amount }, cosign_pubkey });
1219 }
1220
1221 Ok(VtxoTreeSpec { vtxos, expiry_height, server_pubkey, exit_delta, global_cosign_pubkeys })
1222 }
1223}
1224
1225const SIGNED_VTXO_TREE_SPEC_VERSION: u8 = 0x01;
1227
1228impl ProtocolEncoding for SignedVtxoTreeSpec {
1229 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
1230 w.emit_u8(SIGNED_VTXO_TREE_SPEC_VERSION)?;
1231 self.spec.encode(w)?;
1232 self.utxo.encode(w)?;
1233 w.emit_u32(self.cosign_sigs.len() as u32)?;
1234 for sig in &self.cosign_sigs {
1235 sig.encode(w)?;
1236 }
1237 Ok(())
1238 }
1239
1240 fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, crate::encode::ProtocolDecodingError> {
1241 let version = r.read_u8()?;
1242 if version != SIGNED_VTXO_TREE_SPEC_VERSION {
1243 return Err(ProtocolDecodingError::invalid(format_args!(
1244 "invalid SignedVtxoTreeSpec encoding version byte: {version:#x}",
1245 )));
1246 }
1247 let spec = VtxoTreeSpec::decode(r)?;
1248 let utxo = OutPoint::decode(r)?;
1249 let nb_cosign_sigs = r.read_u32()?;
1250 let mut cosign_sigs = Vec::with_capacity(nb_cosign_sigs as usize);
1251 for _ in 0..nb_cosign_sigs {
1252 cosign_sigs.push(schnorr::Signature::decode(r)?);
1253 }
1254 Ok(SignedVtxoTreeSpec { spec, utxo, cosign_sigs })
1255 }
1256}
1257
1258
1259#[cfg(test)]
1260mod test {
1261 use std::iter;
1262 use std::collections::HashMap;
1263 use std::str::FromStr;
1264
1265 use bitcoin::hashes::{siphash24, sha256, Hash, HashEngine};
1266 use bitcoin::secp256k1::{self, rand, Keypair};
1267 use bitcoin::{absolute, transaction};
1268 use rand::SeedableRng;
1269
1270 use crate::encode;
1271 use crate::encode::test::{encoding_roundtrip, json_roundtrip};
1272 use crate::vtxo::VtxoPolicy;
1273 use crate::tree::signed::builder::SignedTreeBuilder;
1274
1275 use super::*;
1276
1277 fn test_tree_amounts(
1278 tree: &UnsignedVtxoTree,
1279 root_value: Amount,
1280 ) {
1281 let map = tree.txs.iter().map(|tx| (tx.compute_txid(), tx)).collect::<HashMap<_, _>>();
1282
1283 for (idx, tx) in tree.txs.iter().take(tree.txs.len() - 1).enumerate() {
1285 println!("tx #{idx}: {}", bitcoin::consensus::encode::serialize_hex(tx));
1286 let input = tx.input.iter().map(|i| {
1287 let prev = i.previous_output;
1288 map.get(&prev.txid).expect(&format!("tx {} not found", prev.txid))
1289 .output[prev.vout as usize].value
1290 }).sum::<Amount>();
1291 let output = tx.output_value();
1292 assert!(input >= output);
1293 assert_eq!(input, output);
1294 }
1295
1296 let root = tree.txs.last().unwrap();
1298 assert_eq!(root_value, root.output_value());
1299 }
1300
1301 #[test]
1302 fn vtxo_tree() {
1303 let secp = secp256k1::Secp256k1::new();
1304 let mut rand = rand::rngs::StdRng::seed_from_u64(42);
1305 let random_sig = {
1306 let key = Keypair::new(&secp, &mut rand);
1307 let sha = sha256::Hash::from_str("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap();
1308 let msg = secp256k1::Message::from_digest(sha.to_byte_array());
1309 secp.sign_schnorr(&msg, &key)
1310 };
1311
1312 let server_key = Keypair::new(&secp, &mut rand);
1313 let server_cosign_key = Keypair::new(&secp, &mut rand);
1314
1315 struct Req {
1316 key: Keypair,
1317 cosign_key: Keypair,
1318 amount: Amount,
1319 }
1320 impl Req {
1321 fn to_vtxo(&self) -> SignedVtxoRequest {
1322 SignedVtxoRequest {
1323 vtxo: VtxoRequest {
1324 amount: self.amount,
1325 policy: VtxoPolicy::new_pubkey(self.key.public_key()),
1326 },
1327 cosign_pubkey: Some(self.cosign_key.public_key()),
1328 }
1329 }
1330 }
1331
1332 let nb_leaves = 27;
1333 let reqs = iter::repeat_with(|| Req {
1334 key: Keypair::new(&secp, &mut rand),
1335 cosign_key: Keypair::new(&secp, &mut rand),
1336 amount: Amount::from_sat(100_000),
1337 }).take(nb_leaves).collect::<Vec<_>>();
1338 let point = "0000000000000000000000000000000000000000000000000000000000000001:1".parse().unwrap();
1339
1340 let spec = VtxoTreeSpec::new(
1341 reqs.iter().map(|r| r.to_vtxo()).collect(),
1342 server_key.public_key(),
1343 101_000,
1344 2016,
1345 vec![server_cosign_key.public_key()],
1346 );
1347 assert_eq!(spec.nb_leaves(), nb_leaves);
1348 assert_eq!(spec.total_required_value().to_sat(), 2700000);
1349 let nb_nodes = spec.nb_nodes();
1350
1351 encoding_roundtrip(&spec);
1352
1353 let unsigned = spec.into_unsigned_tree(point);
1354
1355 test_tree_amounts(&unsigned, unsigned.spec.total_required_value());
1356
1357 let sighashes_hash = {
1358 let mut eng = siphash24::Hash::engine();
1359 unsigned.sighashes.iter().for_each(|h| eng.input(&h[..]));
1360 siphash24::Hash::from_engine(eng)
1361 };
1362 assert_eq!(sighashes_hash.to_string(), "44c13179cd19569f");
1363
1364 let signed = unsigned.into_signed_tree(vec![random_sig; nb_nodes]);
1365
1366 encoding_roundtrip(&signed);
1367
1368 #[derive(Debug, PartialEq, Serialize, Deserialize)]
1369 struct JsonSignedVtxoTreeSpec {
1370 #[serde(with = "encode::serde")]
1371 pub spec: SignedVtxoTreeSpec,
1372 }
1373
1374 json_roundtrip(&JsonSignedVtxoTreeSpec { spec: signed.clone() });
1375
1376 for l in 0..nb_leaves {
1377 let exit = signed.exit_branch(l).unwrap();
1378
1379 let mut iter = exit.iter().enumerate().peekable();
1381 while let Some((i, cur)) = iter.next() {
1382 if let Some((_, next)) = iter.peek() {
1383 assert_eq!(next.input[0].previous_output.txid, cur.compute_txid(), "{}", i);
1384 }
1385 }
1386 }
1387
1388 let cached = signed.into_cached_tree();
1389 for vtxo in cached.all_vtxos() {
1390 encoding_roundtrip(&vtxo);
1391 }
1392 }
1393
1394 #[test]
1395 fn test_tree_builder() {
1396 let expiry = 100_000;
1397 let exit_delta = 24;
1398
1399 let vtxo_pubkey = "035e160cd261ac8ffcd2866a5aab2116bc90fbefdb1d739531e121eee612583802".parse().unwrap();
1400 let policy = VtxoPolicy::new_pubkey(vtxo_pubkey);
1401 let vtxos = (1..50).map(|i| VtxoRequest {
1402 amount: Amount::from_sat(1000 * i),
1403 policy: policy.clone(),
1404 }).collect::<Vec<_>>();
1405
1406 let user_cosign_key = Keypair::from_str("5255d132d6ec7d4fc2a41c8f0018bb14343489ddd0344025cc60c7aa2b3fda6a").unwrap();
1407 let user_cosign_pubkey = user_cosign_key.public_key();
1408 println!("cosign_pubkey: {}", user_cosign_pubkey);
1409
1410 let server_key = Keypair::from_str("1fb316e653eec61de11c6b794636d230379509389215df1ceb520b65313e5426").unwrap();
1411 let server_pubkey = server_key.public_key();
1412 println!("server_pubkey: {}", server_pubkey);
1413
1414 let server_cosign_key = Keypair::from_str("52a506fbae3b725749d2486afd4761841ec685b841c2967e30f24182c4b02eed").unwrap();
1415 let server_cosign_pubkey = server_cosign_key.public_key();
1416 println!("server_cosign_pubkey: {}", server_cosign_pubkey);
1417
1418 let builder = SignedTreeBuilder::new(
1419 vtxos.iter().cloned(), user_cosign_pubkey, expiry, server_pubkey, server_cosign_pubkey,
1420 exit_delta,
1421 );
1422 assert_eq!(builder.total_required_value(), Amount::from_sat(1_225_000));
1423 assert_eq!(builder.funding_script_pubkey().to_hex_string(), "5120ca542aaf6c76c4b4c7822d73d91551ef42482098f3675d915d61782448b2ac5b");
1424
1425 let funding_tx = Transaction {
1426 version: transaction::Version::TWO,
1427 lock_time: absolute::LockTime::ZERO,
1428 input: vec![],
1429 output: vec![builder.funding_txout()],
1430 };
1431 let utxo = OutPoint::new(funding_tx.compute_txid(), 0);
1432 assert_eq!(utxo.to_string(), "49b930a56b7eb510813600b54d01e6017cbb41895e137892c0b41e6399779ef7:0");
1433 let builder = builder.set_utxo(utxo).generate_user_nonces(&user_cosign_key);
1434 let user_pub_nonces = builder.user_pub_nonces().to_vec();
1435
1436 let cosign = {
1437 let builder = SignedTreeBuilder::new_for_cosign(
1438 vtxos.iter().cloned(), user_cosign_pubkey, expiry, server_pubkey,
1439 server_cosign_pubkey, exit_delta, utxo, user_pub_nonces,
1440 );
1441 builder.server_cosign(&server_cosign_key)
1442 };
1443
1444 builder.verify_cosign_response(&cosign).unwrap();
1445 let tree = builder.build_tree(&cosign, &user_cosign_key).unwrap().into_cached_tree();
1446
1447 for vtxo in tree.all_vtxos() {
1449 vtxo.validate(&funding_tx).expect("The new vtxo is valid");
1450 }
1451 }
1452}