ark/vtxo/policy/
signing.rs1
2use std::borrow::Borrow;
3
4use bitcoin::hashes::Hash;
5use bitcoin::{TapSighash, Transaction, TxOut, Witness, sighash, taproot};
6
7use crate::Vtxo;
8use crate::vtxo::policy::{Policy, VtxoPolicy};
9use crate::vtxo::policy::clause::VtxoClause;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
12#[error("the vtxo has no clause signable by the provided signer")]
13pub struct CannotSignVtxoError;
14
15#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
17#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
18pub trait VtxoSigner<P: Policy = VtxoPolicy> {
19 async fn witness(
21 &self,
22 clause: &VtxoClause,
23 control_block: &taproot::ControlBlock,
24 sighash: TapSighash,
25 ) -> Option<Witness>;
26
27 async fn can_sign<G: Sync + Send>(&self, clause: &VtxoClause, vtxo: &Vtxo<G, P>) -> bool {
29 let sighash = TapSighash::all_zeros();
31 let cb = clause.control_block(vtxo);
32 self.witness(clause, &cb, sighash).await.is_some()
33 }
34
35 async fn find_signable_clause<G: Sync + Send>(&self, vtxo: &Vtxo<G, P>) -> Option<VtxoClause> {
38 let exit_delta = vtxo.exit_delta();
39 let expiry_height = vtxo.expiry_height();
40 let server_pubkey = vtxo.server_pubkey();
41
42 let clauses = vtxo.policy().clauses(exit_delta, expiry_height, server_pubkey);
43
44 for clause in clauses {
45 if self.can_sign(&clause, vtxo).await {
46 return Some(clause);
47 }
48 }
49
50 None
51 }
52
53 async fn sign_input<G: Sync + Send>(
59 &self,
60 vtxo: &Vtxo<G, P>,
61 input_idx: usize,
62 sighash_cache: &mut sighash::SighashCache<impl Borrow<Transaction> + Send + Sync>,
63 prevouts: &sighash::Prevouts<impl Borrow<TxOut> + Send + Sync>,
64 ) -> Result<Witness, CannotSignVtxoError> {
65 let clause = self.find_signable_clause(vtxo).await
66 .ok_or(CannotSignVtxoError)?;
67 self.sign_input_with_clause(vtxo, &clause, input_idx, sighash_cache, prevouts).await
68 }
69
70 async fn sign_input_with_clause<G: Sync + Send>(
76 &self,
77 vtxo: &Vtxo<G, P>,
78 clause: &VtxoClause,
79 input_idx: usize,
80 sighash_cache: &mut sighash::SighashCache<impl Borrow<Transaction> + Send + Sync>,
81 prevouts: &sighash::Prevouts<impl Borrow<TxOut> + Send + Sync>,
82 ) -> Result<Witness, CannotSignVtxoError> {
83 let cb = clause.control_block(vtxo);
84
85 let exit_script = clause.tapscript();
86 let leaf_hash = taproot::TapLeafHash::from_script(
87 &exit_script,
88 taproot::LeafVersion::TapScript,
89 );
90
91 let sighash = sighash_cache.taproot_script_spend_signature_hash(
92 input_idx, &prevouts, leaf_hash, sighash::TapSighashType::Default,
93 ).expect("all prevouts provided");
94
95 let witness = self.witness(clause, &cb, sighash).await
96 .ok_or(CannotSignVtxoError)?;
97
98 debug_assert_eq!(
99 witness.size(), clause.witness_size(vtxo),
100 "actual witness size ({}) does not match expected ({})",
101 witness.size(), clause.witness_size(vtxo)
102 );
103
104 Ok(witness)
105 }
106}