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