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