commit_verify/mpc/
atoms.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
26use amplify::confinement::MediumOrdMap;
27use amplify::num::u5;
28use amplify::{Bytes32, FromSliceError, Wrapper};
29use sha2::Sha256;
30use strict_encoding::StrictDumb;
31
32use crate::merkle::MerkleHash;
33use crate::{CommitmentId, DigestExt};
34
35/// The constant defining the minimum depth of the MPC Merkle tree.
36pub const MPC_MINIMAL_DEPTH: u5 = u5::with(3);
37
38/// A specific cryptographic digest algorithm used in constructing MPC Merkle
39/// trees.
40///
41/// # Future use
42///
43/// For supporting zk compression of the proves in the future it is planned to
44/// add more zk-friendly digest algorithms.
45#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Default)]
46#[display(lowercase)]
47#[derive(StrictType, StrictEncode, StrictDecode)]
48#[strict_type(lib = crate::LIB_NAME_COMMIT_VERIFY, tags = repr, try_from_u8, into_u8)]
49#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
50#[repr(u8)]
51pub enum Method {
52    /// A tagged SHA256 digest.
53    #[default]
54    Sha256t = 0,
55}
56
57/// Map from protocol ids to commitment messages.
58pub type MessageMap = MediumOrdMap<ProtocolId, Message>;
59
60/// Source data for creation of multi-message commitments according to [LNPBP-4]
61/// procedure.
62///
63/// [LNPBP-4]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0004.md
64#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
65#[wrapper(Deref, BorrowSlice, Display, FromStr, Hex, Index, RangeOps)]
66#[derive(StrictType, StrictEncode, StrictDecode)]
67#[strict_type(lib = crate::LIB_NAME_COMMIT_VERIFY)]
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
69pub struct ProtocolId(
70    #[from]
71    #[from([u8; 32])]
72    Bytes32,
73);
74
75impl ProtocolId {
76    /// Construct a protocol id from a slice, returning an error if the slice
77    /// length differs from 32 bytes.
78    pub fn copy_from_slice(slice: &[u8]) -> Result<Self, FromSliceError> {
79        Bytes32::copy_from_slice(slice).map(Self)
80    }
81}
82
83/// Original message participating in multi-message commitment.
84///
85/// The message must be represented by a 32-byte hash.
86#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
87#[wrapper(Deref, BorrowSlice, Display, FromStr, Hex, Index, RangeOps)]
88#[derive(StrictType, StrictEncode, StrictDecode)]
89#[strict_type(lib = crate::LIB_NAME_COMMIT_VERIFY)]
90#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
91pub struct Message(
92    #[from]
93    #[from([u8; 32])]
94    Bytes32,
95);
96
97impl Message {
98    /// Construct a message from a slice, returning an error if the slice
99    /// length differs from 32 bytes.
100    pub fn copy_from_slice(slice: &[u8]) -> Result<Self, FromSliceError> {
101        Bytes32::copy_from_slice(slice).map(Self)
102    }
103}
104
105/// A leaf of the MPC Merkle tree.
106#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, From)]
107#[derive(StrictType, StrictEncode, StrictDecode)]
108#[strict_type(lib = crate::LIB_NAME_COMMIT_VERIFY, tags = custom)]
109#[derive(CommitEncode)]
110#[commit_encode(crate = crate, strategy = strict, id = MerkleHash)]
111pub enum Leaf {
112    /// Leaf, inhabited with a commitment under a protocol.
113    // We use this constant since we'd like to be distinct from NodeBranching values
114    #[strict_type(tag = 0x10)]
115    Inhabited {
116        /// The protocol under which the message is created.
117        protocol: ProtocolId,
118        /// The commitment under the protocol.
119        message: Message,
120    },
121
122    /// Lead, uninhabited by any protocol.
123    // We use this constant since we'd like to be distinct from NodeBranching values
124    #[strict_type(tag = 0x11)]
125    Entropy {
126        /// Entropic value.
127        entropy: u64,
128        /// A position of the leaf
129        pos: u32,
130    },
131}
132
133impl Leaf {
134    /// Construct a [`Leaf::Entropy`] variant.
135    pub fn entropy(entropy: u64, pos: u32) -> Self { Self::Entropy { entropy, pos } }
136
137    /// Construct a [`Leaf::Inhabited`] variant.
138    pub fn inhabited(protocol: ProtocolId, message: Message) -> Self {
139        Self::Inhabited { protocol, message }
140    }
141}
142
143impl StrictDumb for Leaf {
144    fn strict_dumb() -> Self { Self::Entropy { entropy: 0, pos: 0 } }
145}
146
147/// Final [LNPBP-4] commitment value.
148///
149/// Represents tagged hash of the merkle root of [`super::MerkleTree`] and
150/// [`super::MerkleBlock`].
151///
152/// [LNPBP-4]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0004.md
153#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
154#[wrapper(Deref, BorrowSlice, Display, FromStr, Hex, Index, RangeOps)]
155#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
156#[strict_type(lib = crate::LIB_NAME_COMMIT_VERIFY)]
157#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
158pub struct Commitment(
159    #[from]
160    #[from([u8; 32])]
161    Bytes32,
162);
163
164impl CommitmentId for Commitment {
165    const TAG: &'static str = "urn:ubideco:mpc:commitment#2024-01-31";
166}
167
168impl Commitment {
169    /// Construct a commitment from a slice, returning an error if the slice
170    /// length differs from 32 bytes.
171    pub fn copy_from_slice(slice: &[u8]) -> Result<Self, FromSliceError> {
172        Bytes32::copy_from_slice(slice).map(Self)
173    }
174}
175
176impl From<Sha256> for Commitment {
177    fn from(hasher: Sha256) -> Self { hasher.finish().into() }
178}
179
180/// Structured source multi-message data for commitment creation
181#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
182pub struct MultiSource {
183    /// A cryptographic digest algorithm used for the construction.
184    pub method: Method,
185    /// Minimal depth of the created LNPBP-4 commitment tree
186    pub min_depth: u5,
187    /// Map of the messages by their respective protocol ids
188    pub messages: MessageMap,
189    /// An optional entropy used in creating MPC Merkle tree.
190    ///
191    /// If not provided, a system random generator is used.
192    /// If the generator is not available, it will cause panic.
193    pub static_entropy: Option<u64>,
194}
195
196impl Default for MultiSource {
197    #[inline]
198    fn default() -> Self {
199        MultiSource {
200            method: Default::default(),
201            min_depth: MPC_MINIMAL_DEPTH,
202            messages: Default::default(),
203            static_entropy: None,
204        }
205    }
206}
207
208impl MultiSource {
209    #[inline]
210    /// Create a default [`MultiSource`] instance, initializing the
211    /// [`MultiSource::static_entropy`] with the provided value.
212    pub fn with_static_entropy(static_entropy: u64) -> Self {
213        MultiSource {
214            static_entropy: Some(static_entropy),
215            ..default!()
216        }
217    }
218}