Skip to content

Commit

Permalink
Merge pull request #154 from LNP-BP/layouts
Browse files Browse the repository at this point in the history
Commitment layouts and vesper description
  • Loading branch information
dr-orlovsky authored Feb 26, 2024
2 parents 5359d84 + 3a157c1 commit b7df64a
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 61 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions commit_verify/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ required-features = ["stl"]
amplify = { workspace = true, features = ["hex", "apfloat"] }
strict_encoding = { workspace = true }
strict_types = { workspace = true }
vesper-lang = "0.1.0"
commit_encoding_derive = { version = "0.11.0-beta.3", path = "derive" }
sha2 = "0.10.8"
ripemd = "0.1.3"
Expand Down
8 changes: 4 additions & 4 deletions commit_verify/derive/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,19 @@ impl CommitDerive {

let inner = match self.conf.strategy {
StrategyAttr::Strict => quote! {
engine.commit_to(self);
engine.commit_to_serialized(self);
},
StrategyAttr::ConcealStrict => quote! {
use #trait_crate::Conceal;
engine.commit_to(&self.conceal());
engine.commit_to_concealed(&self.conceal());
},
StrategyAttr::Transparent => quote! {
use amplify::Wrapper;
engine.commit_to(self.as_inner());
engine.commit_to_serialized(self.as_inner());
},
StrategyAttr::Merklize => quote! {
use amplify::Wrapper;
engine.commit_to(self.as_inner().merklize());
engine.commit_to_merkle(self.as_inner().merklize());
},
};

Expand Down
222 changes: 187 additions & 35 deletions commit_verify/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,56 +19,194 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use amplify::confinement::U64 as U64MAX;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{self, Display, Formatter};
use std::hash::Hash;

use amplify::confinement::{Confined, TinyVec, U64 as U64MAX};
use amplify::Bytes32;
use sha2::Sha256;
use strict_encoding::{StreamWriter, StrictEncode, StrictType};
use strict_encoding::{Sizing, StreamWriter, StrictDumb, StrictEncode, StrictType};
use strict_types::typesys::TypeFqn;

use crate::{DigestExt, LIB_NAME_COMMIT_VERIFY};
use crate::{Conceal, DigestExt, MerkleHash, MerkleLeaves, LIB_NAME_COMMIT_VERIFY};

const COMMIT_MAX_LEN: usize = U64MAX;

#[derive(Debug)]
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub enum CommitColType {
List,
Set,
Map { key: TypeFqn },
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub enum CommitStep {
Serialized(TypeFqn),
Collection(CommitColType, Sizing, TypeFqn),
Hashed(TypeFqn),
Merklized(TypeFqn),
Concealed(TypeFqn),
}

#[derive(Clone, Debug)]
pub struct CommitEngine {
finished: bool,
hasher: Sha256,
layout: Vec<TypeFqn>,
layout: TinyVec<CommitStep>,
}

fn commitment_fqn<T: StrictType>() -> TypeFqn {
TypeFqn::with(
libname!(T::STRICT_LIB_NAME),
T::strict_name().expect("commit encoder can commit only to named types"),
)
}

impl CommitEngine {
pub fn new(tag: &'static str) -> Self {
Self {
finished: false,
hasher: Sha256::from_tag(tag),
layout: vec![],
layout: empty!(),
}
}

pub fn commit_to<T: StrictEncode>(&mut self, value: &T) {
fn inner_commit_to<T: StrictEncode, const MAX_LEN: usize>(&mut self, value: &T) {
debug_assert!(!self.finished);
let writer = StreamWriter::new::<COMMIT_MAX_LEN>(&mut self.hasher);
let writer = StreamWriter::new::<MAX_LEN>(&mut self.hasher);
let ok = value.strict_write(writer).is_ok();
let fqn = TypeFqn::with(
libname!(T::STRICT_LIB_NAME),
T::strict_name().expect("commit encoder can commit only to named types"),
);
self.layout.push(fqn);
debug_assert!(ok);
}

pub fn as_layout(&mut self) -> &[TypeFqn] {
pub fn commit_to_serialized<T: StrictEncode>(&mut self, value: &T) {
let fqn = commitment_fqn::<T>();
debug_assert!(
Some(&fqn.name) != MerkleHash::strict_name().as_ref() ||
fqn.lib.as_str() != MerkleHash::STRICT_LIB_NAME,
"do not use commit_to_serialized for merklized collections, use commit_to_merkle \
instead"
);
debug_assert!(
Some(&fqn.name) != StrictHash::strict_name().as_ref() ||
fqn.lib.as_str() != StrictHash::STRICT_LIB_NAME,
"do not use commit_to_serialized for StrictHash types, use commit_to_hash instead"
);
self.layout
.push(CommitStep::Serialized(fqn))
.expect("too many fields for commitment");

self.inner_commit_to::<_, COMMIT_MAX_LEN>(&value);
}

pub fn commit_to_option<T: StrictEncode + StrictDumb>(&mut self, value: &Option<T>) {
let fqn = commitment_fqn::<T>();
self.layout
.push(CommitStep::Serialized(fqn))
.expect("too many fields for commitment");

self.inner_commit_to::<_, COMMIT_MAX_LEN>(&value);
}

pub fn commit_to_hash<T: CommitEncode<CommitmentId = StrictHash> + StrictType>(
&mut self,
value: T,
) {
let fqn = commitment_fqn::<T>();
self.layout
.push(CommitStep::Hashed(fqn))
.expect("too many fields for commitment");

self.inner_commit_to::<_, 32>(&value.commit_id());
}

pub fn commit_to_merkle<T: MerkleLeaves>(&mut self, value: &T)
where T::Leaf: StrictType {
let fqn = commitment_fqn::<T::Leaf>();
self.layout
.push(CommitStep::Merklized(fqn))
.expect("too many fields for commitment");

let root = MerkleHash::merklize(value);
self.inner_commit_to::<_, 32>(&root);
}

pub fn commit_to_concealed<T: Conceal>(&mut self, value: &T)
where
T: StrictType,
T::Concealed: StrictEncode,
{
let fqn = commitment_fqn::<T>();
self.layout
.push(CommitStep::Concealed(fqn))
.expect("too many fields for commitment");

let concealed = value.conceal();
self.inner_commit_to::<_, COMMIT_MAX_LEN>(&concealed);
}

pub fn commit_to_list<T, const MIN: usize, const MAX: usize>(
&mut self,
collection: &Confined<Vec<T>, MIN, MAX>,
) where
T: StrictEncode + StrictDumb,
{
let fqn = commitment_fqn::<T>();
let step =
CommitStep::Collection(CommitColType::List, Sizing::new(MIN as u64, MAX as u64), fqn);
self.layout
.push(step)
.expect("too many fields for commitment");
self.inner_commit_to::<_, COMMIT_MAX_LEN>(&collection);
}

pub fn commit_to_set<T, const MIN: usize, const MAX: usize>(
&mut self,
collection: &Confined<BTreeSet<T>, MIN, MAX>,
) where
T: Ord + StrictEncode + StrictDumb,
{
let fqn = commitment_fqn::<T>();
let step =
CommitStep::Collection(CommitColType::Set, Sizing::new(MIN as u64, MAX as u64), fqn);
self.layout
.push(step)
.expect("too many fields for commitment");
self.inner_commit_to::<_, COMMIT_MAX_LEN>(&collection);
}

pub fn commit_to_map<K, V, const MIN: usize, const MAX: usize>(
&mut self,
collection: &Confined<BTreeMap<K, V>, MIN, MAX>,
) where
K: Ord + Hash + StrictEncode + StrictDumb,
V: StrictEncode + StrictDumb,
{
let key_fqn = commitment_fqn::<K>();
let val_fqn = commitment_fqn::<V>();
let step = CommitStep::Collection(
CommitColType::Map { key: key_fqn },
Sizing::new(MIN as u64, MAX as u64),
val_fqn,
);
self.layout
.push(step)
.expect("too many fields for commitment");
self.inner_commit_to::<_, COMMIT_MAX_LEN>(&collection);
}

pub fn as_layout(&mut self) -> &[CommitStep] {
self.finished = true;
self.layout.as_ref()
}

pub fn into_layout(self) -> Vec<TypeFqn> { self.layout }
pub fn into_layout(self) -> TinyVec<CommitStep> { self.layout }

pub fn set_finished(&mut self) { self.finished = true; }

pub fn finish(self) -> Sha256 { self.hasher }

pub fn finish_layout(self) -> (Sha256, Vec<TypeFqn>) { (self.hasher, self.layout) }
pub fn finish_layout(self) -> (Sha256, TinyVec<CommitStep>) { (self.hasher, self.layout) }
}

/// Prepares the data to the *consensus commit* procedure by first running
Expand All @@ -79,21 +217,50 @@ pub trait CommitEncode {
type CommitmentId: CommitmentId;

/// Encodes the data for the commitment by writing them directly into a
/// [`io::Write`] writer instance
/// [`std::io::Write`] writer instance
fn commit_encode(&self, e: &mut CommitEngine);
}

#[derive(Getters, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CommitmentLayout {
ty: TypeFqn,
pub struct CommitLayout {
idty: TypeFqn,
#[getter(as_copy)]
tag: &'static str,
fields: Vec<TypeFqn>,
fields: TinyVec<CommitStep>,
}

impl Display for CommitLayout {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.to_vesper().display(), f)
}
}

pub trait CommitmentId: Copy + Ord + From<Sha256> + StrictType {
const TAG: &'static str;
}

pub trait CommitmentLayout: CommitEncode {
fn commitment_layout() -> CommitLayout;
}

impl<T> CommitmentLayout for T
where T: CommitEncode + StrictDumb
{
fn commitment_layout() -> CommitLayout {
let dumb = Self::strict_dumb();
let fields = dumb.commit().into_layout();
CommitLayout {
idty: TypeFqn::with(
libname!(Self::CommitmentId::STRICT_LIB_NAME),
Self::CommitmentId::strict_name()
.expect("commitment types must have explicit type name"),
),
tag: T::CommitmentId::TAG,
fields,
}
}
}

/// High-level API used in client-side validation for producing a single
/// commitment to the data, which includes running all necessary procedures like
/// concealment with [`crate::Conceal`], merklization, strict encoding,
Expand All @@ -107,8 +274,6 @@ pub trait CommitId: CommitEncode {
#[doc = hidden]
fn commit(&self) -> CommitEngine;

fn commitment_layout(&self) -> CommitmentLayout;

/// Performs commitment to client-side-validated data
fn commit_id(&self) -> Self::CommitmentId;
}
Expand All @@ -121,19 +286,6 @@ impl<T: CommitEncode> CommitId for T {
engine
}

fn commitment_layout(&self) -> CommitmentLayout {
let fields = self.commit().into_layout();
CommitmentLayout {
ty: TypeFqn::with(
libname!(Self::CommitmentId::STRICT_LIB_NAME),
Self::CommitmentId::strict_name()
.expect("commitment types must have explicit type name"),
),
tag: T::CommitmentId::TAG,
fields,
}
}

fn commit_id(&self) -> Self::CommitmentId { self.commit().finish().into() }
}

Expand Down
6 changes: 5 additions & 1 deletion commit_verify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,17 @@ pub mod stl;
pub mod merkle;
pub mod mpc;
mod digest;
pub mod vesper;

pub use commit::{CommitVerify, TryCommitVerify, VerifyError};
pub use conceal::Conceal;
pub use convolve::{ConvolveCommit, ConvolveCommitProof, ConvolveVerifyError};
pub use digest::{Digest, DigestExt, Ripemd160, Sha256};
pub use embed::{EmbedCommitProof, EmbedCommitVerify, EmbedVerifyError, VerifyEq};
pub use id::{CommitEncode, CommitEngine, CommitId, CommitmentId, CommitmentLayout, StrictHash};
pub use id::{
CommitColType, CommitEncode, CommitEngine, CommitId, CommitLayout, CommitStep, CommitmentId,
CommitmentLayout, StrictHash,
};
pub use merkle::{MerkleBuoy, MerkleHash, MerkleLeaves, MerkleNode, NodeBranching};

pub const LIB_NAME_COMMIT_VERIFY: &str = "CommitVerify";
Expand Down
Loading

0 comments on commit b7df64a

Please sign in to comment.