1use std::collections::BTreeSet;
24use std::fmt::{Display, Formatter};
25use std::str::FromStr;
26use std::{fmt, vec};
27
28use amplify::confinement::{Confined, MediumOrdMap, U16 as U16MAX};
29use amplify::hex::{FromHex, ToHex};
30use amplify::num::u256;
31use amplify::{hex, ByteArray, Bytes32, FromSliceError, Wrapper};
32use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str};
33use commit_verify::{
34 mpc, CommitEncode, CommitEngine, CommitId, CommitmentId, Conceal, DigestExt, MerkleHash,
35 MerkleLeaves, Sha256, StrictHash,
36};
37use strict_encoding::StrictDumb;
38
39use crate::{
40 impl_serde_baid64, Assign, AssignmentType, Assignments, BundleId, ChainNet, ExposedSeal,
41 ExposedState, Ffv, Genesis, GlobalState, GlobalStateType, Operation, RevealedData,
42 RevealedState, RevealedValue, SchemaId, SealClosingStrategy, SecretSeal, Transition,
43 TransitionBundle, TransitionType, TypedAssigns, LIB_NAME_RGB_COMMIT,
44};
45
46#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
48#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)]
49#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
50#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
51pub struct ContractId(
52 #[from]
53 #[from([u8; 32])]
54 Bytes32,
55);
56
57impl PartialEq<OpId> for ContractId {
58 fn eq(&self, other: &OpId) -> bool { self.to_byte_array() == other.to_byte_array() }
59}
60impl PartialEq<ContractId> for OpId {
61 fn eq(&self, other: &ContractId) -> bool { self.to_byte_array() == other.to_byte_array() }
62}
63
64impl ContractId {
65 pub fn copy_from_slice(slice: impl AsRef<[u8]>) -> Result<Self, FromSliceError> {
66 Bytes32::copy_from_slice(slice).map(Self)
67 }
68}
69
70impl DisplayBaid64 for ContractId {
71 const HRI: &'static str = "rgb";
72 const CHUNKING: bool = true;
73 const PREFIX: bool = true;
74 const EMBED_CHECKSUM: bool = false;
75 const MNEMONIC: bool = false;
76 fn to_baid64_payload(&self) -> [u8; 32] { self.to_byte_array() }
77}
78impl FromBaid64Str for ContractId {}
79impl FromStr for ContractId {
80 type Err = Baid64ParseError;
81 fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_baid64_str(s) }
82}
83impl Display for ContractId {
84 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) }
85}
86
87impl From<mpc::ProtocolId> for ContractId {
88 fn from(id: mpc::ProtocolId) -> Self { ContractId(id.into_inner()) }
89}
90
91impl From<ContractId> for mpc::ProtocolId {
92 fn from(id: ContractId) -> Self { mpc::ProtocolId::from_inner(id.into_inner()) }
93}
94
95impl_serde_baid64!(ContractId);
96
97#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)]
100#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)]
101#[display(Self::to_hex)]
102#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
103#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
104#[cfg_attr(
105 feature = "serde",
106 derive(Serialize, Deserialize),
107 serde(crate = "serde_crate", transparent)
108)]
109pub struct OpId(
110 #[from]
111 #[from([u8; 32])]
112 Bytes32,
113);
114
115impl From<Sha256> for OpId {
116 fn from(hasher: Sha256) -> Self { hasher.finish().into() }
117}
118
119impl CommitmentId for OpId {
120 const TAG: &'static str = "urn:lnp-bp:rgb:operation#2024-02-03";
121}
122
123impl FromStr for OpId {
124 type Err = hex::Error;
125 fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_hex(s) }
126}
127
128impl OpId {
129 pub fn copy_from_slice(slice: impl AsRef<[u8]>) -> Result<Self, FromSliceError> {
130 Bytes32::copy_from_slice(slice).map(Self)
131 }
132}
133
134#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)]
137#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)]
138#[display(Self::to_hex)]
139#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
140#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
141#[cfg_attr(
142 feature = "serde",
143 derive(Serialize, Deserialize),
144 serde(crate = "serde_crate", transparent)
145)]
146pub struct DiscloseHash(
147 #[from]
148 #[from([u8; 32])]
149 Bytes32,
150);
151
152impl From<Sha256> for DiscloseHash {
153 fn from(hasher: Sha256) -> Self { hasher.finish().into() }
154}
155
156impl CommitmentId for DiscloseHash {
157 const TAG: &'static str = "urn:lnp-bp:rgb:disclose#2024-02-16";
158}
159
160impl FromStr for DiscloseHash {
161 type Err = hex::Error;
162 fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_hex(s) }
163}
164
165impl DiscloseHash {
166 pub fn copy_from_slice(slice: impl AsRef<[u8]>) -> Result<Self, FromSliceError> {
167 Bytes32::copy_from_slice(slice).map(Self)
168 }
169}
170
171#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
172#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
173#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
174pub struct AssignmentIndex {
175 pub ty: AssignmentType,
176 pub pos: u16,
177}
178
179impl AssignmentIndex {
180 pub fn new(ty: AssignmentType, pos: u16) -> Self { AssignmentIndex { ty, pos } }
181}
182
183#[derive(Clone, Eq, PartialEq, Hash, Debug)]
184#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
185#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
186#[derive(CommitEncode)]
187#[commit_encode(strategy = strict, id = DiscloseHash)]
188pub struct OpDisclose {
189 pub id: OpId,
190 pub seals: MediumOrdMap<AssignmentIndex, SecretSeal>,
191 pub fungible: MediumOrdMap<AssignmentIndex, RevealedValue>,
192 pub data: MediumOrdMap<AssignmentIndex, RevealedData>,
193}
194
195#[derive(Clone, Eq, PartialEq, Hash, Debug)]
196#[derive(StrictType, StrictEncode, StrictDecode)]
197#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
198#[derive(CommitEncode)]
199#[commit_encode(strategy = strict, id = DiscloseHash)]
200pub struct BundleDisclosure {
201 pub id: BundleId,
202 pub known_transitions: Confined<BTreeSet<DiscloseHash>, 1, U16MAX>,
203}
204
205impl StrictDumb for BundleDisclosure {
206 fn strict_dumb() -> Self {
207 Self {
208 id: strict_dumb!(),
209 known_transitions: Confined::with(strict_dumb!()),
210 }
211 }
212}
213
214impl TransitionBundle {
215 pub fn disclose(&self) -> BundleDisclosure {
217 BundleDisclosure {
218 id: self.bundle_id(),
219 known_transitions: Confined::from_iter_checked(
220 self.known_transitions
221 .iter()
222 .map(|kt| kt.transition.disclose_hash()),
223 ),
224 }
225 }
226
227 pub fn disclose_hash(&self) -> DiscloseHash { self.disclose().commit_id() }
229}
230
231#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
232#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
233#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
234pub struct BaseCommitment {
235 pub schema_id: SchemaId,
236 pub timestamp: i64,
237 pub issuer: StrictHash,
238 pub chain_net: ChainNet,
239 pub seal_closing_strategy: SealClosingStrategy,
240}
241
242#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
243#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
244#[strict_type(lib = LIB_NAME_RGB_COMMIT, tags = custom, dumb = Self::Transition(strict_dumb!(), strict_dumb!()))]
245pub enum TypeCommitment {
246 #[strict_type(tag = 0)]
247 Genesis(BaseCommitment),
248
249 #[strict_type(tag = 1)]
250 Transition(ContractId, TransitionType),
251}
252
253#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
254#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
255#[strict_type(lib = LIB_NAME_RGB_COMMIT)]
256#[derive(CommitEncode)]
257#[commit_encode(strategy = strict, id = OpId)]
258pub struct OpCommitment {
259 pub ffv: Ffv,
260 pub nonce: u64,
261 pub op_type: TypeCommitment,
262 pub metadata: StrictHash,
263 pub globals: MerkleHash,
264 pub inputs: MerkleHash,
265 pub assignments: MerkleHash,
266}
267
268impl Genesis {
269 pub fn commit(&self) -> OpCommitment {
270 let base = BaseCommitment {
271 schema_id: self.schema_id,
272 timestamp: self.timestamp,
273 chain_net: self.chain_net,
274 seal_closing_strategy: self.seal_closing_strategy,
275 issuer: self.issuer.commit_id(),
276 };
277 OpCommitment {
278 ffv: self.ffv,
279 nonce: u64::MAX,
280 op_type: TypeCommitment::Genesis(base),
281 metadata: self.metadata.commit_id(),
282 globals: MerkleHash::merklize(&self.globals),
283 inputs: MerkleHash::void(0, u256::ZERO),
284 assignments: MerkleHash::merklize(&self.assignments),
285 }
286 }
287
288 pub fn disclose_hash(&self) -> DiscloseHash { self.disclose().commit_id() }
289}
290
291impl Transition {
292 pub fn commit(&self) -> OpCommitment {
293 OpCommitment {
294 ffv: self.ffv,
295 nonce: self.nonce,
296 op_type: TypeCommitment::Transition(self.contract_id, self.transition_type),
297 metadata: self.metadata.commit_id(),
298 globals: MerkleHash::merklize(&self.globals),
299 inputs: MerkleHash::merklize(&self.inputs),
300 assignments: MerkleHash::merklize(&self.assignments),
301 }
302 }
303}
304
305impl RevealedState {
306 fn commit_encode(&self, e: &mut CommitEngine) {
307 match self {
308 Self::Void => {}
309 Self::Fungible(val) => e.commit_to_serialized(&val),
310 Self::Structured(dat) => e.commit_to_serialized(dat),
311 }
312 }
313}
314
315#[derive(Clone, Eq, PartialEq, Debug)]
316pub struct AssignmentCommitment {
317 pub ty: AssignmentType,
318 pub state: RevealedState,
319 pub seal: SecretSeal,
320}
321
322impl CommitEncode for AssignmentCommitment {
323 type CommitmentId = MerkleHash;
324
325 fn commit_encode(&self, e: &mut CommitEngine) {
326 e.commit_to_serialized(&self.ty);
327 self.state.commit_encode(e);
328 e.commit_to_serialized(&self.seal);
329 e.set_finished();
330 }
331}
332
333impl<State: ExposedState, Seal: ExposedSeal> Assign<State, Seal> {
334 pub fn commitment(&self, ty: AssignmentType) -> AssignmentCommitment {
335 let Self::ConfidentialSeal { seal, state } = self.conceal() else {
336 unreachable!();
337 };
338 AssignmentCommitment {
339 ty,
340 state: state.state_data(),
341 seal,
342 }
343 }
344}
345
346impl<Seal: ExposedSeal> MerkleLeaves for Assignments<Seal> {
347 type Leaf = AssignmentCommitment;
348 type LeafIter<'tmp>
349 = vec::IntoIter<AssignmentCommitment>
350 where Seal: 'tmp;
351
352 fn merkle_leaves(&self) -> Self::LeafIter<'_> {
353 self.iter()
354 .flat_map(|(ty, a)| {
355 match a {
356 TypedAssigns::Declarative(list) => {
357 list.iter().map(|a| a.commitment(*ty)).collect::<Vec<_>>()
358 }
359 TypedAssigns::Fungible(list) => {
360 list.iter().map(|a| a.commitment(*ty)).collect()
361 }
362 TypedAssigns::Structured(list) => {
363 list.iter().map(|a| a.commitment(*ty)).collect()
364 }
365 }
366 .into_iter()
367 })
368 .collect::<Vec<_>>()
369 .into_iter()
370 }
371}
372
373#[derive(Clone, Eq, PartialEq, Hash, Debug)]
374pub struct GlobalCommitment {
375 pub ty: GlobalStateType,
376 pub state: RevealedData,
377}
378
379impl CommitEncode for GlobalCommitment {
380 type CommitmentId = MerkleHash;
381
382 fn commit_encode(&self, e: &mut CommitEngine) {
383 e.commit_to_serialized(&self.ty);
384 e.commit_to_serialized(&self.state);
385 e.set_finished();
386 }
387}
388
389impl MerkleLeaves for GlobalState {
390 type Leaf = GlobalCommitment;
391 type LeafIter<'tmp> = vec::IntoIter<GlobalCommitment>;
392
393 fn merkle_leaves(&self) -> Self::LeafIter<'_> {
394 self.iter()
395 .flat_map(|(ty, list)| {
396 list.iter().map(|val| GlobalCommitment {
397 ty: *ty,
398 state: val.clone(),
399 })
400 })
401 .collect::<Vec<_>>()
402 .into_iter()
403 }
404}