diff --git a/Cargo.lock b/Cargo.lock index ad01b86e..3342826e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,7 +292,7 @@ dependencies = [ [[package]] name = "commit_encoding_derive" version = "0.11.0-beta.3" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#61b0f4969da8a69cef438de6b3085344c5626ec2" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#9f9057555d5d8907f3537069044f7503b81c3ee7" dependencies = [ "amplify", "amplify_syn", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "commit_verify" version = "0.11.0-beta.3" -source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#61b0f4969da8a69cef438de6b3085344c5626ec2" +source = "git+https://github.com/LNP-BP/client_side_validation?branch=v0.11#9f9057555d5d8907f3537069044f7503b81c3ee7" dependencies = [ "amplify", "commit_encoding_derive", diff --git a/src/contract/id.rs b/src/contract/id.rs index 072b8c44..e7379888 100644 --- a/src/contract/id.rs +++ b/src/contract/id.rs @@ -20,16 +20,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::fmt; use std::fmt::{Display, Formatter}; use std::str::FromStr; +use std::{fmt, vec}; use amplify::hex::{FromHex, ToHex}; +use amplify::num::u256; use amplify::{hex, ByteArray, Bytes32, FromSliceError, Wrapper}; use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32CHECKSUM}; -use commit_verify::{mpc, CommitmentId, DigestExt, Sha256}; +use commit_verify::{ + mpc, CommitEncode, CommitEngine, CommitId, CommitmentId, Conceal, DigestExt, MerkleHash, + MerkleLeaves, Sha256, StrictHash, +}; +use strict_encoding::StrictEncode; -use crate::LIB_NAME_RGB; +use crate::{ + Assign, AssignmentType, Assignments, ConcealedData, ConcealedState, ConfidentialState, + ExposedSeal, ExposedState, Extension, ExtensionType, Ffv, Genesis, GlobalState, + GlobalStateType, Redeemed, SchemaId, SecretSeal, Transition, TransitionType, TypedAssigns, + XChain, LIB_NAME_RGB, +}; /// Unique contract identifier equivalent to the contract genesis commitment #[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)] @@ -127,3 +137,195 @@ impl OpId { Bytes32::copy_from_slice(slice).map(Self) } } + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB)] +pub struct BaseCommitment { + pub schema_id: SchemaId, + pub testnet: bool, + pub alt_layers1: StrictHash, +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB, tags = custom, dumb = Self::Transition(strict_dumb!(), strict_dumb!()))] +pub enum TypeCommitment { + #[strict_type(tag = 0)] + Genesis(BaseCommitment), + + #[strict_type(tag = 1)] + Transition(ContractId, TransitionType), + + #[strict_type(tag = 2)] + Extension(ContractId, ExtensionType), +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB)] +#[derive(CommitEncode)] +#[commit_encode(strategy = strict, id = OpId)] +pub struct OpCommitment { + pub ffv: Ffv, + pub op_type: TypeCommitment, + pub metadata: StrictHash, + pub globals: MerkleHash, + pub inputs: MerkleHash, + pub assignments: MerkleHash, + pub redeemed: StrictHash, + pub valencies: StrictHash, +} + +impl Genesis { + pub fn commit(&self) -> OpCommitment { + let base = BaseCommitment { + schema_id: self.schema_id, + testnet: self.testnet, + alt_layers1: self.alt_layers1.commit_id(), + }; + OpCommitment { + ffv: self.ffv, + op_type: TypeCommitment::Genesis(base), + metadata: self.metadata.commit_id(), + globals: MerkleHash::merklize(&self.globals), + inputs: MerkleHash::void(0, u256::ZERO), + assignments: MerkleHash::merklize(&self.assignments), + redeemed: Redeemed::default().commit_id(), + valencies: self.valencies.commit_id(), + } + } +} + +impl Transition { + pub fn commit(&self) -> OpCommitment { + OpCommitment { + ffv: self.ffv, + op_type: TypeCommitment::Transition(self.contract_id, self.transition_type), + metadata: self.metadata.commit_id(), + globals: MerkleHash::merklize(&self.globals), + inputs: MerkleHash::merklize(&self.inputs), + assignments: MerkleHash::merklize(&self.assignments), + redeemed: Redeemed::default().commit_id(), + valencies: self.valencies.commit_id(), + } + } +} + +impl Extension { + pub fn commit(&self) -> OpCommitment { + OpCommitment { + ffv: self.ffv, + op_type: TypeCommitment::Extension(self.contract_id, self.extension_type), + metadata: self.metadata.commit_id(), + globals: MerkleHash::merklize(&self.globals), + inputs: MerkleHash::void(0, u256::ZERO), + assignments: MerkleHash::merklize(&self.assignments), + redeemed: self.redeemed.commit_id(), + valencies: self.valencies.commit_id(), + } + } +} + +impl ConcealedState { + fn commit_encode(&self, e: &mut CommitEngine) { + match self { + ConcealedState::Void => {} + ConcealedState::Fungible(val) => e.commit_to(&val.commitment), + ConcealedState::Structured(dat) => e.commit_to(dat), + ConcealedState::Attachment(att) => e.commit_to(att), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct AssignmentCommitment { + pub ty: AssignmentType, + pub state: ConcealedState, + pub seal: XChain, +} + +impl CommitEncode for AssignmentCommitment { + type CommitmentId = MerkleHash; + + fn commit_encode(&self, e: &mut CommitEngine) { + e.commit_to(&self.ty); + self.state.commit_encode(e); + e.commit_to(&self.seal); + e.set_finished(); + } +} + +impl Assign { + pub fn commitment(&self, ty: AssignmentType) -> AssignmentCommitment { + let Self::Confidential { seal, state } = self.conceal() else { + unreachable!(); + }; + AssignmentCommitment { + ty, + state: state.state_commitment(), + seal, + } + } +} + +impl MerkleLeaves for Assignments { + type Leaf = AssignmentCommitment; + type LeafIter<'tmp> = vec::IntoIter where Seal: 'tmp; + + fn merkle_leaves(&self) -> Self::LeafIter<'_> { + self.iter() + .flat_map(|(ty, a)| { + match a { + TypedAssigns::Declarative(list) => { + list.iter().map(|a| a.commitment(*ty)).collect::>() + } + TypedAssigns::Fungible(list) => { + list.iter().map(|a| a.commitment(*ty)).collect() + } + TypedAssigns::Structured(list) => { + list.iter().map(|a| a.commitment(*ty)).collect() + } + TypedAssigns::Attachment(list) => { + list.iter().map(|a| a.commitment(*ty)).collect() + } + } + .into_iter() + }) + .collect::>() + .into_iter() + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct GlobalCommitment { + pub ty: GlobalStateType, + pub state: ConcealedData, +} + +impl CommitEncode for GlobalCommitment { + type CommitmentId = MerkleHash; + + fn commit_encode(&self, e: &mut CommitEngine) { + e.commit_to(&self.ty); + e.commit_to(&self.state); + e.set_finished(); + } +} + +impl MerkleLeaves for GlobalState { + type Leaf = GlobalCommitment; + type LeafIter<'tmp> = vec::IntoIter; + + fn merkle_leaves(&self) -> Self::LeafIter<'_> { + self.iter() + .flat_map(|(ty, list)| { + list.iter().map(|val| GlobalCommitment { + ty: *ty, + state: val.conceal(), + }) + }) + .collect::>() + .into_iter() + } +} diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 14945963..56608287 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -52,9 +52,12 @@ pub use fungible::{ InvalidFieldElement, NoiseDumb, PedersenCommitment, RangeProof, RangeProofError, RevealedValue, }; pub use global::{GlobalState, GlobalValues}; -pub use id::{ContractId, OpId}; +pub use id::{ + AssignmentCommitment, BaseCommitment, ContractId, GlobalCommitment, OpCommitment, OpId, + TypeCommitment, +}; pub use operations::{ - Extension, Genesis, Input, Inputs, OpRef, Operation, Redeemed, Transition, Valencies, + Extension, Genesis, Input, Inputs, Metadata, OpRef, Operation, Redeemed, Transition, Valencies, }; pub use seal::{ ExposedSeal, GenesisSeal, GraphSeal, OutputSeal, SecretSeal, TxoSeal, WitnessId, WitnessOrd, diff --git a/src/contract/operations.rs b/src/contract/operations.rs index fc843928..c101aa13 100644 --- a/src/contract/operations.rs +++ b/src/contract/operations.rs @@ -25,7 +25,7 @@ use std::iter; use amplify::confinement::{SmallBlob, TinyOrdMap, TinyOrdSet}; use amplify::Wrapper; -use commit_verify::{CommitId, Conceal}; +use commit_verify::{CommitId, Conceal, MerkleHash, MerkleLeaves, StrictHash}; use strict_encoding::{StrictDeserialize, StrictEncode, StrictSerialize}; use crate::schema::{self, ExtensionType, OpFullType, OpType, SchemaId, TransitionType}; @@ -39,10 +39,26 @@ use crate::{ #[wrapper_mut(DerefMut)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] +#[derive(CommitEncode)] +#[commit_encode(strategy = strict, id = StrictHash)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") + serde(crate = "serde_crate", transparent) +)] +pub struct Metadata(SmallBlob); + +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] +#[wrapper(Deref)] +#[wrapper_mut(DerefMut)] +#[derive(StrictType, StrictEncode, StrictDecode)] +#[strict_type(lib = LIB_NAME_RGB)] +#[derive(CommitEncode)] +#[commit_encode(strategy = strict, id = StrictHash)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) )] pub struct Valencies(TinyOrdSet); @@ -58,10 +74,12 @@ impl<'a> IntoIterator for &'a Valencies { #[wrapper_mut(DerefMut)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] +#[derive(CommitEncode)] +#[commit_encode(strategy = strict, id = StrictHash)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") + serde(crate = "serde_crate", transparent) )] pub struct Redeemed(TinyOrdMap); @@ -80,7 +98,7 @@ impl<'a> IntoIterator for &'a Redeemed { #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "camelCase") + serde(crate = "serde_crate", transparent) )] pub struct Inputs(TinyOrdSet); @@ -91,9 +109,18 @@ impl<'a> IntoIterator for &'a Inputs { fn into_iter(self) -> Self::IntoIter { self.0.iter().copied() } } +impl MerkleLeaves for Inputs { + type Leaf = Input; + type LeafIter<'tmp> = as MerkleLeaves>::LeafIter<'tmp>; + + fn merkle_leaves(&self) -> Self::LeafIter<'_> { self.0.merkle_leaves() } +} + #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] +#[derive(CommitEncode)] +#[commit_encode(strategy = strict, id = MerkleHash)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), @@ -179,9 +206,8 @@ pub struct Genesis { pub schema_id: SchemaId, pub testnet: bool, pub alt_layers1: AltLayer1Set, - pub metadata: SmallBlob, + pub metadata: Metadata, pub globals: GlobalState, - // TODO: Merklize assignments for the commitment pub assignments: Assignments, pub valencies: Valencies, } @@ -203,9 +229,8 @@ pub struct Extension { pub ffv: Ffv, pub contract_id: ContractId, pub extension_type: ExtensionType, - pub metadata: SmallBlob, + pub metadata: Metadata, pub globals: GlobalState, - // TODO: Merklize assignments for the commitment pub assignments: Assignments, pub redeemed: Redeemed, pub valencies: Valencies, @@ -228,10 +253,9 @@ pub struct Transition { pub ffv: Ffv, pub contract_id: ContractId, pub transition_type: TransitionType, - pub metadata: SmallBlob, + pub metadata: Metadata, pub globals: GlobalState, pub inputs: Inputs, - // TODO: Merklize assignments for the commitment pub assignments: Assignments, pub valencies: Valencies, } diff --git a/src/contract/xchain.rs b/src/contract/xchain.rs index 0beb92d6..51c696b4 100644 --- a/src/contract/xchain.rs +++ b/src/contract/xchain.rs @@ -27,7 +27,7 @@ use std::{fmt, io}; use amplify::confinement::TinyOrdSet; use bp::{Bp, Outpoint}; -use commit_verify::Conceal; +use commit_verify::{Conceal, StrictHash}; use strict_encoding::{ DecodeError, DefineUnion, ReadTuple, ReadUnion, StrictDecode, StrictDumb, StrictEncode, StrictSum, StrictType, StrictUnion, TypedRead, TypedWrite, WriteUnion, @@ -81,6 +81,8 @@ impl AltLayer1 { #[wrapper_mut(DerefMut)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB)] +#[derive(CommitEncode)] +#[commit_encode(strategy = strict, id = StrictHash)] #[cfg_attr( feature = "serde", derive(Serialize, Deserialize), diff --git a/src/stl.rs b/src/stl.rs index a016b2a2..7348a90d 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -28,11 +28,13 @@ use strict_types::stl::{std_stl, strict_types_stl}; use strict_types::typelib::LibBuilder; use strict_types::{CompileError, TypeLib}; -use crate::{AnchoredBundle, ContractState, Extension, Genesis, SubSchema, LIB_NAME_RGB}; +use crate::{ + AnchoredBundle, ContractState, Extension, Genesis, OpCommitment, SubSchema, LIB_NAME_RGB, +}; /// Strict types id for the library providing data types for RGB consensus. pub const LIB_ID_RGB: &str = - "urn:ubideco:stl:2YfZKdnGf9Gibvh15WcNSqe3xN7WH1143j1gCbDgecvG#axiom-detail-panther"; + "urn:ubideco:stl:BQugurBw6JSc3iF92RZipSMbFgsRU9M6jkYjSctpMzX2#ecology-nobel-prince"; fn _rgb_core_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! { @@ -48,6 +50,7 @@ fn _rgb_core_stl() -> Result { .transpile::() .transpile::() .transpile::() + .transpile::() .compile() }