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(
32    feature = "serde",
33    derive(Serialize, Deserialize),
34    serde(crate = "serde_crate", rename_all = "camelCase")
35)]
36pub enum TapretKeyError {
37    /// tapret node partner {0} contains alternative commitment
38    AlternativeCommitment(TapretNodePartner),
39
40    /// tapret node partner {0} has an invalid order with the commitment node
41    /// {1}
42    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        // Do via API
112        let (outer_key, proof) = internal_pk.convolve_commit(&path_proof, &msg).unwrap();
113
114        // Do manually
115        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}