Skip to main content

ark/tree/
signed.rs

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, Txid, 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::{musig, scripts, ServerVtxoPolicy, Vtxo, VtxoId, VtxoPolicy, VtxoRequest, SECP};
17use crate::encode::{
18	LengthPrefixedVector, OversizedVectorError, ProtocolDecodingError, ProtocolEncoding, ReadExt, WriteExt
19};
20use crate::error::IncorrectSigningKeyError;
21use crate::tree::{self, Tree};
22use crate::vtxo::{self, Full, GenesisItem, GenesisTransition, HarkLeafVtxoPolicy, MaybePreimage, ServerVtxo, TapScriptClause};
23use crate::vtxo::policy::{check_block_delta, check_block_height, HarkLeaf_v0_VtxoPolicy};
24use crate::vtxo::policy::clause::TimelockSignClause;
25
26
27/// Hash to lock hArk VTXOs from users before forfeits
28pub type UnlockHash = sha256::Hash;
29
30/// Preimage to unlock hArk VTXOs
31pub type UnlockPreimage = [u8; 32];
32
33/// The upper bound witness weight to spend a node transaction.
34pub const NODE_SPEND_WEIGHT: Weight = Weight::from_wu(140);
35
36/// The expiry clause hidden in the node taproot as only script.
37pub fn expiry_clause(server_pubkey: PublicKey, expiry_height: BlockHeight) -> ScriptBuf {
38	TimelockSignClause { pubkey: server_pubkey, timelock_height: expiry_height }.tapscript()
39}
40
41/// The hash-based unlock clause that requires a signature and a preimage
42///
43/// It is used hidden in the leaf taproot as only script or used in the forfeit output.
44pub fn unlock_clause(pubkey: XOnlyPublicKey, unlock_hash: UnlockHash) -> ScriptBuf {
45	scripts::hash_and_sign(unlock_hash, pubkey)
46}
47
48/// The taproot of the leaf policy, i.e. of the output that is spent by the leaf tx
49///
50/// This output is guarded by user+server key and a hash preimage.
51///
52/// The internal key is set to the MuSig of user's VTXO key + server pubkey,
53/// but the keyspend clause is currently not used in the protocol.
54pub fn leaf_cosign_taproot(
55	user_pubkey: PublicKey,
56	server_pubkey: PublicKey,
57	expiry_height: BlockHeight,
58	unlock_hash: UnlockHash,
59) -> taproot::TaprootSpendInfo {
60	HarkLeafVtxoPolicy { user_pubkey, unlock_hash }.taproot(server_pubkey, expiry_height)
61}
62
63/// The hash-based unlock clause that requires a signature and a preimage
64///
65/// It is used hidden in the leaf taproot as only script or used in the forfeit output.
66pub fn unlock_clause_v0(pubkey: XOnlyPublicKey, unlock_hash: UnlockHash) -> ScriptBuf {
67	scripts::hash_and_sign_v0(unlock_hash, pubkey)
68}
69
70/// The taproot of the leaf policy, i.e. of the output that is spent by the leaf tx
71///
72/// This output is guarded by user+server key and a hash preimage.
73///
74/// The internal key is set to the MuSig of user's VTXO key + server pubkey,
75/// but the keyspend clause is currently not used in the protocol.
76pub fn leaf_cosign_taproot_v0(
77	user_pubkey: PublicKey,
78	server_pubkey: PublicKey,
79	expiry_height: BlockHeight,
80	unlock_hash: UnlockHash,
81) -> taproot::TaprootSpendInfo {
82	HarkLeaf_v0_VtxoPolicy { user_pubkey, unlock_hash }.taproot(server_pubkey, expiry_height)
83}
84
85/// The taproot spend info of an output that is spent by an internal node tx
86pub fn cosign_taproot(
87	agg_pk: XOnlyPublicKey,
88	server_pubkey: PublicKey,
89	expiry_height: BlockHeight,
90) -> taproot::TaprootSpendInfo {
91	taproot::TaprootBuilder::new()
92		.add_leaf(0, expiry_clause(server_pubkey, expiry_height)).unwrap()
93		.finalize(&SECP, agg_pk).unwrap()
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub struct VtxoLeafSpec {
98	/// The actual VTXO request.
99	pub vtxo: VtxoRequest,
100
101	/// The public key used by the client to cosign the internal txs of the tree
102	///
103	/// Only interactive participants have a cosign key here.
104	///
105	/// The client SHOULD forget this key after signing the transaction tree.
106	/// Non-interactive participants don't have a cosign pubkey.
107	pub cosign_pubkey: Option<PublicKey>,
108
109	/// The unlock hash used to lock the VTXO before forfeits are signed
110	pub unlock_hash: UnlockHash,
111}
112
113impl ProtocolEncoding for VtxoLeafSpec {
114	fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
115		self.vtxo.policy.encode(w)?;
116		w.emit_u64(self.vtxo.amount.to_sat())?;
117		self.cosign_pubkey.encode(w)?;
118		self.unlock_hash.encode(w)?;
119		Ok(())
120	}
121
122	fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, ProtocolDecodingError> {
123		Ok(VtxoLeafSpec {
124			vtxo: VtxoRequest {
125				policy: VtxoPolicy::decode(r)?,
126				amount: Amount::from_sat(r.read_u64()?),
127			},
128			cosign_pubkey: Option::<PublicKey>::decode(r)?,
129			unlock_hash: sha256::Hash::decode(r)?,
130		})
131	}
132}
133
134/// All the information that uniquely specifies a VTXO tree before it has been signed.
135#[derive(Debug, Clone, Eq, PartialEq)]
136pub struct VtxoTreeSpec {
137	pub vtxos: Vec<VtxoLeafSpec>,
138	pub expiry_height: BlockHeight,
139	pub server_pubkey: PublicKey,
140	pub exit_delta: BlockDelta,
141	pub global_cosign_pubkeys: Vec<PublicKey>,
142}
143
144#[derive(Clone, Copy)]
145enum ChildSpec<'a> {
146	Leaf {
147		spec: &'a VtxoLeafSpec,
148	},
149	Internal {
150		output_value: Amount,
151		agg_pk: PublicKey,
152	},
153}
154
155impl VtxoTreeSpec {
156	pub fn new(
157		vtxos: Vec<VtxoLeafSpec>,
158		server_pubkey: PublicKey,
159		expiry_height: BlockHeight,
160		exit_delta: BlockDelta,
161		global_cosign_pubkeys: Vec<PublicKey>,
162	) -> VtxoTreeSpec {
163		assert_ne!(vtxos.len(), 0);
164		VtxoTreeSpec { vtxos, server_pubkey, expiry_height, exit_delta, global_cosign_pubkeys }
165	}
166
167	pub fn nb_leaves(&self) -> usize {
168		self.vtxos.len()
169	}
170
171	pub fn nb_nodes(&self) -> usize {
172		Tree::nb_nodes_for_leaves(self.nb_leaves())
173	}
174
175	pub fn nb_internal_nodes(&self) -> usize {
176		Tree::nb_nodes_for_leaves(self.nb_leaves()).checked_sub(self.nb_leaves())
177			.expect("tree can't have less nodes than leaves")
178	}
179
180	pub fn iter_vtxos(&self) -> impl Iterator<Item = &VtxoLeafSpec> {
181		self.vtxos.iter()
182	}
183
184	/// Get the leaf index of the given leaf spec.
185	pub fn leaf_idx_of(&self, leaf_spec: &VtxoLeafSpec) -> Option<usize> {
186		self.vtxos.iter().position(|e| e == leaf_spec)
187	}
188
189	/// Get the leaf index of the given vtxo request.
190	///
191	/// Note that in the case of duplicate vtxo requests, this function can
192	/// return any of the indices of these requests.
193	pub fn leaf_idx_of_req(&self, vtxo_request: &VtxoRequest) -> Option<usize> {
194		self.vtxos.iter().position(|e| e.vtxo == *vtxo_request)
195	}
196
197	/// Calculate the total value needed in the tree.
198	///
199	/// This accounts for
200	/// - all vtxos getting their value
201	pub fn total_required_value(&self) -> Amount {
202		self.vtxos.iter().map(|d| d.vtxo.amount).sum::<Amount>()
203	}
204
205	/// Calculate the taproot spend info for a leaf node
206	pub fn leaf_taproot(
207		&self,
208		user_pubkey: PublicKey,
209		unlock_hash: UnlockHash,
210	) -> taproot::TaprootSpendInfo {
211		leaf_cosign_taproot(user_pubkey, self.server_pubkey, self.expiry_height, unlock_hash)
212	}
213
214	/// Calculate the taproot spend info for internal nodes
215	pub fn internal_taproot(&self, agg_pk: XOnlyPublicKey) -> taproot::TaprootSpendInfo {
216		cosign_taproot(agg_pk, self.server_pubkey, self.expiry_height)
217	}
218
219	/// The cosign pubkey used on the vtxo output of the tx funding the tree
220	///
221	/// In Ark rounds this will be the round funding tx scriptPubkey.
222	pub fn funding_tx_cosign_pubkey(&self) -> XOnlyPublicKey {
223		let keys = self.vtxos.iter()
224			.filter_map(|v| v.cosign_pubkey)
225			.chain(self.global_cosign_pubkeys.iter().copied());
226		musig::combine_keys(keys).x_only_public_key().0
227	}
228
229	/// The scriptPubkey used on the vtxo output of the tx funding the tree
230	///
231	/// In Ark rounds this will be the round funding tx scriptPubkey.
232	pub fn funding_tx_script_pubkey(&self) -> ScriptBuf {
233		let agg_pk = self.funding_tx_cosign_pubkey();
234		self.internal_taproot(agg_pk).script_pubkey()
235	}
236
237	/// The output of the tx funding the tree
238	///
239	/// In Ark rounds this will be the round funding tx output.
240	pub fn funding_tx_txout(&self) -> TxOut {
241		TxOut {
242			script_pubkey: self.funding_tx_script_pubkey(),
243			value: self.total_required_value(),
244		}
245	}
246
247	/// Create a node tx
248	///
249	/// The children are an iterator over the next tx, its cosign pubkey
250	/// and the unlock hash if the child is a leaf.
251	fn node_tx<'a>(
252		&self,
253		children: impl Iterator<Item = ChildSpec<'a>>,
254	) -> Transaction {
255		Transaction {
256			version: bitcoin::transaction::Version(3),
257			lock_time: bitcoin::absolute::LockTime::ZERO,
258			input: vec![TxIn {
259				previous_output: OutPoint::null(), // we will fill this later
260				sequence: Sequence::ZERO,
261				script_sig: ScriptBuf::new(),
262				witness: Witness::new(),
263			}],
264			output: children.map(|child| match child {
265				ChildSpec::Leaf { spec } => {
266					let taproot = self.leaf_taproot(
267						spec.vtxo.policy.user_pubkey(),
268						spec.unlock_hash,
269					);
270					TxOut {
271						script_pubkey: taproot.script_pubkey(),
272						value: spec.vtxo.amount,
273					}
274				},
275				ChildSpec::Internal { output_value, agg_pk } => {
276					let taproot = self.internal_taproot(agg_pk.x_only_public_key().0);
277					TxOut {
278						script_pubkey: taproot.script_pubkey(),
279						value: output_value,
280					}
281				},
282			}).chain(Some(fee::fee_anchor())).collect(),
283		}
284	}
285
286	fn leaf_tx(&self, vtxo: &VtxoRequest) -> Transaction {
287		let txout = TxOut {
288			value: vtxo.amount,
289			script_pubkey: vtxo.policy.script_pubkey(self.server_pubkey, self.exit_delta, self.expiry_height),
290		};
291
292		// We collect fees in rounds by the difference in value between the inputs and outputs which
293		// is enforced by the round payment validation code. Therefore, we can leave fees set to
294		// zero for the leaf tx.
295		vtxo::create_exit_tx(OutPoint::null(), txout, None, Amount::ZERO)
296	}
297
298	/// Calculate all the aggregate cosign pubkeys by aggregating the leaf and server pubkeys.
299	///
300	/// Pubkeys expected and returned ordered from leaves to root.
301	pub fn cosign_agg_pks(&self)
302		-> impl Iterator<Item = PublicKey> + iter::DoubleEndedIterator + iter::ExactSizeIterator + '_
303	{
304		Tree::new(self.nb_leaves()).into_iter().map(|node| {
305			if node.is_leaf() {
306				musig::combine_keys([
307					self.vtxos[node.idx()].vtxo.policy.user_pubkey(),
308					self.server_pubkey,
309				])
310			} else {
311				musig::combine_keys(
312					node.leaves().filter_map(|i| self.vtxos[i].cosign_pubkey)
313						.chain(self.global_cosign_pubkeys.iter().copied())
314				)
315			}
316		})
317	}
318
319	/// Return unsigned transactions for all nodes from leaves to root.
320	pub fn unsigned_transactions(&self, utxo: OutPoint) -> Vec<Transaction> {
321		let tree = Tree::new(self.nb_leaves());
322
323		let cosign_agg_pks = self.cosign_agg_pks().collect::<Vec<_>>();
324
325		let mut txs = Vec::<Transaction>::with_capacity(tree.nb_nodes());
326		for node in tree.iter() {
327			let tx = if node.is_leaf() {
328				self.leaf_tx(&self.vtxos[node.idx()].vtxo).clone()
329			} else {
330				let mut buf = [None; tree::RADIX];
331				for (idx, child) in node.children().enumerate() {
332					let child = if let Some(spec) = self.vtxos.get(child) {
333						ChildSpec::Leaf { spec }
334					} else {
335						ChildSpec::Internal {
336							output_value: txs[child].output_value(),
337							agg_pk: cosign_agg_pks[child],
338						}
339					};
340					buf[idx] = Some(child);
341				}
342				self.node_tx(buf.iter().filter_map(|x| *x))
343			};
344			txs.push(tx.clone());
345		};
346
347		// set the prevouts
348		txs.last_mut().unwrap().input[0].previous_output = utxo;
349		for node in tree.iter().rev() {
350			let txid = txs[node.idx()].compute_txid();
351			for (i, child) in node.children().enumerate() {
352				let point = OutPoint::new(txid, u32::try_from(i).expect("tree child index fits in u32"));
353				txs[child].input[0].previous_output = point;
354			}
355		}
356
357		txs
358	}
359
360	/// Return all final transactions for all nodes from leaves to root
361	///
362	/// Internal transactions are signed, leaf txs not.
363	pub fn final_transactions(
364		&self,
365		utxo: OutPoint,
366		internal_signatures: &[schnorr::Signature],
367	) -> Vec<Transaction> {
368		let mut txs = self.unsigned_transactions(utxo);
369		for (tx, sig) in txs.iter_mut().skip(self.nb_leaves()).zip(internal_signatures) {
370			tx.input[0].witness.push(&sig[..]);
371		}
372		txs
373	}
374
375	/// Calculate all the aggregate cosign nonces by aggregating the leaf and server nonces.
376	///
377	/// Nonces expected and returned for all internal nodes ordered from leaves to root.
378	pub fn calculate_cosign_agg_nonces(
379		&self,
380		leaf_cosign_nonces: &HashMap<PublicKey, Vec<PublicNonce>>,
381		global_signer_cosign_nonces: &[impl AsRef<[PublicNonce]>],
382	) -> Result<Vec<AggregatedNonce>, String> {
383		if global_signer_cosign_nonces.len() != self.global_cosign_pubkeys.len() {
384			return Err("missing global signer nonces".into());
385		}
386
387		Tree::new(self.nb_leaves()).iter_internal().enumerate().map(|(idx, node)| {
388			let mut nonces = Vec::new();
389			for pk in node.leaves().filter_map(|i| self.vtxos[i].cosign_pubkey) {
390				nonces.push(leaf_cosign_nonces.get(&pk)
391					.ok_or_else(|| format!("missing nonces for leaf pk {}", pk))?
392					// note that we skip some nonces for some leaves that are at the edges
393					// and skip some levels
394					.get(node.internal_level())
395					.ok_or_else(|| format!("not enough nonces for leaf_pk {}", pk))?
396				);
397			}
398			for glob in global_signer_cosign_nonces {
399				nonces.push(glob.as_ref().get(idx).ok_or("not enough global cosign nonces")?);
400			}
401			Ok(musig::nonce_agg(&nonces))
402		}).collect()
403	}
404
405	/// Convert this spec into an unsigned tree by providing the
406	/// root outpoint and the nodes' aggregate nonces.
407	///
408	/// Nonces expected ordered from leaves to root.
409	pub fn into_unsigned_tree(
410		self,
411		utxo: OutPoint,
412	) -> UnsignedVtxoTree {
413		UnsignedVtxoTree::new(self, utxo)
414	}
415}
416
417/// A VTXO tree ready to be signed.
418///
419/// This type contains various cached values required to sign the tree.
420#[derive(Debug, Clone)]
421pub struct UnsignedVtxoTree {
422	pub spec: VtxoTreeSpec,
423	pub utxo: OutPoint,
424
425	// the following fields are calculated from the above
426
427	/// Aggregate pubkeys for the inputs to all nodes, leaves to root.
428	pub cosign_agg_pks: Vec<PublicKey>,
429	/// Transactions for all nodes, leaves to root.
430	pub txs: Vec<Transaction>,
431	/// Sighashes for the only input of the tx for all internal nodes,
432	/// leaves to root.
433	pub internal_sighashes: Vec<TapSighash>,
434
435	tree: Tree,
436}
437
438impl UnsignedVtxoTree {
439	pub fn new(
440		spec: VtxoTreeSpec,
441		utxo: OutPoint,
442	) -> UnsignedVtxoTree {
443		let tree = Tree::new(spec.nb_leaves());
444
445		let cosign_agg_pks = spec.cosign_agg_pks().collect::<Vec<_>>();
446		let txs = spec.unsigned_transactions(utxo);
447
448		let root_txout = spec.funding_tx_txout();
449		let internal_sighashes = tree.iter_internal().map(|node| {
450			let prev = if let Some((parent, sibling_idx))
451				= tree.parent_idx_of_with_sibling_idx(node.idx())
452			{
453				assert!(!node.is_root());
454				&txs[parent].output[sibling_idx]
455			} else {
456				assert!(node.is_root());
457				&root_txout
458			};
459
460			let mut shc = SighashCache::new(&txs[node.idx()]);
461			shc.taproot_key_spend_signature_hash(
462				0, // input idx is always 0
463				&sighash::Prevouts::All(&[prev]),
464				TapSighashType::Default,
465			).expect("sighash error")
466		}).collect();
467
468		UnsignedVtxoTree { spec, utxo, txs, internal_sighashes, cosign_agg_pks, tree }
469	}
470
471	pub fn nb_leaves(&self) -> usize {
472		self.tree.nb_leaves()
473	}
474
475	/// The number of leaves that have a cosign pubkey
476	pub fn nb_cosigned_leaves(&self) -> usize {
477		self.spec.vtxos.iter()
478			.filter(|v| v.cosign_pubkey.is_some())
479			.count()
480	}
481
482	pub fn nb_nodes(&self) -> usize {
483		self.tree.nb_nodes()
484	}
485
486	pub fn nb_internal_nodes(&self) -> usize {
487		self.tree.nb_internal_nodes()
488	}
489
490	/// Generate partial musig signatures for the nodes in the tree branch of the given
491	/// vtxo request.
492	///
493	/// Note that the signatures are indexed by their place in the tree and thus do not
494	/// necessarily match up with the indices in the secret nonces vector.
495	///
496	/// Aggregate nonces expected for all nodes, ordered from leaves to root.
497	/// Secret nonces expected for branch, ordered from leaf to root.
498	///
499	/// Returns [None] if the vtxo request is not part of the tree.
500	/// Returned signatures over the branch from leaf to root.
501	//TODO(stevenroose) streamline indices of nonces and sigs
502	pub fn cosign_branch(
503		&self,
504		cosign_agg_nonces: &[AggregatedNonce],
505		leaf_idx: usize,
506		cosign_key: &Keypair,
507		cosign_sec_nonces: Vec<SecretNonce>,
508	) -> Result<Vec<PartialSignature>, IncorrectSigningKeyError> {
509		let req = self.spec.vtxos.get(leaf_idx).expect("leaf idx out of bounds");
510		if Some(cosign_key.public_key()) != req.cosign_pubkey {
511			return Err(IncorrectSigningKeyError {
512				required: req.cosign_pubkey,
513				provided: cosign_key.public_key(),
514			});
515		}
516
517		let mut nonce_iter = cosign_sec_nonces.into_iter().enumerate();
518		let mut ret = Vec::with_capacity(self.tree.root().level().saturating_add(1));
519		// skip the leaf
520		for node in self.tree.iter_branch(leaf_idx).skip(1) {
521			// Since we can skip a level, we sometimes have to skip a nonce.
522			// NB We can't just use the index into the sec_nonces vector, because
523			// musig requires us to use the owned SecNonce type to prevent footgun
524			// by reusing secret nonces.
525			let sec_nonce = loop {
526				let next = nonce_iter.next().expect("level overflow");
527				if next.0 == node.internal_level() {
528					break next.1;
529				}
530			};
531
532			let cosign_pubkeys = node.leaves()
533				.filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
534				.chain(self.spec.global_cosign_pubkeys.iter().copied());
535			let sighash = self.internal_sighashes[node.internal_idx()];
536
537			let agg_pk = self.cosign_agg_pks[node.idx()].x_only_public_key().0;
538			let tweak = self.spec.internal_taproot(agg_pk).tap_tweak().to_byte_array();
539			let sig = musig::partial_sign(
540				cosign_pubkeys,
541				cosign_agg_nonces[node.internal_idx()],
542				&cosign_key,
543				sec_nonce,
544				sighash.to_byte_array(),
545				Some(tweak),
546				None,
547			).0;
548			ret.push(sig);
549		}
550
551		Ok(ret)
552	}
553
554	/// Generate partial musig signatures for all internal nodes in the tree.
555	///
556	/// Nonces expected for all internal nodes, ordered from leaves to root.
557	///
558	/// Returns [None] if the vtxo request is not part of the tree.
559	pub fn cosign_tree(
560		&self,
561		cosign_agg_nonces: &[AggregatedNonce],
562		keypair: &Keypair,
563		cosign_sec_nonces: Vec<SecretNonce>,
564	) -> Vec<PartialSignature> {
565		debug_assert_eq!(cosign_agg_nonces.len(), self.nb_internal_nodes());
566		debug_assert_eq!(cosign_sec_nonces.len(), self.nb_internal_nodes());
567
568		let nonces = cosign_sec_nonces.into_iter().zip(cosign_agg_nonces);
569		self.tree.iter_internal().zip(nonces).map(|(node, (sec_nonce, agg_nonce))| {
570			let sighash = self.internal_sighashes[node.internal_idx()];
571
572			let cosign_pubkeys = node.leaves()
573				.filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
574				.chain(self.spec.global_cosign_pubkeys.iter().copied());
575			let agg_pk = self.cosign_agg_pks[node.idx()];
576			debug_assert_eq!(agg_pk, musig::combine_keys(cosign_pubkeys.clone()));
577			let taproot = self.spec.internal_taproot(agg_pk.x_only_public_key().0);
578			musig::partial_sign(
579				cosign_pubkeys,
580				*agg_nonce,
581				&keypair,
582				sec_nonce,
583				sighash.to_byte_array(),
584				Some(taproot.tap_tweak().to_byte_array()),
585				None,
586			).0
587		}).collect()
588	}
589
590	/// Verify partial cosign signature of a single internal node
591	fn verify_internal_node_cosign_partial_sig(
592		&self,
593		node: &tree::Node,
594		pk: PublicKey,
595		agg_nonces: &[AggregatedNonce],
596		part_sig: PartialSignature,
597		pub_nonce: PublicNonce,
598	) -> Result<(), CosignSignatureError> {
599		debug_assert!(!node.is_leaf());
600
601		let sighash = self.internal_sighashes[node.internal_idx()];
602
603		let key_agg = {
604			let cosign_pubkeys = node.leaves()
605				.filter_map(|i| self.spec.vtxos[i].cosign_pubkey)
606				.chain(self.spec.global_cosign_pubkeys.iter().copied());
607			let agg_pk = self.cosign_agg_pks[node.idx()].x_only_public_key().0;
608			let taproot = self.spec.internal_taproot(agg_pk);
609			let taptweak = taproot.tap_tweak().to_byte_array();
610			musig::tweaked_key_agg(cosign_pubkeys, taptweak).0
611		};
612		let agg_nonce = agg_nonces.get(node.internal_idx())
613			.ok_or(CosignSignatureError::NotEnoughNonces)?;
614		let session = musig::Session::new(&key_agg, *agg_nonce, &sighash.to_byte_array());
615		let ok = session.partial_verify(&key_agg, &part_sig, &pub_nonce, musig::pubkey_to(pk));
616		if !ok {
617			return Err(CosignSignatureError::invalid_sig(pk));
618		}
619		Ok(())
620	}
621
622	/// Verify the partial cosign signatures from one of the leaves.
623	///
624	/// Nonces and partial signatures expected for all internal nodes,
625	/// ordered from leaves to root.
626	pub fn verify_branch_cosign_partial_sigs(
627		&self,
628		cosign_agg_nonces: &[AggregatedNonce],
629		request: &VtxoLeafSpec,
630		cosign_pub_nonces: &[PublicNonce],
631		cosign_part_sigs: &[PartialSignature],
632	) -> Result<(), String> {
633		assert_eq!(cosign_agg_nonces.len(), self.nb_internal_nodes());
634
635		let cosign_pubkey = request.cosign_pubkey.ok_or("no cosign pubkey for request")?;
636		let leaf_idx = self.spec.leaf_idx_of(request).ok_or("request not in tree")?;
637
638		// skip the leaf of the branch we verify
639		let internal_branch = self.tree.iter_branch(leaf_idx).skip(1);
640
641		// quickly check if the number of sigs is sane
642		match internal_branch.clone().count().cmp(&cosign_part_sigs.len()) {
643			cmp::Ordering::Less => return Err("too few partial signatures".into()),
644			cmp::Ordering::Greater => return Err("too many partial signatures".into()),
645			cmp::Ordering::Equal => {},
646		}
647
648		let mut part_sigs_iter = cosign_part_sigs.iter();
649		let mut pub_nonce_iter = cosign_pub_nonces.iter().enumerate();
650		for node in internal_branch {
651			let pub_nonce = loop {
652				let next = pub_nonce_iter.next().ok_or("not enough pub nonces")?;
653				if next.0 == node.internal_level() {
654					break next.1;
655				}
656			};
657			self.verify_internal_node_cosign_partial_sig(
658				node,
659				cosign_pubkey,
660				cosign_agg_nonces,
661				part_sigs_iter.next().ok_or("not enough sigs")?.clone(),
662				*pub_nonce,
663			).map_err(|e| format!("part sig verification failed: {}", e))?;
664		}
665
666		Ok(())
667	}
668
669	/// Verify the partial cosign signatures for all nodes.
670	///
671	/// Nonces and partial signatures expected for all internal nodes,
672	/// ordered from leaves to root.
673	pub fn verify_global_cosign_partial_sigs(
674		&self,
675		pk: PublicKey,
676		agg_nonces: &[AggregatedNonce],
677		pub_nonces: &[PublicNonce],
678		part_sigs: &[PartialSignature],
679	) -> Result<(), CosignSignatureError> {
680		for node in self.tree.iter_internal() {
681			let sigs = *part_sigs.get(node.internal_idx())
682				.ok_or_else(|| CosignSignatureError::missing_sig(pk))?;
683			let nonces = *pub_nonces.get(node.internal_idx())
684				.ok_or_else(|| CosignSignatureError::NotEnoughNonces)?;
685			self.verify_internal_node_cosign_partial_sig(node, pk, agg_nonces, sigs, nonces)?;
686		}
687
688		Ok(())
689	}
690
691	/// Combine all partial cosign signatures.
692	///
693	/// Nonces expected for all internal nodes, ordered from leaves to root.
694	///
695	/// Branch signatures expected for internal nodes in branch ordered from leaf to root.
696	///
697	/// Server signatures expected for all internal nodes ordered from leaves to root,
698	/// in the same order as `global_cosign_pubkeys`.
699	pub fn combine_partial_signatures(
700		&self,
701		cosign_agg_nonces: &[AggregatedNonce],
702		branch_part_sigs: &HashMap<PublicKey, Vec<PartialSignature>>,
703		global_signer_part_sigs: &[impl AsRef<[PartialSignature]>],
704	) -> Result<Vec<schnorr::Signature>, CosignSignatureError> {
705		// to ease implementation, we're reconstructing the part sigs map with dequeues
706		let mut leaf_part_sigs = branch_part_sigs.iter()
707			.map(|(pk, sigs)| (pk, sigs.iter().collect()))
708			.collect::<HashMap<_, VecDeque<_>>>();
709
710		if global_signer_part_sigs.len() != self.spec.global_cosign_pubkeys.len() {
711			return Err(CosignSignatureError::Invalid(
712				"invalid nb of global cosigner partial signatures",
713			));
714		}
715		for (pk, sigs) in self.spec.global_cosign_pubkeys.iter().zip(global_signer_part_sigs) {
716			if sigs.as_ref().len() != self.nb_internal_nodes() {
717				// NB if the called didn't order part sigs identically as global_cosign_pubkeys,
718				// this pubkey indication is actually wrong..
719				return Err(CosignSignatureError::MissingSignature { pk: *pk });
720			}
721		}
722
723		let max_level = match self.tree.root().is_leaf() {
724			true => 0,
725			false => self.tree.root().internal_level(),
726		};
727		self.tree.iter_internal().map(|node| {
728			let mut cosign_pks = Vec::with_capacity(max_level.saturating_add(1));
729			let mut part_sigs = Vec::with_capacity(max_level.saturating_add(1));
730			for leaf in node.leaves() {
731				if let Some(cosign_pk) = self.spec.vtxos[leaf].cosign_pubkey {
732					let part_sig = leaf_part_sigs.get_mut(&cosign_pk)
733						.ok_or(CosignSignatureError::missing_sig(cosign_pk))?
734						.pop_front()
735						.ok_or(CosignSignatureError::missing_sig(cosign_pk))?;
736					cosign_pks.push(cosign_pk);
737					part_sigs.push(part_sig);
738				}
739			}
740			// add global signers
741			cosign_pks.extend(&self.spec.global_cosign_pubkeys);
742			for sigs in global_signer_part_sigs {
743				part_sigs.push(sigs.as_ref().get(node.internal_idx()).expect("checked before"));
744			}
745
746			let agg_pk = self.cosign_agg_pks[node.idx()].x_only_public_key().0;
747			let taproot = self.spec.internal_taproot(agg_pk);
748			let agg_nonce = *cosign_agg_nonces.get(node.internal_idx())
749				.ok_or(CosignSignatureError::NotEnoughNonces)?;
750			let sighash = self.internal_sighashes[node.internal_idx()].to_byte_array();
751			let tweak = taproot.tap_tweak().to_byte_array();
752			Ok(musig::combine_partial_signatures(
753				cosign_pks, agg_nonce, sighash, Some(tweak), &part_sigs,
754			))
755		}).collect()
756	}
757
758	/// Verify the signatures of all the internal node txs.
759	///
760	/// Signatures expected for all internal nodes, ordered from leaves to root.
761	pub fn verify_cosign_sigs(
762		&self,
763		signatures: &[schnorr::Signature],
764	) -> Result<(), XOnlyPublicKey> {
765		for node in self.tree.iter_internal() {
766			let sighash = self.internal_sighashes[node.internal_idx()];
767			let agg_pk = &self.cosign_agg_pks[node.idx()].x_only_public_key().0;
768			let pk = self.spec.internal_taproot(*agg_pk).output_key().to_x_only_public_key();
769			let sig = signatures.get(node.internal_idx()).ok_or_else(|| pk)?;
770			if SECP.verify_schnorr(sig, &sighash.into(), &pk).is_err() {
771				return Err(pk);
772			}
773		}
774		Ok(())
775	}
776
777	/// Convert into a [SignedVtxoTreeSpec] by providing the signatures.
778	///
779	/// Signatures expected for all internal nodes, ordered from leaves to root.
780	pub fn into_signed_tree(
781		self,
782		signatures: Vec<schnorr::Signature>,
783	) -> SignedVtxoTreeSpec {
784		SignedVtxoTreeSpec {
785			spec: self.spec,
786			utxo: self.utxo,
787			cosign_sigs: signatures,
788		}
789	}
790}
791
792/// Error returned from cosigning a VTXO tree.
793#[derive(PartialEq, Eq, thiserror::Error)]
794pub enum CosignSignatureError {
795	#[error("missing cosign signature from pubkey {pk}")]
796	MissingSignature { pk: PublicKey },
797	#[error("invalid cosign signature from pubkey {pk}")]
798	InvalidSignature { pk: PublicKey },
799	#[error("not enough nonces")]
800	NotEnoughNonces,
801	#[error("invalid cosign signatures: {0}")]
802	Invalid(&'static str),
803}
804
805impl fmt::Debug for CosignSignatureError {
806	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
807	    fmt::Display::fmt(self, f)
808	}
809}
810
811impl CosignSignatureError {
812	fn missing_sig(cosign_pk: PublicKey) -> CosignSignatureError {
813		CosignSignatureError::MissingSignature { pk: cosign_pk }
814	}
815	fn invalid_sig(cosign_pk: PublicKey) -> CosignSignatureError {
816		CosignSignatureError::InvalidSignature { pk: cosign_pk }
817	}
818}
819
820/// All the information needed to uniquely specify a fully signed VTXO tree.
821#[derive(Debug, Clone, PartialEq)]
822pub struct SignedVtxoTreeSpec {
823	pub spec: VtxoTreeSpec,
824	pub utxo: OutPoint,
825	/// The signatures for the internal txs, from leaves to root.
826	pub cosign_sigs: Vec<schnorr::Signature>,
827}
828
829impl SignedVtxoTreeSpec {
830	/// Signatures expected for internal nodes ordered from leaves to root.
831	pub fn new(
832		spec: VtxoTreeSpec,
833		utxo: OutPoint,
834		cosign_signatures: Vec<schnorr::Signature>,
835	) -> SignedVtxoTreeSpec {
836		SignedVtxoTreeSpec { spec, utxo, cosign_sigs: cosign_signatures }
837	}
838
839	pub fn nb_leaves(&self) -> usize {
840		self.spec.nb_leaves()
841	}
842
843	/// Construct the exit branch starting from the root ending in the leaf.
844	///
845	/// Panics if `leaf_idx` is out of range.
846	///
847	/// This call is quite inefficient and if you want to make repeated calls,
848	/// it is advised to use [CachedSignedVtxoTree::exit_branch] instead.
849	pub fn exit_branch(&self, leaf_idx: usize) -> Vec<Transaction> {
850		let txs = self.all_final_txs();
851		let tree = Tree::new(self.spec.nb_leaves());
852		let mut ret = tree.iter_branch(leaf_idx)
853			.map(|n| txs[n.idx()].clone())
854			.collect::<Vec<_>>();
855		ret.reverse();
856		ret
857	}
858
859	/// Get all final txs in this tree, starting with the leaves, towards the root
860	pub fn all_final_txs(&self) -> Vec<Transaction> {
861		self.spec.final_transactions(self.utxo, &self.cosign_sigs)
862	}
863
864	pub fn into_cached_tree(self) -> CachedSignedVtxoTree {
865		CachedSignedVtxoTree {
866			txs: self.all_final_txs(),
867			spec: self,
868		}
869	}
870}
871
872/// A fully signed VTXO tree, with all the transaction cached.
873///
874/// This is useful for cheap extraction of VTXO branches.
875pub struct CachedSignedVtxoTree {
876	pub spec: SignedVtxoTreeSpec,
877	/// All signed txs in this tree, starting with the leaves, towards the root.
878	pub txs: Vec<Transaction>,
879}
880
881impl CachedSignedVtxoTree {
882	/// Construct the exit branch starting from the root ending in the leaf.
883	///
884	/// Panics if `leaf_idx` is out of range.
885	pub fn exit_branch(&self, leaf_idx: usize) -> Vec<&Transaction> {
886		let tree = Tree::new(self.spec.spec.nb_leaves());
887		let mut ret = tree.iter_branch(leaf_idx)
888			.map(|n| &self.txs[n.idx()])
889			.collect::<Vec<_>>();
890		ret.reverse();
891		ret
892	}
893
894	pub fn nb_leaves(&self) -> usize {
895		self.spec.nb_leaves()
896	}
897
898	pub fn nb_nodes(&self) -> usize {
899		Tree::nb_nodes_for_leaves(self.spec.nb_leaves())
900	}
901
902	/// Get all final txs in this tree, starting with the leaves, towards the root.
903	///
904	/// The leaf transactions are unsigned and the node transactions are signed.
905	/// This is equivalent to `unsigned_leaf_txs` chained with `signed_node_txs`
906	pub fn all_final_txs(&self) -> &[Transaction] {
907		&self.txs
908	}
909
910	/// Returns all leaf transactions
911	///
912	/// These transactions aren't signed (yet)
913	pub fn unsigned_leaf_txs(&self) -> &[Transaction] {
914		&self.txs[..self.nb_leaves()]
915	}
916
917	/// Returns all internal node transactions
918	///
919	/// These transactions are fully signed
920	pub fn internal_node_txs(&self) -> &[Transaction] {
921		&self.txs[self.nb_leaves()..]
922	}
923
924	/// Build the genesis item for the given node tx and its output idx
925	fn build_genesis_item_at<'a>(&self, tree: &Tree, node_idx: usize, output_idx: u8) -> GenesisItem {
926		debug_assert_eq!(self.nb_leaves(), tree.nb_leaves(), "tree corresponds to self");
927		debug_assert!(node_idx < tree.nb_nodes(), "Node index is in tree");
928
929		let other_outputs = self.txs.get(node_idx).expect("Each node has a tx")
930			.output.iter().enumerate()
931			.filter(|(i, _)| *i != output_idx as usize) // Exclude this output
932			.filter(|(_, out)| !out.is_p2a_fee_anchor())    // Exclude the fee-anchor
933			.map(|(_, out)| out)
934			.cloned()
935			.collect();
936
937		let node = tree.node_at(node_idx);
938
939		let transition = if node.is_leaf() {
940			debug_assert_eq!(output_idx, 0, "Leafs have a single output");
941			let req = self.spec.spec.vtxos.get(node_idx).expect("Every leaf has a spec");
942			GenesisTransition::new_hash_locked_cosigned(
943				req.vtxo.policy.user_pubkey(),
944				None,
945				MaybePreimage::Hash(req.unlock_hash),
946			)
947		} else {
948			let pubkeys = node.leaves()
949				.filter_map(|i| self.spec.spec.vtxos[i].cosign_pubkey)
950				.chain(self.spec.spec.global_cosign_pubkeys.iter().copied())
951				.collect();
952
953			let sig = self.spec.cosign_sigs.get(node.internal_idx())
954				.expect("enough sigs for all nodes");
955
956			GenesisTransition::new_cosigned(pubkeys, Some(*sig))
957		};
958
959		let fee_amount = Amount::ZERO;
960		GenesisItem {transition, output_idx, other_outputs, fee_amount }
961	}
962
963	/// Construct the server vtxo at the given node index.
964	///
965	/// The index corresponds to the prevout of self.txs[index]
966	///
967	/// Panics if `node_idx` is out of range.
968	fn build_internal_vtxo(&self, node_idx: usize) -> ServerVtxo<Full> {
969		let tree = Tree::new(self.spec.spec.nb_leaves());
970		assert!(node_idx < tree.nb_nodes(), "node_idx out of range");
971
972		let mut genesis = tree.iter_branch_with_output(node_idx)
973			.map(|(idx, child_idx)| self.build_genesis_item_at(&tree, idx, u8::try_from(child_idx).expect("tree child index fits in u8")))
974			.collect::<Vec<_>>();
975		genesis.reverse();
976
977		let node = tree.node_at(node_idx);
978		let spec = &self.spec.spec;
979		let (point, amount) = match tree.parent_idx_of_with_sibling_idx(node_idx) {
980			None => (self.spec.utxo, spec.total_required_value()),
981			Some((parent_idx, child_idx)) => {
982				let parent_tx = self.txs.get(parent_idx).expect("parent tx exists");
983				let point = OutPoint::new(parent_tx.compute_txid(), u32::try_from(child_idx).expect("tree child index fits in u32"));
984				(point, parent_tx.output[child_idx].value)
985			}
986		};
987
988		let policy = if node.is_leaf() {
989			let req = spec.vtxos.get(node_idx).expect("one vtxo request for every leaf");
990			ServerVtxoPolicy::new_hark_leaf(req.vtxo.policy.user_pubkey(), req.unlock_hash)
991		} else {
992			let agg_pk = musig::combine_keys(
993				node.leaves().filter_map(|i| self.spec.spec.vtxos[i].cosign_pubkey)
994					.chain(self.spec.spec.global_cosign_pubkeys.iter().copied())
995			);
996			ServerVtxoPolicy::new_expiry(agg_pk.x_only_public_key().0)
997		};
998
999		ServerVtxo {
1000			policy,
1001			amount,
1002			expiry_height: self.spec.spec.expiry_height,
1003			server_pubkey: self.spec.spec.server_pubkey,
1004			exit_delta: self.spec.spec.exit_delta,
1005			anchor_point: self.spec.utxo,
1006			genesis: Full { items: genesis },
1007			point,
1008		}
1009	}
1010
1011	/// Construct the VTXO at the given leaf index.
1012	///
1013	/// Panics if `leaf_idx` is out of range.
1014	pub fn build_vtxo(&self, leaf_idx: usize) -> Vtxo<Full> {
1015		let req = self.spec.spec.vtxos.get(leaf_idx).expect("index is not a leaf");
1016
1017		let genesis = {
1018			let tree = Tree::new(self.spec.spec.nb_leaves());
1019			let leaf = self.build_genesis_item_at(&tree, leaf_idx, 0);
1020			let internal = tree.iter_branch_with_output(leaf_idx)
1021				.map(|(node_idx, child_idx)| {
1022					self.build_genesis_item_at(&tree, node_idx, u8::try_from(child_idx).expect("tree child index fits in u8"))
1023				});
1024
1025			let mut genesis = [leaf].into_iter().chain(internal).collect::<Vec<_>>();
1026			genesis.reverse();
1027			genesis
1028		};
1029
1030		Vtxo {
1031			amount: req.vtxo.amount,
1032			expiry_height: self.spec.spec.expiry_height,
1033			server_pubkey: self.spec.spec.server_pubkey,
1034			exit_delta: self.spec.spec.exit_delta,
1035			anchor_point: self.spec.utxo,
1036			genesis: Full { items: genesis },
1037			policy: req.vtxo.policy.clone(),
1038			point: {
1039				let leaf_tx = self.txs.get(leaf_idx).expect("leaf idx exists");
1040				OutPoint::new(leaf_tx.compute_txid(), 0)
1041			},
1042		}
1043	}
1044
1045	/// Construct all internal ServerVtxos, each paired with the txid
1046	/// of the transaction that spends it.
1047	pub fn internal_vtxos(&self) -> impl Iterator<Item = (ServerVtxo<Full>, Txid)> + '_ {
1048		(0..self.nb_nodes()).map(|idx| {
1049			let vtxo = self.build_internal_vtxo(idx);
1050			let spending_txid = self.txs[idx].compute_txid();
1051			(vtxo, spending_txid)
1052		})
1053	}
1054
1055	/// Construct all individual vtxos from this round.
1056	pub fn output_vtxos(&self) -> impl Iterator<Item = Vtxo<Full>> + ExactSizeIterator + '_ {
1057		(0..self.nb_leaves()).map(|idx| self.build_vtxo(idx))
1058	}
1059
1060	pub fn spend_info(&self) -> impl Iterator<Item = (VtxoId, Txid)> + '_ {
1061		self.internal_vtxos()
1062			.map(|(vtxo, spending_txid)| (vtxo.id(), spending_txid))
1063	}
1064}
1065
1066/// Calculate the scriptspend sighash of a hArk leaf transaction
1067pub fn hashlocked_leaf_sighash(
1068	leaf_tx: &Transaction,
1069	user_pubkey: PublicKey,
1070	server_pubkey: PublicKey,
1071	unlock_hash: UnlockHash,
1072	prev_txout: &TxOut,
1073) -> TapSighash {
1074	let agg_pk = musig::combine_keys([user_pubkey, server_pubkey])
1075		.x_only_public_key().0;
1076	let clause = unlock_clause(agg_pk, unlock_hash);
1077	let leaf_hash = TapLeafHash::from_script(&clause, bitcoin::taproot::LeafVersion::TapScript);
1078	let mut shc = SighashCache::new(leaf_tx);
1079	shc.taproot_script_spend_signature_hash(
1080		0, // input idx is always 0
1081		&sighash::Prevouts::All(&[prev_txout]),
1082		leaf_hash,
1083		TapSighashType::Default,
1084	).expect("sighash error")
1085}
1086
1087/// Calculate the scriptspend sighash of a hArk leaf transaction
1088pub fn hashlocked_leaf_sighash_v0(
1089	leaf_tx: &Transaction,
1090	user_pubkey: PublicKey,
1091	server_pubkey: PublicKey,
1092	unlock_hash: UnlockHash,
1093	prev_txout: &TxOut,
1094) -> TapSighash {
1095	let agg_pk = musig::combine_keys([user_pubkey, server_pubkey])
1096		.x_only_public_key().0;
1097	let clause = unlock_clause_v0(agg_pk, unlock_hash);
1098	let leaf_hash = TapLeafHash::from_script(&clause, bitcoin::taproot::LeafVersion::TapScript);
1099	let mut shc = SighashCache::new(leaf_tx);
1100	shc.taproot_script_spend_signature_hash(
1101		0, // input idx is always 0
1102		&sighash::Prevouts::All(&[prev_txout]),
1103		leaf_hash,
1104		TapSighashType::Default,
1105	).expect("sighash error")
1106}
1107
1108/// Create the leaf tx sighash from an existing VTXO
1109///
1110/// This is used after the interactive part of the round is finished by
1111/// both user and server to cosign the leaf input script-spend before
1112/// exchanging forfeit signatures for the unlock preimage.
1113fn hashlocked_leaf_sighash_from_vtxo(
1114	vtxo: &Vtxo<Full>,
1115	chain_anchor: &Transaction,
1116) -> TapSighash {
1117	assert_eq!(chain_anchor.compute_txid(), vtxo.chain_anchor().txid);
1118
1119	// we need the penultimate TxOut and last tx
1120	let mut preleaf_txout = chain_anchor.output[vtxo.chain_anchor().vout as usize].clone();
1121	let mut leaf_tx = None;
1122	let mut peekable_iter = vtxo.transactions().peekable();
1123	while let Some(item) = peekable_iter.next() {
1124		// we don't know when we're penultimate, update txout
1125		// each time except last
1126		if peekable_iter.peek().is_some() {
1127			preleaf_txout = item.tx.output[item.output_idx].clone();
1128		}
1129
1130		// then only take the last tx
1131		if peekable_iter.peek().is_none() {
1132			leaf_tx = Some(item.tx);
1133		}
1134	}
1135	let leaf_tx = leaf_tx.expect("at least one tx");
1136
1137	let last_genesis = vtxo.genesis.items.last().expect("at least one genesis item");
1138	match &last_genesis.transition {
1139		GenesisTransition::HashLockedCosigned(inner) => {
1140			debug_assert_eq!(inner.user_pubkey, vtxo.user_pubkey());
1141			hashlocked_leaf_sighash(
1142				&leaf_tx, inner.user_pubkey, vtxo.server_pubkey(), inner.unlock.hash(),
1143				&preleaf_txout,
1144			)
1145		},
1146		GenesisTransition::HashLockedCosigned_v0(inner) => {
1147			debug_assert_eq!(inner.user_pubkey, vtxo.user_pubkey());
1148			hashlocked_leaf_sighash_v0(
1149				&leaf_tx, inner.user_pubkey, vtxo.server_pubkey(), inner.unlock.hash(),
1150				&preleaf_txout,
1151			)
1152		},
1153		_ => panic!("VTXO is not a HashLockedCosigned VTXO"),
1154	}
1155}
1156
1157#[derive(Debug)]
1158pub struct LeafVtxoCosignRequest {
1159	pub vtxo_id: VtxoId,
1160	pub pub_nonce: musig::PublicNonce,
1161}
1162
1163pub struct LeafVtxoCosignContext<'a> {
1164	key: &'a Keypair,
1165	pub_nonce: musig::PublicNonce,
1166	sec_nonce: musig::SecretNonce,
1167	sighash: TapSighash,
1168}
1169
1170impl<'a> LeafVtxoCosignContext<'a> {
1171	/// Create a new [LeafVtxoCosignRequest] for the given VTXO
1172	///
1173	/// Panics if the chain_anchor tx is incorrect or if this VTXO is not a
1174	/// hArk leaf VTXO.
1175	pub fn new(
1176		vtxo: &Vtxo<Full>,
1177		chain_anchor: &Transaction,
1178		key: &'a Keypair,
1179	) -> (Self, LeafVtxoCosignRequest) {
1180		let sighash = hashlocked_leaf_sighash_from_vtxo(&vtxo, chain_anchor);
1181		let (sec_nonce, pub_nonce) = musig::nonce_pair_with_msg(key, &sighash.to_byte_array());
1182		let vtxo_id = vtxo.id();
1183		let req = LeafVtxoCosignRequest { vtxo_id, pub_nonce };
1184		let ret = Self { key, pub_nonce, sec_nonce, sighash };
1185		(ret, req)
1186	}
1187
1188	/// Finalize the VTXO using the response from the server
1189	pub fn finalize(
1190		self,
1191		vtxo: &mut Vtxo<Full>,
1192		response: LeafVtxoCosignResponse,
1193	) -> bool {
1194		let agg_nonce = musig::nonce_agg(&[&self.pub_nonce, &response.public_nonce]);
1195		let (_part_sig, final_sig) = musig::partial_sign(
1196			[vtxo.user_pubkey(), vtxo.server_pubkey()],
1197			agg_nonce,
1198			self.key,
1199			self.sec_nonce,
1200			self.sighash.to_byte_array(),
1201			None,
1202			Some(&[&response.partial_signature]),
1203		);
1204		let final_sig = final_sig.expect("has other sigs");
1205
1206		let pubkey = musig::combine_keys([vtxo.user_pubkey(), vtxo.server_pubkey()])
1207			.x_only_public_key().0;
1208		debug_assert_eq!(pubkey, leaf_cosign_taproot(
1209			vtxo.user_pubkey(),
1210			vtxo.server_pubkey(),
1211			vtxo.expiry_height(),
1212			vtxo.unlock_hash().expect("checked is hark vtxo"),
1213		).internal_key());
1214		if SECP.verify_schnorr(&final_sig, &self.sighash.into(), &pubkey).is_err() {
1215			return false;
1216		}
1217
1218		vtxo.provide_unlock_signature(final_sig)
1219	}
1220}
1221
1222#[derive(Debug)]
1223pub struct LeafVtxoCosignResponse {
1224	pub public_nonce: musig::PublicNonce,
1225	pub partial_signature: musig::PartialSignature,
1226}
1227
1228impl LeafVtxoCosignResponse {
1229	/// Cosign a [LeafVtxoCosignRequest]
1230	pub fn new_cosign(
1231		request: &LeafVtxoCosignRequest,
1232		vtxo: &Vtxo<Full>,
1233		chain_anchor: &Transaction,
1234		server_key: &Keypair,
1235	) -> Self {
1236		debug_assert_eq!(server_key.public_key(), vtxo.server_pubkey());
1237		let sighash = hashlocked_leaf_sighash_from_vtxo(&vtxo, chain_anchor);
1238		let (public_nonce, partial_signature) = musig::deterministic_partial_sign(
1239			server_key,
1240			[vtxo.user_pubkey()],
1241			&[&request.pub_nonce],
1242			sighash.to_byte_array(),
1243			None,
1244		);
1245		Self { public_nonce, partial_signature }
1246	}
1247}
1248
1249pub mod builder {
1250	//! This module allows a single party to construct his own signed
1251	//! VTXO tree, to then request signatures from the server.
1252	//!
1253	//! This is not used for rounds, where the tree is created with
1254	//! many users at once.
1255
1256	use std::collections::HashMap;
1257	use std::marker::PhantomData;
1258
1259	use bitcoin::{Amount, OutPoint, ScriptBuf, TxOut};
1260	use bitcoin::hashes::{sha256, Hash};
1261	use bitcoin::secp256k1::{Keypair, PublicKey};
1262	use bitcoin_ext::{BlockDelta, BlockHeight};
1263
1264	use crate::tree::signed::{UnlockHash, UnlockPreimage, VtxoLeafSpec};
1265	use crate::{musig, VtxoRequest};
1266	use crate::error::IncorrectSigningKeyError;
1267
1268	use super::{CosignSignatureError, SignedVtxoTreeSpec, UnsignedVtxoTree, VtxoTreeSpec};
1269
1270	pub mod state {
1271		mod sealed {
1272			/// Just a trait to seal the BuilderState trait
1273			pub trait Sealed {}
1274			impl Sealed for super::Preparing {}
1275			impl Sealed for super::CanGenerateNonces {}
1276			impl Sealed for super::ServerCanCosign {}
1277			impl Sealed for super::CanFinish {}
1278		}
1279
1280		/// A marker trait used as a generic for [super::SignedTreeBuilder]
1281		pub trait BuilderState: sealed::Sealed {}
1282
1283		/// The user is preparing the funding tx
1284		pub struct Preparing;
1285		impl BuilderState for Preparing {}
1286
1287		/// The UTXO that will be used to fund the tree is known, so the
1288		/// user's signing nonces can be generated
1289		pub struct CanGenerateNonces;
1290		impl BuilderState for CanGenerateNonces {}
1291
1292		/// All the information for the server to cosign the tree is known
1293		pub struct ServerCanCosign;
1294		impl BuilderState for ServerCanCosign {}
1295
1296		/// The user is ready to build the tree as soon as it has
1297		/// a cosign response from the server
1298		pub struct CanFinish;
1299		impl BuilderState for CanFinish {}
1300
1301		/// Trait to capture all states that have sufficient information
1302		/// for either party to create signatures
1303		pub trait CanSign: BuilderState {}
1304		impl CanSign for ServerCanCosign {}
1305		impl CanSign for CanFinish {}
1306	}
1307
1308	/// Just an enum to hold either a tree spec or an unsigned tree
1309	enum BuilderTree {
1310		Spec(VtxoTreeSpec),
1311		Unsigned(UnsignedVtxoTree),
1312	}
1313
1314	impl BuilderTree {
1315		fn unsigned_tree(&self) -> Option<&UnsignedVtxoTree> {
1316			match self {
1317				BuilderTree::Spec(_) => None,
1318				BuilderTree::Unsigned(t) => Some(t),
1319			}
1320		}
1321
1322		fn into_unsigned_tree(self) -> Option<UnsignedVtxoTree> {
1323			match self {
1324				BuilderTree::Spec(_) => None,
1325				BuilderTree::Unsigned(t) => Some(t),
1326			}
1327		}
1328	}
1329
1330	/// A builder for a single party to construct a VTXO tree
1331	///
1332	/// For more information, see the module documentation.
1333	pub struct SignedTreeBuilder<S: state::BuilderState> {
1334		pub expiry_height: BlockHeight,
1335		pub server_pubkey: PublicKey,
1336		pub exit_delta: BlockDelta,
1337		/// The cosign pubkey used to cosign all nodes in the tree
1338		pub cosign_pubkey: PublicKey,
1339		/// The unlock hash used to unlock all VTXOs in the tree
1340		pub unlock_preimage: UnlockPreimage,
1341
1342		tree: BuilderTree,
1343
1344		/// users public nonces, leaves to the root
1345		user_pub_nonces: Vec<musig::PublicNonce>,
1346		/// users secret nonces, leaves to the root
1347		/// this field is empty on the server side
1348		user_sec_nonces: Option<Vec<musig::SecretNonce>>,
1349		_state: PhantomData<S>,
1350	}
1351
1352	impl<T: state::BuilderState> SignedTreeBuilder<T> {
1353		fn tree_spec(&self) -> &VtxoTreeSpec {
1354			match self.tree {
1355				BuilderTree::Spec(ref s) => s,
1356				BuilderTree::Unsigned(ref t) => &t.spec,
1357			}
1358		}
1359
1360		/// The total value required for the tree to be funded
1361		pub fn total_required_value(&self) -> Amount {
1362			self.tree_spec().total_required_value()
1363		}
1364
1365		/// The scriptPubkey to send the board funds to
1366		pub fn funding_script_pubkey(&self) -> ScriptBuf {
1367			self.tree_spec().funding_tx_script_pubkey()
1368		}
1369
1370		/// The TxOut to create in the funding tx
1371		pub fn funding_txout(&self) -> TxOut {
1372			let spec = self.tree_spec();
1373			TxOut {
1374				value: spec.total_required_value(),
1375				script_pubkey: spec.funding_tx_script_pubkey(),
1376			}
1377		}
1378	}
1379
1380	impl<T: state::CanSign> SignedTreeBuilder<T> {
1381		/// Get the user's public nonces
1382		pub fn user_pub_nonces(&self) -> &[musig::PublicNonce] {
1383			&self.user_pub_nonces
1384		}
1385	}
1386
1387	#[derive(Debug, thiserror::Error)]
1388	#[error("signed VTXO tree builder error: {0}")]
1389	pub struct SignedTreeBuilderError(&'static str);
1390
1391	impl SignedTreeBuilder<state::Preparing> {
1392		/// Construct the spec to be used in [SignedTreeBuilder]
1393		pub fn construct_tree_spec(
1394			vtxos: impl IntoIterator<Item = VtxoRequest>,
1395			cosign_pubkey: PublicKey,
1396			unlock_hash: UnlockHash,
1397			expiry_height: BlockHeight,
1398			server_pubkey: PublicKey,
1399			server_cosign_pubkey: PublicKey,
1400			exit_delta: BlockDelta,
1401		) -> Result<VtxoTreeSpec, SignedTreeBuilderError> {
1402			let reqs = vtxos.into_iter()
1403				.map(|vtxo| VtxoLeafSpec {
1404					vtxo: vtxo,
1405					cosign_pubkey: None,
1406					unlock_hash: unlock_hash,
1407				})
1408				.collect::<Vec<_>>();
1409			if reqs.len() < 2 {
1410				return Err(SignedTreeBuilderError("need to have at least 2 VTXOs in tree"));
1411			}
1412			Ok(VtxoTreeSpec::new(
1413				reqs,
1414				server_pubkey,
1415				expiry_height,
1416				exit_delta,
1417				// NB we place server last because then it looks closer like
1418				// a regular user-signed tree which Vtxo::validate relies on
1419				vec![cosign_pubkey, server_cosign_pubkey],
1420			))
1421		}
1422
1423		/// Create a new [SignedTreeBuilder]
1424		pub fn new(
1425			vtxos: impl IntoIterator<Item = VtxoRequest>,
1426			cosign_pubkey: PublicKey,
1427			unlock_preimage: UnlockPreimage,
1428			expiry_height: BlockHeight,
1429			server_pubkey: PublicKey,
1430			server_cosign_pubkey: PublicKey,
1431			exit_delta: BlockDelta,
1432		) -> Result<SignedTreeBuilder<state::Preparing>, SignedTreeBuilderError> {
1433			let tree = Self::construct_tree_spec(
1434				vtxos,
1435				cosign_pubkey,
1436				sha256::Hash::hash(&unlock_preimage),
1437				expiry_height,
1438				server_pubkey,
1439				server_cosign_pubkey,
1440				exit_delta,
1441			)?;
1442
1443			Ok(SignedTreeBuilder {
1444				expiry_height, server_pubkey, exit_delta, cosign_pubkey, unlock_preimage,
1445				tree: BuilderTree::Spec(tree),
1446				user_pub_nonces: Vec::new(),
1447				user_sec_nonces: None,
1448				_state: PhantomData,
1449			})
1450		}
1451
1452		/// Set the utxo from which the tree will be created
1453		pub fn set_utxo(self, utxo: OutPoint) -> SignedTreeBuilder<state::CanGenerateNonces> {
1454			let unsigned_tree = match self.tree {
1455				BuilderTree::Spec(s) => s.into_unsigned_tree(utxo),
1456				BuilderTree::Unsigned(t) => t, // should not happen
1457			};
1458			SignedTreeBuilder {
1459				tree: BuilderTree::Unsigned(unsigned_tree),
1460
1461				expiry_height: self.expiry_height,
1462				server_pubkey: self.server_pubkey,
1463				exit_delta: self.exit_delta,
1464				cosign_pubkey: self.cosign_pubkey,
1465				unlock_preimage: self.unlock_preimage,
1466				user_pub_nonces: self.user_pub_nonces,
1467				user_sec_nonces: self.user_sec_nonces,
1468				_state: PhantomData,
1469			}
1470		}
1471	}
1472
1473	impl SignedTreeBuilder<state::CanGenerateNonces> {
1474		/// Generate user nonces
1475		pub fn generate_user_nonces(
1476			self,
1477			cosign_key: &Keypair,
1478		) -> SignedTreeBuilder<state::CanFinish> {
1479			let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1480
1481			let mut cosign_sec_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1482			let mut cosign_pub_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1483			for sh in &unsigned_tree.internal_sighashes {
1484				let pair = musig::nonce_pair_with_msg(&cosign_key, &sh.to_byte_array());
1485				cosign_sec_nonces.push(pair.0);
1486				cosign_pub_nonces.push(pair.1);
1487			}
1488
1489			SignedTreeBuilder {
1490				user_pub_nonces: cosign_pub_nonces,
1491				user_sec_nonces: Some(cosign_sec_nonces),
1492
1493				expiry_height: self.expiry_height,
1494				server_pubkey: self.server_pubkey,
1495				exit_delta: self.exit_delta,
1496				cosign_pubkey: self.cosign_pubkey,
1497				unlock_preimage: self.unlock_preimage,
1498				tree: self.tree,
1499				_state: PhantomData,
1500			}
1501		}
1502	}
1503
1504	/// Holds the cosignature information of the server
1505	#[derive(Debug, Clone)]
1506	pub struct SignedTreeCosignResponse {
1507		pub pub_nonces: Vec<musig::PublicNonce>,
1508		pub partial_signatures: Vec<musig::PartialSignature>,
1509	}
1510
1511	impl SignedTreeBuilder<state::ServerCanCosign> {
1512		/// Create a new [SignedTreeBuilder] for the server to cosign
1513		pub fn new_for_cosign(
1514			vtxos: impl IntoIterator<Item = VtxoRequest>,
1515			cosign_pubkey: PublicKey,
1516			unlock_preimage: UnlockPreimage,
1517			expiry_height: BlockHeight,
1518			server_pubkey: PublicKey,
1519			server_cosign_pubkey: PublicKey,
1520			exit_delta: BlockDelta,
1521			utxo: OutPoint,
1522			user_pub_nonces: Vec<musig::PublicNonce>,
1523		) -> Result<SignedTreeBuilder<state::ServerCanCosign>, SignedTreeBuilderError> {
1524			let unsigned_tree = SignedTreeBuilder::construct_tree_spec(
1525				vtxos,
1526				cosign_pubkey,
1527				sha256::Hash::hash(&unlock_preimage),
1528				expiry_height,
1529				server_pubkey,
1530				server_cosign_pubkey,
1531				exit_delta,
1532			)?.into_unsigned_tree(utxo);
1533
1534			Ok(SignedTreeBuilder {
1535				expiry_height,
1536				server_pubkey,
1537				exit_delta,
1538				cosign_pubkey,
1539				unlock_preimage,
1540				user_pub_nonces,
1541				tree: BuilderTree::Unsigned(unsigned_tree),
1542				user_sec_nonces: None,
1543				_state: PhantomData,
1544			})
1545		}
1546
1547		/// The server cosigns the tree nodes
1548		pub fn server_cosign(&self, server_cosign_key: &Keypair) -> SignedTreeCosignResponse {
1549			let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1550
1551			let mut sec_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1552			let mut pub_nonces = Vec::with_capacity(unsigned_tree.internal_sighashes.len());
1553			for sh in &unsigned_tree.internal_sighashes {
1554				let pair = musig::nonce_pair_with_msg(&server_cosign_key, &sh.to_byte_array());
1555				sec_nonces.push(pair.0);
1556				pub_nonces.push(pair.1);
1557			}
1558
1559			let agg_nonces = self.user_pub_nonces().iter().zip(&pub_nonces)
1560				.map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1561				.collect::<Vec<_>>();
1562
1563			let sigs = unsigned_tree.cosign_tree(&agg_nonces, &server_cosign_key, sec_nonces);
1564
1565			SignedTreeCosignResponse {
1566				pub_nonces,
1567				partial_signatures: sigs,
1568			}
1569		}
1570	}
1571
1572	impl SignedTreeBuilder<state::CanFinish> {
1573		/// Validate the server's partial signatures
1574		pub fn verify_cosign_response(
1575			&self,
1576			server_cosign: &SignedTreeCosignResponse,
1577		) -> Result<(), CosignSignatureError> {
1578			let unsigned_tree = self.tree.unsigned_tree().expect("state invariant");
1579
1580			let agg_nonces = self.user_pub_nonces().iter()
1581				.zip(&server_cosign.pub_nonces)
1582				.map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1583				.collect::<Vec<_>>();
1584
1585			unsigned_tree.verify_global_cosign_partial_sigs(
1586				*unsigned_tree.spec.global_cosign_pubkeys.get(1).expect("state invariant"),
1587				&agg_nonces,
1588				&server_cosign.pub_nonces,
1589				&server_cosign.partial_signatures,
1590			)
1591		}
1592
1593		pub fn build_tree(
1594			self,
1595			server_cosign: &SignedTreeCosignResponse,
1596			cosign_key: &Keypair,
1597		) -> Result<SignedVtxoTreeSpec, IncorrectSigningKeyError> {
1598			if cosign_key.public_key() != self.cosign_pubkey {
1599				return Err(IncorrectSigningKeyError {
1600					required: Some(self.cosign_pubkey),
1601					provided: cosign_key.public_key(),
1602				});
1603			}
1604
1605			let agg_nonces = self.user_pub_nonces().iter().zip(&server_cosign.pub_nonces)
1606				.map(|(u, s)| musig::AggregatedNonce::new(&[u, s]))
1607				.collect::<Vec<_>>();
1608
1609			let unsigned_tree = self.tree.into_unsigned_tree().expect("state invariant");
1610			let sec_nonces = self.user_sec_nonces.expect("state invariant");
1611			let partial_sigs = unsigned_tree.cosign_tree(&agg_nonces, cosign_key, sec_nonces);
1612
1613			debug_assert!(unsigned_tree.verify_global_cosign_partial_sigs(
1614				self.cosign_pubkey,
1615				&agg_nonces,
1616				&self.user_pub_nonces,
1617				&partial_sigs,
1618			).is_ok(), "produced invalid partial signatures");
1619
1620			let sigs = unsigned_tree.combine_partial_signatures(
1621				&agg_nonces,
1622				&HashMap::new(),
1623				&[&server_cosign.partial_signatures, &partial_sigs],
1624			).expect("should work with correct cosign signatures");
1625
1626			Ok(unsigned_tree.into_signed_tree(sigs))
1627		}
1628	}
1629}
1630
1631/// The serialization version of [VtxoTreeSpec].
1632const VTXO_TREE_SPEC_VERSION: u8 = 0x02;
1633
1634impl ProtocolEncoding for VtxoTreeSpec {
1635	fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
1636		w.emit_u8(VTXO_TREE_SPEC_VERSION)?;
1637		w.emit_u32(self.expiry_height)?;
1638		self.server_pubkey.encode(w)?;
1639		w.emit_u16(self.exit_delta)?;
1640		LengthPrefixedVector::new(&self.global_cosign_pubkeys).encode(w)?;
1641		LengthPrefixedVector::new(&self.vtxos).encode(w)?;
1642		Ok(())
1643	}
1644
1645	fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, ProtocolDecodingError> {
1646		let version = r.read_u8()?;
1647
1648		if version != VTXO_TREE_SPEC_VERSION {
1649			return Err(ProtocolDecodingError::invalid(format_args!(
1650				"invalid VtxoTreeSpec encoding version byte: {version:#x}",
1651			)));
1652		}
1653
1654		let expiry_height = check_block_height(r.read_u32()?)
1655			.map_err(|e| ProtocolDecodingError::invalid_err(e, "expiry_height"))?;
1656		let server_pubkey = PublicKey::decode(r)?;
1657		let exit_delta = check_block_delta(r.read_u16()?)
1658			.map_err(|e| ProtocolDecodingError::invalid_err(e, "exit_delta"))?;
1659		let global_cosign_pubkeys = LengthPrefixedVector::decode(r)?.into_inner();
1660		let vtxos = LengthPrefixedVector::decode(r)?.into_inner();
1661		if vtxos.is_empty() {
1662			return Err(ProtocolDecodingError::invalid(
1663				"vtxo tree spec must have at least one leaf",
1664			));
1665		}
1666		Ok(VtxoTreeSpec { vtxos, expiry_height, server_pubkey, exit_delta, global_cosign_pubkeys })
1667	}
1668}
1669
1670/// The serialization version of [SignedVtxoTreeSpec].
1671const SIGNED_VTXO_TREE_SPEC_VERSION: u8 = 0x02;
1672
1673/// The serialization version of [SignedVtxoTreeSpec] with u32 as signature count
1674const SIGNED_VTXO_TREE_SPEC_VERSION_U32_SIZE: u8 = 0x01;
1675
1676impl ProtocolEncoding for SignedVtxoTreeSpec {
1677	fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
1678		w.emit_u8(SIGNED_VTXO_TREE_SPEC_VERSION)?;
1679		self.spec.encode(w)?;
1680		self.utxo.encode(w)?;
1681		LengthPrefixedVector::new(&self.cosign_sigs).encode(w)?;
1682		Ok(())
1683	}
1684
1685	fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, ProtocolDecodingError> {
1686		let version = r.read_u8()?;
1687		if version != SIGNED_VTXO_TREE_SPEC_VERSION
1688			&& version != SIGNED_VTXO_TREE_SPEC_VERSION_U32_SIZE
1689		{
1690			return Err(ProtocolDecodingError::invalid(format_args!(
1691				"invalid SignedVtxoTreeSpec encoding version byte: {version:#x}",
1692			)));
1693		}
1694		let spec = VtxoTreeSpec::decode(r)?;
1695		let utxo = OutPoint::decode(r)?;
1696		let cosign_sigs = if version == SIGNED_VTXO_TREE_SPEC_VERSION_U32_SIZE {
1697			let nb_cosign_sigs = r.read_u32()?;
1698			OversizedVectorError::check::<schnorr::Signature>(nb_cosign_sigs as usize)?;
1699			let mut cosign_sigs = Vec::with_capacity(nb_cosign_sigs as usize);
1700			for _ in 0..nb_cosign_sigs {
1701				cosign_sigs.push(schnorr::Signature::decode(r)?);
1702			}
1703			cosign_sigs
1704		} else {
1705			LengthPrefixedVector::decode(r)?.into_inner()
1706		};
1707		Ok(SignedVtxoTreeSpec { spec, utxo, cosign_sigs })
1708	}
1709}
1710
1711
1712#[cfg(test)]
1713mod test {
1714	use std::iter;
1715	use std::collections::HashMap;
1716	use std::str::FromStr;
1717
1718	use bitcoin::hashes::{siphash24, sha256, Hash, HashEngine};
1719	use bitcoin::key::rand::Rng;
1720	use bitcoin::secp256k1::{self, rand, Keypair};
1721	use bitcoin::{absolute, transaction};
1722	use rand::SeedableRng;
1723
1724	use crate::encode;
1725	use crate::test_util::{encoding_roundtrip, json_roundtrip};
1726	use crate::tree::signed::builder::SignedTreeBuilder;
1727	use crate::vtxo::policy::{ServerVtxoPolicy, VtxoPolicy};
1728
1729	use super::*;
1730
1731	fn test_tree_amounts(
1732		tree: &UnsignedVtxoTree,
1733		root_value: Amount,
1734	) {
1735		let map = tree.txs.iter().map(|tx| (tx.compute_txid(), tx)).collect::<HashMap<_, _>>();
1736
1737		// skip the root
1738		for (idx, tx) in tree.txs.iter().take(tree.txs.len().saturating_sub(1)).enumerate() {
1739			println!("tx #{idx}: {}", bitcoin::consensus::encode::serialize_hex(tx));
1740			let input = tx.input.iter().map(|i| {
1741				let prev = i.previous_output;
1742				map.get(&prev.txid).expect(&format!("tx {} not found", prev.txid))
1743					.output[prev.vout as usize].value
1744			}).sum::<Amount>();
1745			let output = tx.output_value();
1746			assert!(input >= output);
1747			assert_eq!(input, output);
1748		}
1749
1750		// check the root
1751		let root = tree.txs.last().unwrap();
1752		assert_eq!(root_value, root.output_value());
1753	}
1754
1755	#[test]
1756	fn vtxo_tree() {
1757		let secp = secp256k1::Secp256k1::new();
1758		let mut rand = rand::rngs::StdRng::seed_from_u64(42);
1759		let random_sig = {
1760			let key = Keypair::new(&secp, &mut rand);
1761			let sha = sha256::Hash::from_str("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap();
1762			let msg = secp256k1::Message::from_digest(sha.to_byte_array());
1763			secp.sign_schnorr(&msg, &key)
1764		};
1765
1766		let server_key = Keypair::new(&secp, &mut rand);
1767		let server_cosign_key = Keypair::new(&secp, &mut rand);
1768
1769		struct Req {
1770			key: Keypair,
1771			cosign_key: Keypair,
1772			amount: Amount,
1773			hash: sha256::Hash,
1774		}
1775		impl Req {
1776			fn to_vtxo(&self) -> VtxoLeafSpec {
1777				VtxoLeafSpec {
1778					vtxo: VtxoRequest {
1779						amount: self.amount,
1780						policy: VtxoPolicy::new_pubkey(self.key.public_key()),
1781					},
1782					cosign_pubkey: Some(self.cosign_key.public_key()),
1783					unlock_hash: self.hash,
1784				}
1785			}
1786		}
1787
1788		let nb_leaves = 27;
1789		let reqs = iter::repeat_with(|| Req {
1790			key: Keypair::new(&secp, &mut rand),
1791			cosign_key:  Keypair::new(&secp, &mut rand),
1792			amount: Amount::from_sat(100_000),
1793			hash: sha256::Hash::from_byte_array(rand.r#gen()),
1794		}).take(nb_leaves).collect::<Vec<_>>();
1795		let point = "0000000000000000000000000000000000000000000000000000000000000001:1".parse().unwrap();
1796
1797		let spec = VtxoTreeSpec::new(
1798			reqs.iter().map(|r| r.to_vtxo()).collect(),
1799			server_key.public_key(),
1800			101_000,
1801			2016,
1802			vec![server_cosign_key.public_key()],
1803		);
1804		assert_eq!(spec.nb_leaves(), nb_leaves);
1805		assert_eq!(spec.total_required_value().to_sat(), 2700000);
1806		let nb_nodes = spec.nb_nodes();
1807
1808		encoding_roundtrip(&spec);
1809
1810		let unsigned = spec.into_unsigned_tree(point);
1811
1812		test_tree_amounts(&unsigned, unsigned.spec.total_required_value());
1813
1814		let sighashes_hash = {
1815			let mut eng = siphash24::Hash::engine();
1816			unsigned.internal_sighashes.iter().for_each(|h| eng.input(&h[..]));
1817			siphash24::Hash::from_engine(eng)
1818		};
1819		assert_eq!(sighashes_hash.to_string(), "78ae911f557c0b86");
1820
1821		let signed = unsigned.into_signed_tree(vec![random_sig; nb_nodes]);
1822
1823		encoding_roundtrip(&signed);
1824
1825		#[derive(Debug, PartialEq, Serialize, Deserialize)]
1826		struct JsonSignedVtxoTreeSpec {
1827			#[serde(with = "encode::serde")]
1828			pub spec: SignedVtxoTreeSpec,
1829		}
1830
1831		json_roundtrip(&JsonSignedVtxoTreeSpec { spec: signed.clone() });
1832
1833		for l in 0..nb_leaves {
1834			let exit = signed.exit_branch(l);
1835
1836			// Assert it's a valid chain.
1837			let mut iter = exit.iter().enumerate().peekable();
1838			while let Some((i, cur)) = iter.next() {
1839				if let Some((_, next)) = iter.peek() {
1840					assert_eq!(next.input[0].previous_output.txid, cur.compute_txid(), "{}", i);
1841				}
1842			}
1843		}
1844
1845		let cached = signed.into_cached_tree();
1846		for vtxo in cached.output_vtxos() {
1847			encoding_roundtrip(&vtxo);
1848		}
1849	}
1850
1851	#[test]
1852	fn test_tree_builder() {
1853		let expiry = 100_000;
1854		let exit_delta = 24;
1855
1856		let vtxo_key = Keypair::from_str("985247fb0ef008f8043b6be28add87710d42d482433ef287235bfe041ee6cc11").unwrap();
1857		let policy = VtxoPolicy::new_pubkey(vtxo_key.public_key());
1858		let user_cosign_key = Keypair::from_str("5255d132d6ec7d4fc2a41c8f0018bb14343489ddd0344025cc60c7aa2b3fda6a").unwrap();
1859		let user_cosign_pubkey = user_cosign_key.public_key();
1860		println!("user_cosign_pubkey: {}", user_cosign_pubkey);
1861
1862		let server_key = Keypair::from_str("1fb316e653eec61de11c6b794636d230379509389215df1ceb520b65313e5426").unwrap();
1863		let server_pubkey = server_key.public_key();
1864		println!("server_pubkey: {}", server_pubkey);
1865
1866		let server_cosign_key = Keypair::from_str("52a506fbae3b725749d2486afd4761841ec685b841c2967e30f24182c4b02eed").unwrap();
1867		let server_cosign_pubkey = server_cosign_key.public_key();
1868		println!("server_cosign_pubkey: {}", server_cosign_pubkey);
1869
1870		let unlock_preimage = rand::random::<UnlockPreimage>();
1871		let unlock_hash = sha256::Hash::hash(&unlock_preimage);
1872		println!("unlock_hash: {}", unlock_hash);
1873
1874		// we test different number of nodes
1875		for nb_vtxos in [2, 3, 4, 5, 10, 50] {
1876			println!("building tree with {} vtxos", nb_vtxos);
1877			let vtxos = (0..nb_vtxos).map(|i| VtxoRequest {
1878				amount: Amount::from_sat(1000 * (i + 1)),
1879				policy: policy.clone(),
1880			}).collect::<Vec<_>>();
1881
1882			let builder = SignedTreeBuilder::new(
1883				vtxos.iter().cloned(), user_cosign_pubkey, unlock_preimage, expiry, server_pubkey,
1884				server_cosign_pubkey, exit_delta,
1885			).unwrap();
1886
1887			let funding_tx = Transaction {
1888				version: transaction::Version::TWO,
1889				lock_time: absolute::LockTime::ZERO,
1890				input: vec![],
1891				output: vec![builder.funding_txout()],
1892			};
1893			let utxo = OutPoint::new(funding_tx.compute_txid(), 0);
1894			let builder = builder.set_utxo(utxo).generate_user_nonces(&user_cosign_key);
1895			let user_pub_nonces = builder.user_pub_nonces().to_vec();
1896
1897			let cosign = {
1898				let builder = SignedTreeBuilder::new_for_cosign(
1899					vtxos.iter().cloned(), user_cosign_pubkey, unlock_preimage, expiry, server_pubkey,
1900					server_cosign_pubkey, exit_delta, utxo, user_pub_nonces,
1901				).unwrap();
1902				builder.server_cosign(&server_cosign_key)
1903			};
1904
1905			builder.verify_cosign_response(&cosign).unwrap();
1906			let tree = builder.build_tree(&cosign, &user_cosign_key).unwrap().into_cached_tree();
1907
1908			// finalize vtxos and check
1909			for mut vtxo in tree.output_vtxos() {
1910				{
1911					// check that with just the preimage, the VTXO is not valid
1912					let mut with_preimage = vtxo.clone();
1913					assert!(with_preimage.provide_unlock_preimage(unlock_preimage));
1914					assert!(with_preimage.validate(&funding_tx).is_err());
1915				}
1916
1917				let (ctx, req) = LeafVtxoCosignContext::new(&vtxo, &funding_tx, &vtxo_key);
1918				let cosign = LeafVtxoCosignResponse::new_cosign(&req, &vtxo, &funding_tx, &server_key);
1919				assert!(ctx.finalize(&mut vtxo, cosign));
1920
1921
1922				// We still miss some signatures. But the tree should be vaild
1923				assert!(!vtxo.has_all_witnesses());
1924				vtxo.validate_unsigned(&funding_tx).expect("Tree is valid if we ignore sigs");
1925				vtxo.validate(&funding_tx).expect_err("The signature check must fail");
1926
1927				assert!(vtxo.provide_unlock_preimage(unlock_preimage));
1928
1929				println!("vtxo debug: {:#?}", vtxo);
1930				println!("vtxo hex: {}", vtxo.serialize_hex());
1931				assert!(vtxo.has_all_witnesses());
1932				vtxo.validate(&funding_tx).expect("should be value");
1933
1934				vtxo.invalidate_final_sig();
1935				assert!(vtxo.has_all_witnesses(), "still has all witnesses, just an invalid one");
1936				vtxo.validate_unsigned(&funding_tx).expect("unsigned still valid");
1937				vtxo.validate(&funding_tx).expect_err("but not fully valid anymore");
1938			}
1939
1940			for (idx, (vtxo, _spending_txid)) in tree.internal_vtxos().enumerate() {
1941				// Verify transactions with consensus checks
1942				let mut prev_txout = funding_tx.output[vtxo.chain_anchor().vout as usize].clone();
1943
1944				// Verify that all vtxo.transactions() are consensus valid
1945				for item in vtxo.transactions() {
1946					crate::test_util::verify_tx(&[prev_txout], 0, &item.tx)
1947						.expect("Invalid transaction");
1948					prev_txout = item.tx.output[item.output_idx].clone();
1949				}
1950
1951				// Verify the policies
1952				if idx < nb_vtxos as usize {
1953					// All leafs have the HarkLeafPolicy
1954					matches!(vtxo.policy(), ServerVtxoPolicy::HarkLeaf(_));
1955				} else {
1956					matches!(vtxo.policy(), ServerVtxoPolicy::Expiry(_));
1957				}
1958			}
1959		}
1960	}
1961
1962	#[test]
1963	fn vtxo_leaf_spec_encoding() {
1964		let pk1: PublicKey = "020aceb65eed0ee5c512d3718e6f4bd868a7efb58ede7899ffd9bcba09555d4eb8".parse().unwrap();
1965		let pk2: PublicKey = "02e4ed0ca35c3b8a2ff675b9b23f4961964b57e130afa607e32a83d2d9a510622b".parse().unwrap();
1966		let hash = sha256::Hash::from_str("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap();
1967
1968		// Test with Some(cosign_pubkey)
1969		let spec_with_cosign = VtxoLeafSpec {
1970			vtxo: VtxoRequest {
1971				amount: Amount::from_sat(100_000),
1972				policy: VtxoPolicy::new_pubkey(pk1),
1973			},
1974			cosign_pubkey: Some(pk2),
1975			unlock_hash: hash,
1976		};
1977		encoding_roundtrip(&spec_with_cosign);
1978
1979		// Test with None cosign_pubkey
1980		let spec_without_cosign = VtxoLeafSpec {
1981			vtxo: VtxoRequest {
1982				amount: Amount::from_sat(200_000),
1983				policy: VtxoPolicy::new_pubkey(pk1),
1984			},
1985			cosign_pubkey: None,
1986			unlock_hash: hash,
1987		};
1988		encoding_roundtrip(&spec_without_cosign);
1989	}
1990
1991	#[test]
1992	fn vtxo_tree_spec_rejects_empty_vtxos() {
1993		let pk1: PublicKey = "020aceb65eed0ee5c512d3718e6f4bd868a7efb58ede7899ffd9bcba09555d4eb8"
1994			.parse().unwrap();
1995		let pk2: PublicKey = "02e4ed0ca35c3b8a2ff675b9b23f4961964b57e130afa607e32a83d2d9a510622b"
1996			.parse().unwrap();
1997		let hash = sha256::Hash::from_str(
1998			"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
1999		).unwrap();
2000
2001		// Build a valid spec and verify it roundtrips.
2002		let leaf = VtxoLeafSpec {
2003			vtxo: VtxoRequest {
2004				amount: Amount::from_sat(100_000),
2005				policy: VtxoPolicy::new_pubkey(pk1),
2006			},
2007			cosign_pubkey: Some(pk2),
2008			unlock_hash: hash,
2009		};
2010		let spec = VtxoTreeSpec::new(vec![leaf], pk1, 100_000, 2016, vec![]);
2011		encoding_roundtrip(&spec);
2012
2013		// Empty the vtxos and verify decode rejects it.
2014		let empty_spec = VtxoTreeSpec { vtxos: vec![], ..spec };
2015		let buf = empty_spec.serialize();
2016		let err = VtxoTreeSpec::deserialize(&buf)
2017			.expect_err("decoding a VtxoTreeSpec with zero vtxos must fail");
2018		let msg = err.to_string();
2019		assert!(msg.contains("at least one leaf"), "unexpected error message: {msg}");
2020	}
2021
2022	#[test]
2023	fn test_compat_u32_size_signed_spec() {
2024		//! check the backwards compatibility of parsing SignedVtxoTreeSpec with u32 as size prefix
2025		let hex = "0102888a0100035b0ef8c9bd756af433edc3129975888f6f18b8185b2afbaabc8bb3029a00cf81e0070102f8112234026e68b1e4d1565540d7b791ced1b64c5f30525cbe14f21dd7aa8c78030003c48b53afac0d2d5169cd6848f03f67a21db6f506f8b8fc2dbef2552b6a7dc111a08601000000000002c6c80e198e170ca6f8fa17810d8ee23c7c0d85c5d2febc95c3e24b1878ca733fbfd032abc31b253f5063521fd5b4c431f2cdd3fee1b4ec00a9b00f69d3b033e7000296dfec4c92e831ffe3619285646a46c545577f19dbc27c2fc0950bffb0f4a362a08601000000000003850a7d2bf22e6ba669695410a8b03c5800a0d4c2bec814b9eb21b0cddd2af5c935395dea8bd6dcac26a8a417b553b18d13027c23e8016c3466b81e70832254360002415f712d6e551b715542422b975a2ae7c635b44a1e3747d5a8674231fa10a841a08601000000000002a3d4de26a87f8bb9d5c0b9f1e3409a635b35f656575d8ad60a6a2294ac4e50b87c0cc2177dfce6432efa42ca6c04c0b774dbb3c5ca2573cd893443e10e393bfd0100000000000000000000000000000000000000000000000000000000000000010000000400000002cc1980aba21f65a51342cbaa3beb69d930eac87fe3086c45df4e642f2dd07cd19e0e4c7b9584b9e952eb4fb02e7f43364ee9bbfc667236aa166fd10a97dcbb02cc1980aba21f65a51342cbaa3beb69d930eac87fe3086c45df4e642f2dd07cd19e0e4c7b9584b9e952eb4fb02e7f43364ee9bbfc667236aa166fd10a97dcbb02cc1980aba21f65a51342cbaa3beb69d930eac87fe3086c45df4e642f2dd07cd19e0e4c7b9584b9e952eb4fb02e7f43364ee9bbfc667236aa166fd10a97dcbb02cc1980aba21f65a51342cbaa3beb69d930eac87fe3086c45df4e642f2dd07cd19e0e4c7b9584b9e952eb4fb02e7f43364ee9bbfc667236aa166fd10a97dcbb";
2026		let ret = SignedVtxoTreeSpec::deserialize_hex(hex).unwrap();
2027		assert_eq!(ret.cosign_sigs.len(), 4);
2028	}
2029}