1use bc::Tx;
23use commit_verify::{mpc, ConvolveCommit, ConvolveCommitProof};
24
25use super::{TapretFirst, TapretKeyError, TapretProof};
26
27#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
30pub enum TapretError {
31 #[from]
33 #[display(inner)]
34 KeyEmbedding(TapretKeyError),
35
36 #[display(doc_comments)]
38 NoTaprootOutput,
39}
40
41impl ConvolveCommitProof<mpc::Commitment, Tx, TapretFirst> for TapretProof {
42 type Suppl = Self;
43
44 fn restore_original(&self, commitment: &Tx) -> Tx {
45 let mut tx = commitment.clone();
46
47 for txout in &mut tx.outputs {
48 if txout.script_pubkey.is_p2tr() {
49 txout.script_pubkey = self.original_pubkey_script();
50 break;
51 }
52 }
53 tx
54 }
55
56 fn extract_supplement(&self) -> &Self::Suppl { self }
57}
58
59impl ConvolveCommit<mpc::Commitment, TapretProof, TapretFirst> for Tx {
60 type Commitment = Tx;
61 type CommitError = TapretError;
62
63 fn convolve_commit(
64 &self,
65 supplement: &TapretProof,
66 msg: &mpc::Commitment,
67 ) -> Result<(Tx, TapretProof), Self::CommitError> {
68 let mut tx = self.clone();
69
70 for txout in &mut tx.outputs {
71 if txout.script_pubkey.is_p2tr() {
72 let (commitment, proof) =
73 txout.convolve_commit(supplement, msg).map_err(TapretError::from)?;
74 *txout = commitment;
75 return Ok((tx, proof));
76 }
77 }
78
79 Err(TapretError::NoTaprootOutput)
80 }
81}
82
83#[cfg(test)]
84mod test {
85 #![cfg_attr(coverage_nightly, coverage(off))]
86
87 use std::str::FromStr;
88
89 use amplify::hex::FromHex;
90 use amplify::Bytes32;
91 use bc::InternalPk;
92 use commit_verify::mpc::Commitment;
93 use commit_verify::ConvolveVerifyError;
94 use secp256k1::{ffi, XOnlyPublicKey};
95
96 use super::*;
97 use crate::tapret::TapretPathProof;
98
99 #[test]
100 fn no_commitment() {
101 let tx = Tx::from_str(
102 "020000000001027763e2a0ad25d45b63a19c33491b67c5037e72709121290bac5481a5d5d0c9330100000000ffffffff7763e2a0ad25d45b63a19c33491b67c5037e72709121290bac5481a5d5d0c9330400000000ffffffff02026e010000000000225120455dfcc062ef80609b007377f127e4abdb5cb0052158af1fab7aa628c34563f1d508000000000000225120a2788d4208ec6b4b600aef4c13075cf1d47bda0299ed1e6eedce4e7a90fb2a2c0141150df5377a34deded048dc01bff3d4f5f31d8a89fe2fbf1d0295993c1f899b3cefd1a63900ea6346b78edd476524c08ae094ff417bfa525b585ee66ebc26bb9e010141d959f21b498d90c2ff9f5b0bf3aee9158527501162eab2e3d56371714877a97df80caab15e366855aa56443b7d081c234a4ce4d6414815a874624cbe46b643370100000000"
103 ).unwrap();
104
105 #[allow(unsafe_code)]
106 let internal_pk: XOnlyPublicKey = unsafe {
107 ffi::XOnlyPublicKey::from_array_unchecked(<[u8; 64]>::from_hex(
108 "cb5271aa59fc637e29d034ec75363ca241fda5d3939684603b469b185be7e50f18ec6fd539e7dc1fd5fb4cf046d2cef5028a5ca0cdb09a252683e6a6eb2ad61d",
109 ).unwrap()).into()
110 };
111 let proof = TapretProof {
112 path_proof: TapretPathProof {
113 partner_node: None,
114 nonce: 0,
115 },
116 internal_pk: InternalPk::from(internal_pk),
117 };
118
119 let msg = Commitment::from(Bytes32::zero());
120 assert_eq!(
121 ConvolveCommitProof::<_, Tx, _>::verify(&proof, &msg, &tx),
122 Err(ConvolveVerifyError::CommitmentMismatch)
123 );
124 }
125}