dbc/tapret/
xonlypk.rs

1// Deterministic bitcoin commitments library.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2024 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved.
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use 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/// Errors during tapret commitment embedding into x-only public key.
29#[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    /// tapret node partner {0} contains alternative commitment
34    AlternativeCommitment(TapretNodePartner),
35
36    /// tapret node partner {0} has an invalid order with the commitment node
37    /// {1}
38    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        // Do via API
110        let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap();
111
112        // Do manually
113        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}