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