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