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::clause::VtxoClause;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
11#[error("the vtxo has no clause signable by the provided signer")]
12pub struct CannotSignVtxoError;
13
14#[async_trait::async_trait]
16pub trait VtxoSigner {
17 async fn witness(
19 &self,
20 clause: &VtxoClause,
21 control_block: &taproot::ControlBlock,
22 sighash: TapSighash,
23 ) -> Option<Witness>;
24
25 async fn can_sign(&self, clause: &VtxoClause, vtxo: &Vtxo) -> bool {
27 let sighash = TapSighash::all_zeros();
29 let cb = clause.control_block(vtxo);
30 self.witness(clause, &cb, sighash).await.is_some()
31 }
32
33 async fn find_signable_clause(&self, vtxo: &Vtxo) -> Option<VtxoClause> {
36 let exit_delta = vtxo.exit_delta();
37 let expiry_height = vtxo.expiry_height();
38 let server_pubkey = vtxo.server_pubkey();
39
40 let clauses = vtxo.policy().clauses(exit_delta, expiry_height, server_pubkey);
41
42 for clause in clauses {
43 if self.can_sign(&clause, vtxo).await {
44 return Some(clause);
45 }
46 }
47
48 None
49 }
50
51 async fn sign_input(
57 &self,
58 vtxo: &Vtxo,
59 input_idx: usize,
60 sighash_cache: &mut sighash::SighashCache<impl Borrow<Transaction> + Send + Sync>,
61 prevouts: &sighash::Prevouts<impl Borrow<TxOut> + Send + Sync>,
62 ) -> Result<Witness, CannotSignVtxoError> {
63 let clause = self.find_signable_clause(vtxo).await
64 .ok_or(CannotSignVtxoError)?;
65 self.sign_input_with_clause(vtxo, &clause, input_idx, sighash_cache, prevouts).await
66 }
67
68 async fn sign_input_with_clause(
74 &self,
75 vtxo: &Vtxo,
76 clause: &VtxoClause,
77 input_idx: usize,
78 sighash_cache: &mut sighash::SighashCache<impl Borrow<Transaction> + Send + Sync>,
79 prevouts: &sighash::Prevouts<impl Borrow<TxOut> + Send + Sync>,
80 ) -> Result<Witness, CannotSignVtxoError> {
81 let cb = clause.control_block(vtxo);
82
83 let exit_script = clause.tapscript();
84 let leaf_hash = taproot::TapLeafHash::from_script(
85 &exit_script,
86 taproot::LeafVersion::TapScript,
87 );
88
89 let sighash = sighash_cache.taproot_script_spend_signature_hash(
90 input_idx, &prevouts, leaf_hash, sighash::TapSighashType::Default,
91 ).expect("all prevouts provided");
92
93 let witness = self.witness(clause, &cb, sighash).await
94 .ok_or(CannotSignVtxoError)?;
95
96 debug_assert_eq!(
97 witness.size(), clause.witness_size(vtxo),
98 "actual witness size ({}) does not match expected ({})",
99 witness.size(), clause.witness_size(vtxo)
100 );
101
102 Ok(witness)
103 }
104}