1use bc::{InternalPk, OutputPk, TapBranchHash, TapLeafHash, TapNodeHash, TapScript};
23use commit_verify::{mpc, CommitVerify, ConvolveCommit, ConvolveCommitProof};
24
25use super::{TapretFirst, TapretNodePartner, TapretPathProof, TapretProof};
26use crate::tapret::tapscript::TapretCommitment;
27
28#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
30#[display(doc_comments)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
32pub enum TapretKeyError {
33 AlternativeCommitment(TapretNodePartner),
35
36 IncorrectOrdering(TapretNodePartner, TapLeafHash),
39}
40
41impl ConvolveCommitProof<mpc::Commitment, InternalPk, TapretFirst> for TapretProof {
42 type Suppl = TapretPathProof;
43
44 fn restore_original(&self, _: &OutputPk) -> InternalPk { self.internal_pk }
45
46 fn extract_supplement(&self) -> &Self::Suppl { &self.path_proof }
47}
48
49impl ConvolveCommit<mpc::Commitment, TapretProof, TapretFirst> for InternalPk {
50 type Commitment = OutputPk;
51 type CommitError = TapretKeyError;
52
53 fn convolve_commit(
54 &self,
55 supplement: &TapretPathProof,
56 msg: &mpc::Commitment,
57 ) -> Result<(OutputPk, TapretProof), Self::CommitError> {
58 let tapret_commitment = TapretCommitment::with(*msg, supplement.nonce);
59 let script_commitment = TapScript::commit(&tapret_commitment);
60
61 let merkle_root: TapNodeHash = if let Some(ref partner) = supplement.partner_node {
62 if !partner.check_no_commitment() {
63 return Err(TapretKeyError::AlternativeCommitment(partner.clone()));
64 }
65
66 let commitment_leaf = script_commitment.tap_leaf_hash();
67 let commitment_hash = TapNodeHash::from(commitment_leaf);
68
69 if !partner.check_ordering(commitment_hash) {
70 return Err(TapretKeyError::IncorrectOrdering(partner.clone(), commitment_leaf));
71 }
72
73 TapBranchHash::with_nodes(commitment_hash, partner.tap_node_hash()).into()
74 } else {
75 TapLeafHash::with_tap_script(&script_commitment).into()
76 };
77
78 let (output_key, _) = self.to_output_pk(Some(merkle_root));
79
80 let proof = TapretProof {
81 path_proof: supplement.clone(),
82 internal_pk: *self,
83 };
84
85 Ok((output_key, proof))
86 }
87}
88
89#[cfg(test)]
90mod test {
91 #![cfg_attr(coverage_nightly, coverage(off))]
92
93 use std::str::FromStr;
94
95 use bc::{IntoTapHash, LeafScript};
96 use commit_verify::mpc::Commitment;
97
98 use super::*;
99
100 #[test]
101 fn key_path() {
102 let internal_pk = InternalPk::from_str(
103 "c5f93479093e2b8f724a79844cc10928dd44e9a390b539843fb83fbf842723f3",
104 )
105 .unwrap();
106 let msg = mpc::Commitment::from([8u8; 32]);
107 let path_proof = TapretPathProof::root(0);
108
109 let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap();
111
112 let tapret_commitment = TapretCommitment::with(msg, path_proof.nonce);
114 let script_commitment = TapScript::commit(&tapret_commitment);
115 let script_leaf = TapLeafHash::with_tap_script(&script_commitment);
116 let (real_key, _) = internal_pk.to_output_pk(Some(script_leaf.into_tap_hash()));
117
118 assert_eq!(outer_key, real_key);
119
120 assert_eq!(proof, TapretProof {
121 path_proof,
122 internal_pk
123 });
124
125 ConvolveCommitProof::<Commitment, InternalPk, TapretFirst>::verify(
126 &proof, &msg, &outer_key,
127 )
128 .unwrap();
129 }
130
131 #[test]
132 fn single_script() {
133 let internal_pk = InternalPk::from_str(
134 "c5f93479093e2b8f724a79844cc10928dd44e9a390b539843fb83fbf842723f3",
135 )
136 .unwrap();
137 let msg = mpc::Commitment::from([8u8; 32]);
138 let path_proof = TapretPathProof::with(
139 TapretNodePartner::RightLeaf(LeafScript::from_tap_script(default!())),
140 1,
141 )
142 .unwrap();
143
144 let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap();
145
146 assert_eq!(proof, TapretProof {
147 path_proof,
148 internal_pk
149 });
150
151 ConvolveCommitProof::<Commitment, InternalPk, TapretFirst>::verify(
152 &proof, &msg, &outer_key,
153 )
154 .unwrap();
155 }
156
157 #[test]
158 #[should_panic(expected = "IncorrectOrdering")]
159 fn invalid_partner_ordering() {
160 let internal_pk = InternalPk::from_str(
161 "c5f93479093e2b8f724a79844cc10928dd44e9a390b539843fb83fbf842723f3",
162 )
163 .unwrap();
164 let msg = mpc::Commitment::from([8u8; 32]);
165 let path_proof = TapretPathProof::with(
166 TapretNodePartner::RightLeaf(LeafScript::from_tap_script(default!())),
167 11,
168 )
169 .unwrap();
170
171 let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap();
172
173 assert_eq!(proof, TapretProof {
174 path_proof,
175 internal_pk
176 });
177
178 ConvolveCommitProof::<Commitment, InternalPk, TapretFirst>::verify(
179 &proof, &msg, &outer_key,
180 )
181 .unwrap();
182 }
183}