From ec9b66e8296206ea78ff5d971d914457ead79627 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 1 Feb 2024 01:55:12 +0100 Subject: [PATCH] commit: refactor CommitmentId trait system and tags --- commit_verify/derive/src/derive.rs | 2 -- commit_verify/derive/src/params.rs | 9 ++------- commit_verify/derive/tests/base.rs | 26 ++++++++++++++---------- commit_verify/src/id.rs | 25 ++++++++++------------- commit_verify/src/lib.rs | 2 +- commit_verify/src/merkle.rs | 22 +++++++++++--------- commit_verify/src/mpc/atoms.rs | 8 ++++++-- commit_verify/src/mpc/block.rs | 32 +++++++++++++++--------------- commit_verify/src/mpc/tree.rs | 10 +++++----- 9 files changed, 69 insertions(+), 67 deletions(-) diff --git a/commit_verify/derive/src/derive.rs b/commit_verify/derive/src/derive.rs index 86bd57c5..59d62213 100644 --- a/commit_verify/derive/src/derive.rs +++ b/commit_verify/derive/src/derive.rs @@ -29,7 +29,6 @@ impl CommitDerive { let (impl_generics, ty_generics, where_clause) = self.data.generics.split_for_impl(); let trait_crate = &self.conf.commit_crate; let commitment_id = &self.conf.id; - let tag = &self.conf.tag; let ident_name = &self.data.name; let inner = match self.conf.strategy { @@ -53,7 +52,6 @@ impl CommitDerive { Ok(quote! { #[automatically_derived] impl #impl_generics #trait_crate::CommitEncode for #ident_name #ty_generics #where_clause { - const COMMITMENT_TAG: &'static str = #tag; type CommitmentId = #commitment_id; fn commit_encode(&self, engine: &mut #trait_crate::CommitEngine) { diff --git a/commit_verify/derive/src/params.rs b/commit_verify/derive/src/params.rs index 9ae063b2..6f235f0c 100644 --- a/commit_verify/derive/src/params.rs +++ b/commit_verify/derive/src/params.rs @@ -19,14 +19,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use amplify_syn::{ArgValueReq, AttrReq, DataType, LiteralClass, ParametrizedAttr, TypeClass}; +use amplify_syn::{ArgValueReq, AttrReq, DataType, ParametrizedAttr, TypeClass}; use proc_macro2::Span; use quote::ToTokens; -use syn::{DeriveInput, Error, LitStr, Path, Result}; +use syn::{DeriveInput, Error, Path, Result}; const ATTR: &str = "commit_encode"; const ATTR_CRATE: &str = "crate"; -const ATTR_TAG: &str = "tag"; const ATTR_ID: &str = "id"; const ATTR_STRATEGY: &str = "strategy"; const ATTR_STRATEGY_STRICT: &str = "strict"; @@ -37,7 +36,6 @@ const ATTR_STRATEGY_MERKLIZE: &str = "merklize"; pub struct ContainerAttr { pub commit_crate: Path, pub strategy: StrategyAttr, - pub tag: LitStr, pub id: Path, } @@ -77,14 +75,12 @@ impl TryFrom for ContainerAttr { let req = AttrReq::with(map![ ATTR_CRATE => ArgValueReq::optional(TypeClass::Path), ATTR_ID => ArgValueReq::required(TypeClass::Path), - ATTR_TAG => ArgValueReq::required(LiteralClass::Str), ATTR_STRATEGY => ArgValueReq::required(TypeClass::Path), ]); params.check(req)?; let path = params.arg_value(ATTR_STRATEGY).expect("must be present"); let strategy = StrategyAttr::try_from(&path)?; - let tag = params.arg_value(ATTR_TAG).expect("must be present"); let id = params.arg_value(ATTR_ID).expect("must be present"); Ok(ContainerAttr { @@ -92,7 +88,6 @@ impl TryFrom for ContainerAttr { .arg_value(ATTR_CRATE) .unwrap_or_else(|_| path!(commit_verify)), strategy, - tag, id, }) } diff --git a/commit_verify/derive/tests/base.rs b/commit_verify/derive/tests/base.rs index de848eaa..a85a7f2d 100644 --- a/commit_verify/derive/tests/base.rs +++ b/commit_verify/derive/tests/base.rs @@ -31,7 +31,7 @@ mod common; use std::fmt::Display; use amplify::{Bytes32, Wrapper}; -use commit_verify::{CommitEncode, CommitmentId, Conceal, DigestExt, Sha256}; +use commit_verify::{CommitEncode, CommitId, CommitmentId, Conceal, DigestExt, Sha256}; use strict_encoding::{StrictDecode, StrictDumb, StrictEncode}; const TEST_LIB: &str = "TestLib"; @@ -46,20 +46,24 @@ struct DumbId( Bytes32, ); +impl CommitmentId for DumbId { + const TAG: &'static str = ""; +} + impl From for DumbId { fn from(value: Sha256) -> Self { value.finish().into() } } -fn verify_commit(value: T, expect: &'static str) -where T::Id: Display { - assert_eq!(&value.commitment_id().to_string(), expect, "invalid commitment"); +fn verify_commit(value: T, expect: &'static str) +where T::CommitmentId: Display { + assert_eq!(&value.commit_id().to_string(), expect, "invalid commitment"); } #[test] fn strategy_transparent() -> common::Result { #[derive(Wrapper, Clone, PartialEq, Eq, Debug, From)] #[derive(CommitEncode)] - #[commit_encode(strategy = transparent, tag = "", id = DumbId)] + #[commit_encode(strategy = transparent, id = DumbId)] struct ShortLen(u16); verify_commit(ShortLen(0), "2bb00b2f346511235882255a898a224b6858e18ebec0a11967eb51f0ed1a2ff5"); @@ -78,7 +82,7 @@ fn strategy_strict_enum() -> common::Result { #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = TEST_LIB, tags = repr, into_u8, try_from_u8)] #[derive(CommitEncode)] - #[commit_encode(strategy = strict, tag = "", id = DumbId)] + #[commit_encode(strategy = strict, id = DumbId)] #[repr(u8)] enum Prim { #[strict_type(dumb)] @@ -100,7 +104,7 @@ fn strategy_strict_tuple() -> common::Result { #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = TEST_LIB)] #[derive(CommitEncode)] - #[commit_encode(strategy = strict, tag = "", id = DumbId)] + #[commit_encode(strategy = strict, id = DumbId)] struct TaggedInfo(u16, u64); verify_commit( @@ -117,7 +121,7 @@ fn strategy_strict_struct() -> common::Result { #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = TEST_LIB)] #[derive(CommitEncode)] - #[commit_encode(strategy = strict, tag = "", id = DumbId)] + #[commit_encode(strategy = strict, id = DumbId)] struct TaggedInfo { a: u16, b: u64, @@ -141,7 +145,7 @@ fn enum_associated() -> common::Result { #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = TEST_LIB, tags = order)] #[derive(CommitEncode)] - #[commit_encode(strategy = strict, tag = "", id = DumbId)] + #[commit_encode(strategy = strict, id = DumbId)] enum Assoc { One { hash: [u8; 32], @@ -172,7 +176,7 @@ fn enum_custom_tags() -> common::Result { #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = TEST_LIB, tags = order)] #[derive(CommitEncode)] - #[commit_encode(strategy = strict, tag = "", id = DumbId)] + #[commit_encode(strategy = strict, id = DumbId)] enum Assoc { #[strict_type(tag = 8)] One { hash: [u8; 32], ord: u8 }, @@ -205,7 +209,7 @@ fn conceal() -> common::Result { #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = TEST_LIB, tags = order, dumb = { Self::Concealed(0) })] #[derive(CommitEncode)] - #[commit_encode(strategy = conceal, tag = "", id = DumbId)] + #[commit_encode(strategy = conceal, id = DumbId)] enum Data { Revealed(u128), Concealed(u8), diff --git a/commit_verify/src/id.rs b/commit_verify/src/id.rs index 31c20d4b..695154e1 100644 --- a/commit_verify/src/id.rs +++ b/commit_verify/src/id.rs @@ -69,10 +69,8 @@ impl CommitEngine { /// necessary conceal and merklization procedures, and them performing strict /// encoding for the resulted data. pub trait CommitEncode { - const COMMITMENT_TAG: &'static str; - /// Type of the resulting commitment. - type CommitmentId: From + StrictType; + type CommitmentId: CommitmentId; /// Encodes the data for the commitment by writing them directly into a /// [`io::Write`] writer instance @@ -86,28 +84,27 @@ pub struct CommitmentLayout { fields: Vec, } +pub trait CommitmentId: Copy + Ord + From + StrictType { + const TAG: &'static str; +} + /// 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, /// wrapped into [`CommitEncode`], followed by the actual commitment to its /// output. -pub trait CommitmentId: CommitEncode { - /// Type of the resulting commitment. - type Id: From; - +pub trait CommitId: CommitEncode { fn commit(&self) -> CommitEngine; fn commitment_layout(&self) -> CommitmentLayout; /// Performs commitment to client-side-validated data - fn commitment_id(&self) -> Self::Id; + fn commit_id(&self) -> Self::CommitmentId; } -impl CommitmentId for T { - type Id = T::CommitmentId; - +impl CommitId for T { fn commit(&self) -> CommitEngine { - let mut engine = CommitEngine::new(T::COMMITMENT_TAG); + let mut engine = CommitEngine::new(T::CommitmentId::TAG); self.commit_encode(&mut engine); engine.set_finished(); engine @@ -121,10 +118,10 @@ impl CommitmentId for T { Self::CommitmentId::strict_name() .expect("commitment types must have explicit type name"), ), - tag: T::COMMITMENT_TAG, + tag: T::CommitmentId::TAG, fields, } } - fn commitment_id(&self) -> Self::Id { self.commit().finish().into() } + fn commit_id(&self) -> Self::CommitmentId { self.commit().finish().into() } } diff --git a/commit_verify/src/lib.rs b/commit_verify/src/lib.rs index 8f69a391..e4f98bb4 100644 --- a/commit_verify/src/lib.rs +++ b/commit_verify/src/lib.rs @@ -62,7 +62,7 @@ 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, CommitmentId, CommitmentLayout}; +pub use id::{CommitEncode, CommitEngine, CommitId, CommitmentId, CommitmentLayout}; pub use merkle::{MerkleBuoy, MerkleHash, MerkleLeaves, MerkleNode, NodeBranching}; pub const LIB_NAME_COMMIT_VERIFY: &str = "CommitVerify"; diff --git a/commit_verify/src/merkle.rs b/commit_verify/src/merkle.rs index d1822c57..8dc3133e 100644 --- a/commit_verify/src/merkle.rs +++ b/commit_verify/src/merkle.rs @@ -30,7 +30,7 @@ use sha2::Sha256; use strict_encoding::StrictEncode; use crate::digest::DigestExt; -use crate::{CommitmentId, LIB_NAME_COMMIT_VERIFY}; +use crate::{CommitId, CommitmentId, LIB_NAME_COMMIT_VERIFY}; /// Type of a merkle node branching. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -55,7 +55,7 @@ pub enum NodeBranching { #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_COMMIT_VERIFY)] #[derive(CommitEncode)] -#[commit_encode(crate = crate, strategy = strict, id = MerkleHash, tag = "urn:lnpbp:merkle:node#2024-01-31")] +#[commit_encode(crate = crate, strategy = strict, id = MerkleHash)] pub struct MerkleNode { pub branching: NodeBranching, pub depth: u8, @@ -118,6 +118,10 @@ pub struct MerkleHash( Bytes32, ); +impl CommitmentId for MerkleHash { + const TAG: &'static str = "urn:lnpbp:merkle:node#2024-01-31"; +} + impl From for MerkleHash { fn from(hash: Sha256) -> Self { hash.finish().into() } } @@ -126,11 +130,11 @@ const VIRTUAL_LEAF: MerkleHash = MerkleHash(Bytes32::from_array([0xFF; 32])); impl MerkleHash { pub fn void(depth: impl Into, width: impl Into) -> Self { - MerkleNode::void(depth, width).commitment_id() + MerkleNode::void(depth, width).commit_id() } pub fn single(depth: impl Into, width: impl Into, node: MerkleHash) -> Self { - MerkleNode::single(depth, width, node).commitment_id() + MerkleNode::single(depth, width, node).commit_id() } pub fn branches( @@ -139,7 +143,7 @@ impl MerkleHash { node1: MerkleHash, node2: MerkleHash, ) -> Self { - MerkleNode::branches(depth, width, node1, node2).commitment_id() + MerkleNode::branches(depth, width, node1, node2).commit_id() } } @@ -149,7 +153,7 @@ impl MerkleHash { /// /// [LNPBP-81]: https://github.com/LNP-BP/LNPBPs/blob/master/lnpbp-0081.md pub fn merklize(leaves: &impl MerkleLeaves) -> Self { - let mut nodes = leaves.merkle_leaves().map(|leaf| leaf.commitment_id()); + let mut nodes = leaves.merkle_leaves().map(|leaf| leaf.commit_id()); let len = nodes.len() as u32; if len == 1 { // If we have just one leaf, it's MerkleNode value is the root @@ -197,7 +201,7 @@ impl MerkleHash { } pub trait MerkleLeaves { - type Leaf: CommitmentId; + type Leaf: CommitId; type LeafIter<'tmp>: ExactSizeIterator where Self: 'tmp; @@ -205,7 +209,7 @@ pub trait MerkleLeaves { } impl MerkleLeaves for Confined, MIN, { u16::MAX as usize }> -where T: CommitmentId + Copy +where T: CommitId + Copy { type Leaf = T; type LeafIter<'tmp> = iter::Copied> where Self: 'tmp; @@ -214,7 +218,7 @@ where T: CommitmentId + Copy } impl MerkleLeaves for Confined, MIN, { u16::MAX as usize }> -where T: CommitmentId + Copy +where T: CommitId + Copy { type Leaf = T; type LeafIter<'tmp> = iter::Copied> where Self: 'tmp; diff --git a/commit_verify/src/mpc/atoms.rs b/commit_verify/src/mpc/atoms.rs index aca321b2..d025a339 100644 --- a/commit_verify/src/mpc/atoms.rs +++ b/commit_verify/src/mpc/atoms.rs @@ -26,7 +26,7 @@ use sha2::Sha256; use strict_encoding::StrictDumb; use crate::merkle::MerkleHash; -use crate::DigestExt; +use crate::{CommitmentId, DigestExt}; pub const MPC_MINIMAL_DEPTH: u5 = u5::with(3); @@ -86,7 +86,7 @@ impl Message { #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = crate::LIB_NAME_COMMIT_VERIFY, tags = custom)] #[derive(CommitEncode)] -#[commit_encode(crate = crate, strategy = strict, tag = "urn:lnpbp:mpc:tree:leaf#2024-01-31", id = MerkleHash)] +#[commit_encode(crate = crate, strategy = strict, id = MerkleHash)] pub enum Leaf { // We use this constant since we'd like to be distinct from NodeBranching values #[strict_type(tag = 0x10)] @@ -132,6 +132,10 @@ pub struct Commitment( Bytes32, ); +impl CommitmentId for Commitment { + const TAG: &'static str = "urn:lnpbp:mpc:commitment#2024-01-31"; +} + impl Commitment { pub fn copy_from_slice(slice: &[u8]) -> Result { Bytes32::copy_from_slice(slice).map(Self) diff --git a/commit_verify/src/mpc/block.rs b/commit_verify/src/mpc/block.rs index c204b0fa..1b0be34e 100644 --- a/commit_verify/src/mpc/block.rs +++ b/commit_verify/src/mpc/block.rs @@ -28,7 +28,7 @@ use amplify::confinement::{Confined, LargeVec}; use amplify::num::u5; use strict_encoding::{StrictDeserialize, StrictEncode, StrictSerialize}; -use crate::id::CommitmentId; +use crate::id::CommitId; use crate::merkle::{MerkleBuoy, MerkleHash}; use crate::mpc::atoms::Leaf; use crate::mpc::tree::protocol_id_pos; @@ -124,7 +124,7 @@ impl TreeNode { TreeNode::CommitmentLeaf { protocol_id, message, - } => Leaf::inhabited(protocol_id, message).commitment_id(), + } => Leaf::inhabited(protocol_id, message).commit_id(), } } } @@ -134,7 +134,7 @@ impl TreeNode { #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_COMMIT_VERIFY)] #[derive(CommitEncode)] -#[commit_encode(crate = crate, strategy = conceal, id = Commitment, tag = "urn:lnpbp:mpc:commitment#2024-01-31")] +#[commit_encode(crate = crate, strategy = conceal, id = Commitment)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct MerkleBlock { /// Tree depth (up to 16). @@ -173,7 +173,7 @@ impl From<&MerkleTree> for MerkleBlock { }) .unwrap_or_else(|| TreeNode::ConcealedNode { depth: tree.depth, - hash: Leaf::entropy(tree.entropy, pos).commitment_id(), + hash: Leaf::entropy(tree.entropy, pos).commit_id(), }) }); let cross_section = @@ -388,8 +388,8 @@ impl MerkleBlock { /// each one of them. pub fn merge_reveal(&mut self, other: MerkleBlock) -> Result { let orig = self.clone(); - let base_root = self.commitment_id(); - let merged_root = other.commitment_id(); + let base_root = self.commit_id(); + let merged_root = other.commit_id(); if base_root != merged_root { return Err(MergeError::UnrelatedBlocks { base_root, @@ -488,12 +488,12 @@ Failed merge: {self:#?}" ); assert_eq!( base_root, - self.commitment_id(), + self.commit_id(), "LNPBP-4 merge-reveal procedure is broken; please report the below data to the LNP/BP \ Standards Association Original commitment id: {base_root} Changed commitment id: {}", - self.commitment_id() + self.commit_id() ); Ok(self.cross_section.len() as u16) @@ -623,7 +623,7 @@ impl MerkleProof { message: Message, ) -> Result { let block = MerkleBlock::with(self, protocol_id, message)?; - Ok(block.commitment_id()) + Ok(block.commit_id()) } } @@ -643,9 +643,9 @@ mod test { // Check we preserve entropy value assert_eq!(Some(tree.entropy), block.entropy); // Check if we remove entropy the commitment doesn't change - let cid1 = block.commitment_id(); + let cid1 = block.commit_id(); block.entropy = None; - let cid2 = block.commitment_id(); + let cid2 = block.commit_id(); assert_eq!(cid1, cid2); } @@ -658,13 +658,13 @@ mod test { let (pid, msg) = msgs.first_key_value().unwrap(); let leaf = Leaf::inhabited(*pid, *msg); let cid1 = block.cross_section.first().unwrap().to_merkle_node(); - let cid2 = leaf.commitment_id(); + let cid2 = leaf.commit_id(); assert_eq!(cid1, cid2); assert_eq!(tree.conceal(), block.conceal()); assert_eq!(tree.root(), block.conceal()); assert_eq!(tree.root(), cid1); - assert_eq!(tree.commitment_id(), block.commitment_id()) + assert_eq!(tree.commit_id(), block.commit_id()) } #[test] @@ -676,7 +676,7 @@ mod test { assert_eq!(tree.conceal(), block.conceal()); assert_eq!(tree.root(), block.conceal()); - assert_eq!(tree.commitment_id(), block.commitment_id()) + assert_eq!(tree.commit_id(), block.commit_id()) } } @@ -689,7 +689,7 @@ mod test { assert_eq!(tree.conceal(), block.conceal()); assert_eq!(tree.root(), block.conceal()); - assert_eq!(tree.commitment_id(), block.commitment_id()) + assert_eq!(tree.commit_id(), block.commit_id()) } } @@ -721,7 +721,7 @@ mod test { } } - assert_eq!(merged_block.commitment_id(), mpc_tree.commitment_id()); + assert_eq!(merged_block.commit_id(), mpc_tree.commit_id()); } } } diff --git a/commit_verify/src/mpc/tree.rs b/commit_verify/src/mpc/tree.rs index 99c9e813..d9d9f110 100644 --- a/commit_verify/src/mpc/tree.rs +++ b/commit_verify/src/mpc/tree.rs @@ -40,7 +40,7 @@ type OrderedMap = MediumOrdMap; #[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_COMMIT_VERIFY)] #[derive(CommitEncode)] -#[commit_encode(crate = crate, strategy = conceal, id = Commitment, tag = "urn:lnpbp:mpc:commitment#2024-01-31")] +#[commit_encode(crate = crate, strategy = conceal, id = Commitment)] pub struct MerkleTree { /// Tree depth (up to 32). pub(super) depth: u5, @@ -249,7 +249,7 @@ mod test { use strict_encoding::StrictEncode; use crate::mpc::tree::test_helpers::{make_random_messages, make_random_tree}; - use crate::{CommitmentId, Conceal}; + use crate::{CommitId, Conceal}; #[test] #[should_panic(expected = "Empty")] @@ -316,7 +316,7 @@ mod test { fn tree_id() { let msgs = make_random_messages(9); let tree = make_random_tree(&msgs); - let id = tree.commitment_id(); + let id = tree.commit_id(); let root = tree.root(); assert_ne!(id.into_inner(), root.into_inner()); } @@ -325,7 +325,7 @@ mod test { fn tree_id_entropy() { let msgs = make_random_messages(9); let mut tree = make_random_tree(&msgs); - let id1 = tree.commitment_id(); + let id1 = tree.commit_id(); tree.entropy = loop { let entropy = random(); @@ -333,7 +333,7 @@ mod test { break entropy; } }; - let id2 = tree.commitment_id(); + let id2 = tree.commit_id(); assert_ne!(id1, id2); }