commit_verify/
lib.rs

1// Client-side-validation foundation libraries.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed in 2019-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
6// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association, Switzerland.
9// Copyright (C) 2024-2025 LNP/BP Laboratories,
10//                         Institute for Distributed and Cognitive Systems
11// (InDCS), Switzerland. Copyright (C) 2019-2025 Dr Maxim Orlovsky.
12// All rights under the above copyrights are reserved.
13//
14// Licensed under the Apache License, Version 2.0 (the "License"); you may not
15// use this file except in compliance with the License. You may obtain a copy of
16// the License at
17//
18//        http://www.apache.org/licenses/LICENSE-2.0
19//
20// Unless required by applicable law or agreed to in writing, software
21// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
22// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
23// License for the specific language governing permissions and limitations under
24// the License.
25
26// Coding conventions
27#![deny(
28    unsafe_code,
29    dead_code,
30    missing_docs,
31    unused_variables,
32    unused_mut,
33    unused_imports,
34    non_upper_case_globals,
35    non_camel_case_types,
36    non_snake_case
37)]
38#![cfg_attr(docsrs, feature(doc_auto_cfg))]
39#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
40
41//! Standard cryptographic commitment library, created and supported by the
42//! LNP/BP Labs.
43// TODO: Extend description and readme
44
45#[macro_use]
46extern crate amplify;
47#[macro_use]
48extern crate strict_encoding;
49#[macro_use]
50extern crate commit_encoding_derive;
51#[cfg(feature = "serde")]
52#[macro_use]
53extern crate serde;
54
55#[cfg(feature = "derive")]
56pub use commit_encoding_derive::CommitEncode;
57
58mod commit;
59mod conceal;
60mod convolve;
61mod embed;
62mod id;
63#[cfg(feature = "stl")]
64pub mod stl;
65
66pub mod merkle;
67pub mod mpc;
68mod digest;
69#[cfg(feature = "vesper")]
70pub mod vesper;
71
72pub use commit::{CommitVerify, TryCommitVerify, VerifyError};
73pub use conceal::Conceal;
74pub use convolve::{ConvolveCommit, ConvolveCommitProof, ConvolveVerifyError};
75pub use digest::{Digest, DigestExt, Ripemd160, Sha256};
76pub use embed::{EmbedCommitProof, EmbedCommitVerify, EmbedVerifyError, VerifyEq};
77pub use id::{
78    CommitColType, CommitEncode, CommitEngine, CommitId, CommitLayout, CommitStep, CommitmentId,
79    CommitmentLayout, StrictHash,
80};
81pub use merkle::{MerkleBuoy, MerkleHash, MerkleLeaves, MerkleNode, NodeBranching};
82
83/// Name of the CommitVerify strict type library.
84pub const LIB_NAME_COMMIT_VERIFY: &str = "CommitVerify";
85
86/// Marker trait for specific commitment protocols.
87///
88/// Generic parameter `Protocol` used in commitment scheme traits provides a
89/// context & configuration for the concrete implementations.
90///
91/// Introduction of such generic allows to:
92/// - implement trait for foreign data types;
93/// - add multiple implementations under different commitment protocols to the
94///   combination of the same message and container type (each of each will have
95///   its own `Proof` type defined as an associated generic).
96pub trait CommitmentProtocol {}
97
98/// Protocol defining commits created by using externally created hash value
99/// *optionally pre-tagged*.
100pub struct UntaggedProtocol;
101impl CommitmentProtocol for UntaggedProtocol {}
102
103/// Reserved bytes.
104#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
105#[display("reserved")]
106#[derive(StrictType, StrictEncode)]
107#[strict_type(lib = LIB_NAME_COMMIT_VERIFY)]
108pub struct ReservedBytes<const LEN: usize, const VAL: u8 = 0>([u8; LEN]);
109
110impl<const LEN: usize, const VAL: u8> Default for ReservedBytes<LEN, VAL> {
111    fn default() -> Self { Self([VAL; LEN]) }
112}
113
114impl<const LEN: usize, const VAL: u8> From<[u8; LEN]> for ReservedBytes<LEN, VAL> {
115    fn from(value: [u8; LEN]) -> Self {
116        assert_eq!(value, [VAL; LEN]);
117        Self(value)
118    }
119}
120
121impl<const LEN: usize, const VAL: u8> ReservedBytes<LEN, VAL> {
122    /// Constant constructor.
123    pub const fn new() -> Self { Self([VAL; LEN]) }
124}
125
126mod _reserved {
127    use strict_encoding::{DecodeError, ReadTuple, StrictDecode, TypedRead};
128
129    use crate::{CommitEncode, CommitEngine, ReservedBytes, StrictHash};
130
131    impl<const LEN: usize, const VAL: u8> CommitEncode for ReservedBytes<LEN, VAL> {
132        type CommitmentId = StrictHash;
133
134        fn commit_encode(&self, e: &mut CommitEngine) { e.commit_to_serialized(self) }
135    }
136
137    impl<const LEN: usize, const VAL: u8> StrictDecode for ReservedBytes<LEN, VAL> {
138        fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
139            let reserved = reader.read_tuple(|r| r.read_field().map(Self))?;
140            if reserved != ReservedBytes::<LEN, VAL>::default() {
141                Err(DecodeError::DataIntegrityError(format!(
142                    "unsupported reserved byte value indicating a future RGB version. Please \
143                     update your software, or, if the problem persists, contact your vendor \
144                     providing the following version information: {reserved}"
145                )))
146            } else {
147                Ok(reserved)
148            }
149        }
150    }
151
152    #[cfg(feature = "serde")]
153    mod _serde {
154        use amplify::hex::{FromHex, ToHex};
155        use serde::de::Error;
156        use serde::{Deserialize, Deserializer, Serialize, Serializer};
157
158        use super::*;
159
160        impl<const LEN: usize, const VAL: u8> Serialize for ReservedBytes<LEN, VAL> {
161            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162            where S: Serializer {
163                if serializer.is_human_readable() {
164                    serializer.serialize_str(&self.0.to_hex())
165                } else {
166                    serializer.serialize_bytes(self.0.as_ref())
167                }
168            }
169        }
170
171        impl<'de, const LEN: usize, const VAL: u8> Deserialize<'de> for ReservedBytes<LEN, VAL> {
172            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173            where D: Deserializer<'de> {
174                let v = if deserializer.is_human_readable() {
175                    let s = String::deserialize(deserializer)?;
176                    Vec::<u8>::from_hex(&s)
177                        .map_err(|_| D::Error::custom("invalid reserved value"))?
178                } else {
179                    Vec::<u8>::deserialize(deserializer)?
180                };
181
182                if v != [VAL; LEN] {
183                    return Err(Error::custom("invalid reserved value"));
184                }
185
186                Ok(default!())
187            }
188        }
189    }
190}
191
192/// Helpers for writing test functions working with commit schemes
193#[cfg(test)]
194pub mod test_helpers {
195    #![cfg_attr(coverage_nightly, coverage(off))]
196
197    use amplify::confinement::SmallVec;
198    use amplify::hex::FromHex;
199
200    pub use super::commit::test_helpers::*;
201    pub use super::embed::test_helpers::*;
202    use super::*;
203
204    /// Generates a set of messages for testing purposes
205    ///
206    /// All of these messages MUST produce different commitments, otherwise the
207    /// commitment algorithm is not collision-resistant
208    pub fn gen_messages() -> Vec<SmallVec<u8>> {
209        vec![
210            // empty message
211            b"".to_vec(),
212            // zero byte message
213            b"\x00".to_vec(),
214            // text message
215            b"test".to_vec(),
216            // text length-extended message
217            b"test*".to_vec(),
218            // short binary message
219            Vec::from_hex("deadbeef").unwrap(),
220            // length-extended version
221            Vec::from_hex("deadbeef00").unwrap(),
222            // prefixed version
223            Vec::from_hex("00deadbeef").unwrap(),
224            // serialized public key as text
225            b"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798".to_vec(),
226            // the same public key binary data
227            Vec::from_hex("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")
228                .unwrap(),
229            // different public key
230            Vec::from_hex("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")
231                .unwrap(),
232        ]
233        .into_iter()
234        .map(|v| SmallVec::try_from(v).unwrap())
235        .collect()
236    }
237}