commit_verify/convolve.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
// Client-side-validation foundation libraries.
//
// SPDX-License-Identifier: Apache-2.0
//
// Written in 2019-2024 by
// Dr. Maxim Orlovsky <orlovsky@lnp-bp.org>
//
// Copyright (C) 2019-2024 LNP/BP Standards Association. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Convolved commitments (convolve-commit-verify scheme).
use crate::{CommitmentProtocol, VerifyEq};
/// Error during commitment verification
#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)]
#[display(doc_comments)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub enum ConvolveVerifyError {
/// commitment doesn't match the message.
CommitmentMismatch,
/// the message is invalid since a valid commitment to it can't be created.
ImpossibleMessage,
/// the proof is invalid and the commitment can't be verified.
InvalidProof,
}
/// Proof type used by [`ConvolveCommit`] protocol.
pub trait ConvolveCommitProof<Msg, Source, Protocol>
where
Self: Sized + VerifyEq,
Source: ConvolveCommit<Msg, Self, Protocol>,
Protocol: CommitmentProtocol,
{
/// Supplement is a part of the proof data provided during commitment
/// procedure.
type Suppl;
/// Restores the original source before the commitment from the supplement
/// (the `self`) and commitment.
fn restore_original(&self, commitment: &Source::Commitment) -> Source;
/// Extract supplement from the proof.
fn extract_supplement(&self) -> &Self::Suppl;
/// Verifies commitment using proof (the `self`) against the message.
///
/// Default implementation repeats [`ConvolveCommit::convolve_commit`]
/// procedure, restoring the original value out of proof data, checking
/// that the resulting commitment matches the provided one in the
/// `commitment` parameter.
///
/// # Errors
///
/// Errors if the commitment doesn't pass the validation (see
/// [`ConvolveVerifyError`] variants for the cases when this may happen).
fn verify(
&self,
msg: &Msg,
commitment: &Source::Commitment,
) -> Result<(), ConvolveVerifyError>
where
Self: VerifyEq,
{
let original = self.restore_original(commitment);
let suppl = self.extract_supplement();
let (commitment_prime, proof) = original
.convolve_commit(suppl, msg)
.map_err(|_| ConvolveVerifyError::ImpossibleMessage)?;
if !self.verify_eq(&proof) {
return Err(ConvolveVerifyError::InvalidProof);
}
if !commitment.verify_eq(&commitment_prime) {
return Err(ConvolveVerifyError::CommitmentMismatch);
}
Ok(())
}
}
/// Trait for *convolve-commit-verify scheme*, where some data structure (named
/// *container*) may commit to existing *message* using *supplement* and
/// producing final *commitment* value. The commitment can't be used to restore
/// original message, however the fact of the commitment may be
/// deterministically *verified* when the message and the supplement (now acting
/// as a *proof*) proof are *revealed*.
///
/// In other words, *convolve-commit* takes an object (`self`), a *supplement*,
/// convolves them in certain way together and than uses the result to produce a
/// commitment to a *message* and a *proof*:
/// - `self + supplement -> internal_repr`;
/// - `internal_repr + msg -> (commitment, proof)`.
///
/// Later on, a verifier presented with a message and the proof may do the
/// commitment verification in the following way: `msg, proof, commitment ->
/// bool`.
///
/// To use *convolve-commit-verify scheme* one needs to implement this trait for
/// a data structure acting as a container for a specific commitment under
/// certain protocol, specified as generic parameters. The container type must
/// specify commitment types as associated type [`Self::Commitment`]. The
/// commitment type in certain cases may be equal to the original container
/// type; when the commitment represents internally modified container.
///
/// The difference between *convolve-commit-verify* and *embed-commit-verify*
/// schemes is in the fact that unlike embed-commit, convolve-commit does not
/// produce a proof external to the commitment, but instead requires additional
/// immutable supplement information which is not a part of the container
/// converted into the commitment. As an example one may consider procedures of
/// homomorphic public key tweaking with the hash of the message, which is
/// a case of embed-commit procedure, producing original public key as a proof
/// and tweaked public key as a commitment -- and procedure of pay-to-contract
/// commitment in scriptPubkey of a transaction output, which requires
/// additional information about the public key or scripts present in the
/// scriptPubkey only in hashed form (this is *supplement*), and producing just
/// a modified version of the scriptPubkey (commitment) without any additional
/// proof data.
///
/// Operations with *convolve-commit-verify scheme* may be represented in form
/// of `ConvolveCommit: (Container, Supplement, Message) -> Commitment` (see
/// [`Self::convolve_commit`] and
/// `Verify: (Container', Supplement, Message) -> bool` (see
/// [`ConvolveCommitProof::verify`]).
///
/// This trait is heavily used in **deterministic bitcoin commitments**.
///
/// # Protocol definition
///
/// Generic parameter `Protocol` provides context & configuration for commitment
/// scheme protocol used for this container type.
///
/// Introduction of this generic allows to:
/// - implement trait for foreign data types;
/// - add multiple implementations under different commitment protocols to the
/// combination of the same message and container type (each of each will have
/// its own `Proof` type defined as an associated generic).
///
/// Usually represents an uninstantiable type, but may be a structure
/// containing commitment protocol configuration or context objects.
///
/// ```
/// # use commit_verify::CommitmentProtocol;
///
/// // Uninstantiable type
/// pub enum Lnpbp6 {}
///
/// impl CommitmentProtocol for Lnpbp6 {}
///
/// // Protocol definition
/// pub enum Lnpbp1 {}
/// // ...
/// ```
pub trait ConvolveCommit<Msg, Proof, Protocol>
where
Self: Sized,
Proof: ConvolveCommitProof<Msg, Self, Protocol>,
Protocol: CommitmentProtocol,
{
/// Commitment type produced as a result of [`Self::convolve_commit`]
/// procedure.
type Commitment: Sized + VerifyEq;
/// Error type that may be reported during [`Self::convolve_commit`]
/// procedure. It may also be returned from [`ConvolveCommitProof::verify`]
/// in case the proof data are invalid and the commitment can't be
/// re-created.
type CommitError: std::error::Error;
/// Takes the `supplement` to unparse the content of this container (`self`)
/// ("convolves" these two data together) and uses them to produce a final
/// [`Self::Commitment`] to the message `msg`.
///
/// Implementations must error with a dedicated error type enumerating
/// commitment procedure mistakes.
fn convolve_commit(
&self,
supplement: &Proof::Suppl,
msg: &Msg,
) -> Result<(Self::Commitment, Proof), Self::CommitError>;
/// Phantom method used to add `Protocol` generic parameter to the trait.
///
/// # Panics
///
/// Always panics when called.
#[doc(hidden)]
fn _phantom(_: Protocol) {
unimplemented!("EmbedCommitVerify::_phantom is a marker method which must not be used")
}
}