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