rgbstd/interface/
iimpl.rs

1// RGB standard library for working with smart contracts on Bitcoin & Lightning
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2023 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved.
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::fmt::{self, Display, Formatter};
23use std::str::FromStr;
24
25use amplify::confinement::{TinyOrdMap, TinyOrdSet};
26use amplify::{ByteArray, Bytes32};
27use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32};
28use commit_verify::{CommitStrategy, CommitmentId};
29use rgb::{
30    AssignmentType, ExtensionType, GlobalStateType, SchemaId, SchemaTypeIndex, Script, SubSchema,
31    TransitionType, ValencyType,
32};
33use strict_encoding::{FieldName, TypeName};
34use strict_types::encoding::{
35    StrictDecode, StrictDeserialize, StrictEncode, StrictSerialize, StrictType,
36};
37
38use crate::interface::iface::IfaceId;
39use crate::interface::{Iface, VerNo};
40use crate::{ReservedBytes, LIB_NAME_RGB_STD};
41
42/// Interface identifier.
43///
44/// Interface identifier commits to all of the interface data.
45#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
46#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)]
47#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
48#[strict_type(lib = LIB_NAME_RGB_STD)]
49#[cfg_attr(
50    feature = "serde",
51    derive(Serialize, Deserialize),
52    serde(crate = "serde_crate", transparent)
53)]
54pub struct ImplId(
55    #[from]
56    #[from([u8; 32])]
57    Bytes32,
58);
59
60impl ToBaid58<32> for ImplId {
61    const HRI: &'static str = "im";
62    const CHUNKING: Option<Chunking> = CHUNKING_32;
63    fn to_baid58_payload(&self) -> [u8; 32] { self.to_byte_array() }
64    fn to_baid58_string(&self) -> String { self.to_string() }
65}
66impl FromBaid58<32> for ImplId {}
67impl Display for ImplId {
68    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
69        if !f.alternate() {
70            f.write_str("urn:lnp-bp:im:")?;
71        }
72        if f.sign_minus() {
73            write!(f, "{:.2}", self.to_baid58())
74        } else {
75            write!(f, "{:#.2}", self.to_baid58())
76        }
77    }
78}
79impl FromStr for ImplId {
80    type Err = Baid58ParseError;
81    fn from_str(s: &str) -> Result<Self, Self::Err> {
82        Self::from_baid58_maybe_chunked_str(s.trim_start_matches("urn:lnp-bp:"), ':', '#')
83    }
84}
85impl ImplId {
86    pub fn to_mnemonic(&self) -> String { self.to_baid58().mnemonic() }
87}
88
89/// Maps certain form of type id (global or owned state or a valency) to a
90/// human-readable name.
91///
92/// Two distinct [`NamedField`] objects must always have both different state
93/// ids and names.   
94#[derive(Clone, Eq, PartialOrd, Ord, Debug)]
95#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
96#[strict_type(lib = LIB_NAME_RGB_STD)]
97#[cfg_attr(
98    feature = "serde",
99    derive(Serialize, Deserialize),
100    serde(crate = "serde_crate", rename_all = "camelCase")
101)]
102pub struct NamedField<T: SchemaTypeIndex> {
103    pub id: T,
104    pub name: FieldName,
105    /// Reserved bytes for storing information about value transformation
106    /// procedures
107    pub reserved: ReservedBytes<0u8, 4usize>,
108}
109
110impl<T> PartialEq for NamedField<T>
111where T: SchemaTypeIndex
112{
113    fn eq(&self, other: &Self) -> bool { self.id == other.id || self.name == other.name }
114}
115
116impl<T: SchemaTypeIndex> NamedField<T> {
117    pub fn with(id: T, name: FieldName) -> NamedField<T> {
118        NamedField {
119            id,
120            name,
121            reserved: default!(),
122        }
123    }
124}
125
126/// Maps operation numeric type id to a human-readable name.
127///
128/// Two distinct [`NamedType`] objects must always have both different state
129/// ids and names.   
130#[derive(Clone, Eq, PartialOrd, Ord, Debug)]
131#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
132#[strict_type(lib = LIB_NAME_RGB_STD)]
133#[cfg_attr(
134    feature = "serde",
135    derive(Serialize, Deserialize),
136    serde(crate = "serde_crate", rename_all = "camelCase")
137)]
138pub struct NamedType<T: SchemaTypeIndex> {
139    pub id: T,
140    pub name: TypeName,
141    /// Reserved bytes for storing information about adaptor procedures
142    pub reserved: ReservedBytes<0, 4>,
143}
144
145impl<T> PartialEq for NamedType<T>
146where T: SchemaTypeIndex
147{
148    fn eq(&self, other: &Self) -> bool { self.id == other.id || self.name == other.name }
149}
150
151impl<T: SchemaTypeIndex> NamedType<T> {
152    pub fn with(id: T, name: TypeName) -> NamedType<T> {
153        NamedType {
154            id,
155            name,
156            reserved: default!(),
157        }
158    }
159}
160
161#[derive(Clone, Eq, PartialEq, Debug)]
162#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
163#[strict_type(lib = LIB_NAME_RGB_STD)]
164pub struct SchemaIfaces {
165    pub schema: SubSchema,
166    pub iimpls: TinyOrdMap<IfaceId, IfaceImpl>,
167}
168
169impl SchemaIfaces {
170    pub fn new(schema: SubSchema) -> Self {
171        SchemaIfaces {
172            schema,
173            iimpls: none!(),
174        }
175    }
176}
177
178/// Interface implementation for some specific schema.
179#[derive(Clone, Eq, PartialEq, Debug)]
180#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
181#[strict_type(lib = LIB_NAME_RGB_STD)]
182#[cfg_attr(
183    feature = "serde",
184    derive(Serialize, Deserialize),
185    serde(crate = "serde_crate", rename_all = "camelCase")
186)]
187pub struct IfaceImpl {
188    pub version: VerNo,
189    pub schema_id: SchemaId,
190    pub iface_id: IfaceId,
191    pub global_state: TinyOrdSet<NamedField<GlobalStateType>>,
192    pub assignments: TinyOrdSet<NamedField<AssignmentType>>,
193    pub valencies: TinyOrdSet<NamedField<ValencyType>>,
194    pub transitions: TinyOrdSet<NamedType<TransitionType>>,
195    pub extensions: TinyOrdSet<NamedField<ExtensionType>>,
196    pub script: Script,
197}
198
199impl CommitStrategy for IfaceImpl {
200    type Strategy = commit_verify::strategies::Strict;
201}
202
203impl CommitmentId for IfaceImpl {
204    const TAG: [u8; 32] = *b"urn:lnpbp:rgb:ifaceimpl:v01#2303";
205    type Id = ImplId;
206}
207
208impl StrictSerialize for IfaceImpl {}
209impl StrictDeserialize for IfaceImpl {}
210
211impl IfaceImpl {
212    #[inline]
213    pub fn impl_id(&self) -> ImplId { self.commitment_id() }
214
215    pub fn global_type(&self, name: &FieldName) -> Option<GlobalStateType> {
216        self.global_state
217            .iter()
218            .find(|nt| &nt.name == name)
219            .map(|nt| nt.id)
220    }
221
222    pub fn assignments_type(&self, name: &FieldName) -> Option<AssignmentType> {
223        self.assignments
224            .iter()
225            .find(|nt| &nt.name == name)
226            .map(|nt| nt.id)
227    }
228
229    pub fn transition_type(&self, name: &TypeName) -> Option<TransitionType> {
230        self.transitions
231            .iter()
232            .find(|nt| &nt.name == name)
233            .map(|nt| nt.id)
234    }
235
236    pub fn global_name(&self, id: GlobalStateType) -> Option<&FieldName> {
237        self.global_state
238            .iter()
239            .find(|nt| nt.id == id)
240            .map(|nt| &nt.name)
241    }
242
243    pub fn assignment_name(&self, id: AssignmentType) -> Option<&FieldName> {
244        self.assignments
245            .iter()
246            .find(|nt| nt.id == id)
247            .map(|nt| &nt.name)
248    }
249
250    pub fn transition_name(&self, id: TransitionType) -> Option<&TypeName> {
251        self.transitions
252            .iter()
253            .find(|nt| nt.id == id)
254            .map(|nt| &nt.name)
255    }
256}
257
258// TODO: Implement validation of implementation against interface requirements
259
260#[derive(Clone, Eq, PartialEq, Debug)]
261#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
262#[strict_type(lib = LIB_NAME_RGB_STD)]
263#[cfg_attr(
264    feature = "serde",
265    derive(Serialize, Deserialize),
266    serde(crate = "serde_crate", rename_all = "camelCase")
267)]
268pub struct IfacePair {
269    pub iface: Iface,
270    pub iimpl: IfaceImpl,
271}
272
273impl IfacePair {
274    pub fn with(iface: Iface, iimpl: IfaceImpl) -> IfacePair { IfacePair { iface, iimpl } }
275
276    pub fn iface_id(&self) -> IfaceId { self.iface.iface_id() }
277    pub fn impl_id(&self) -> ImplId { self.iimpl.impl_id() }
278    pub fn global_type(&self, name: &FieldName) -> Option<GlobalStateType> {
279        self.iimpl.global_type(name)
280    }
281    pub fn assignments_type(&self, name: &FieldName) -> Option<AssignmentType> {
282        self.iimpl.assignments_type(name)
283    }
284    pub fn transition_type(&self, name: &TypeName) -> Option<TransitionType> {
285        self.iimpl.transition_type(name)
286    }
287}