ark/vtxo/policy/
signing.rs1
2use std::borrow::Borrow;
3
4use bitcoin::hashes::Hash;
5use bitcoin::secp256k1::schnorr;
6use bitcoin::{sighash, taproot, TapSighash, Transaction, TxOut, Witness};
7
8use crate::Vtxo;
9use crate::vtxo::policy::{Policy, VtxoPolicy};
10use crate::vtxo::policy::clause::VtxoClause;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
13#[error("the vtxo has no clause signable by the provided signer")]
14pub struct CannotSignVtxoError;
15
16#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
18#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
19pub trait VtxoSigner<P: Policy = VtxoPolicy> {
20 async fn sign_keyspend<G: Sync + Send>(
22 &self,
23 vtxo: &Vtxo<G, P>,
24 sighash: TapSighash,
25 ) -> Option<schnorr::Signature>;
26
27 async fn witness(
29 &self,
30 clause: &VtxoClause,
31 control_block: &taproot::ControlBlock,
32 sighash: TapSighash,
33 ) -> Option<Witness>;
34
35 async fn can_sign<G: Sync + Send>(&self, clause: &VtxoClause, vtxo: &Vtxo<G, P>) -> bool {
37 let sighash = TapSighash::all_zeros();
39 let cb = clause.control_block(vtxo);
40 self.witness(clause, &cb, sighash).await.is_some()
41 }
42
43 async fn find_signable_clause<G: Sync + Send>(&self, vtxo: &Vtxo<G, P>) -> Option<VtxoClause> {
46 let exit_delta = vtxo.exit_delta();
47 let expiry_height = vtxo.expiry_height();
48 let server_pubkey = vtxo.server_pubkey();
49
50 let clauses = vtxo.policy().clauses(exit_delta, expiry_height, server_pubkey);
51
52 for clause in clauses {
53 if self.can_sign(&clause, vtxo).await {
54 return Some(clause);
55 }
56 }
57
58 None
59 }
60
61 async fn sign_input<G: Sync + Send>(
67 &self,
68 vtxo: &Vtxo<G, P>,
69 input_idx: usize,
70 sighash_cache: &mut sighash::SighashCache<impl Borrow<Transaction> + Send + Sync>,
71 prevouts: &sighash::Prevouts<impl Borrow<TxOut> + Send + Sync>,
72 ) -> Result<Witness, CannotSignVtxoError> {
73 let clause = self.find_signable_clause(vtxo).await
74 .ok_or(CannotSignVtxoError)?;
75 self.sign_input_with_clause(vtxo, &clause, input_idx, sighash_cache, prevouts).await
76 }
77
78 async fn sign_input_with_keyspend<G: Sync + Send>(
80 &self,
81 vtxo: &Vtxo<G, P>,
82 input_idx: usize,
83 sighash_cache: &mut sighash::SighashCache<impl Borrow<Transaction> + Send + Sync>,
84 prevouts: &sighash::Prevouts<impl Borrow<TxOut> + Send + Sync>,
85 ) -> Result<Witness, CannotSignVtxoError> {
86 let sighash = sighash_cache.taproot_key_spend_signature_hash(
87 input_idx, &prevouts, sighash::TapSighashType::Default,
88 ).expect("all prevouts provided");
89
90 let sig = self.sign_keyspend(vtxo, sighash).await
91 .ok_or(CannotSignVtxoError)?;
92 let witness = Witness::from_slice(&[&sig[..]]);
93
94 Ok(witness)
95 }
96
97 async fn sign_input_with_clause<G: Sync + Send>(
103 &self,
104 vtxo: &Vtxo<G, P>,
105 clause: &VtxoClause,
106 input_idx: usize,
107 sighash_cache: &mut sighash::SighashCache<impl Borrow<Transaction> + Send + Sync>,
108 prevouts: &sighash::Prevouts<impl Borrow<TxOut> + Send + Sync>,
109 ) -> Result<Witness, CannotSignVtxoError> {
110 let cb = clause.control_block(vtxo);
111
112 let exit_script = clause.tapscript();
113 let leaf_hash = taproot::TapLeafHash::from_script(
114 &exit_script,
115 taproot::LeafVersion::TapScript,
116 );
117
118 let sighash = sighash_cache.taproot_script_spend_signature_hash(
119 input_idx, &prevouts, leaf_hash, sighash::TapSighashType::Default,
120 ).expect("all prevouts provided");
121
122 let witness = self.witness(clause, &cb, sighash).await
123 .ok_or(CannotSignVtxoError)?;
124
125 debug_assert_eq!(
126 witness.size(), clause.witness_size(vtxo),
127 "actual witness size ({}) does not match expected ({})",
128 witness.size(), clause.witness_size(vtxo)
129 );
130
131 Ok(witness)
132 }
133}