ark/tree/
signed.rs

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