rgbstd/interface/
iface.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::cmp::Ordering;
23use std::fmt::{self, Display, Formatter};
24use std::str::FromStr;
25
26use amplify::confinement::{TinyOrdMap, TinyOrdSet};
27use amplify::{ByteArray, Bytes32};
28use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32};
29use commit_verify::{CommitStrategy, CommitmentId};
30use rgb::Occurrences;
31use strict_encoding::{
32    FieldName, StrictDecode, StrictDeserialize, StrictDumb, StrictEncode, StrictSerialize,
33    StrictType, TypeName,
34};
35use strict_types::{SemId, TypeSystem};
36
37use crate::interface::VerNo;
38use crate::LIB_NAME_RGB_STD;
39
40/// Interface identifier.
41///
42/// Interface identifier commits to all of the interface data.
43#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
44#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)]
45#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
46#[strict_type(lib = LIB_NAME_RGB_STD)]
47#[cfg_attr(
48    feature = "serde",
49    derive(Serialize, Deserialize),
50    serde(crate = "serde_crate", transparent)
51)]
52pub struct IfaceId(
53    #[from]
54    #[from([u8; 32])]
55    Bytes32,
56);
57
58impl ToBaid58<32> for IfaceId {
59    const HRI: &'static str = "if";
60    const CHUNKING: Option<Chunking> = CHUNKING_32;
61    fn to_baid58_payload(&self) -> [u8; 32] { self.to_byte_array() }
62    fn to_baid58_string(&self) -> String { self.to_string() }
63}
64impl FromBaid58<32> for IfaceId {}
65impl Display for IfaceId {
66    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
67        if !f.alternate() {
68            f.write_str("urn:lnp-bp:if:")?;
69        }
70        if f.sign_minus() {
71            write!(f, "{:.2}", self.to_baid58())
72        } else {
73            write!(f, "{:#.2}", self.to_baid58())
74        }
75    }
76}
77impl FromStr for IfaceId {
78    type Err = Baid58ParseError;
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        Self::from_baid58_maybe_chunked_str(s.trim_start_matches("urn:lnp-bp:"), ':', '#')
81    }
82}
83impl IfaceId {
84    pub fn to_mnemonic(&self) -> String { self.to_baid58().mnemonic() }
85}
86
87#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
88pub enum Req {
89    Optional,
90    Required,
91    NoneOrMore,
92    OneOrMore,
93}
94
95impl Req {
96    pub fn is_required(self) -> bool { self == Req::Required || self == Req::OneOrMore }
97    pub fn is_multiple(self) -> bool { self == Req::NoneOrMore || self == Req::OneOrMore }
98}
99
100#[derive(Clone, PartialEq, Eq, Hash, Debug)]
101#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
102#[strict_type(lib = LIB_NAME_RGB_STD)]
103#[cfg_attr(
104    feature = "serde",
105    derive(Serialize, Deserialize),
106    serde(crate = "serde_crate", rename_all = "camelCase")
107)]
108pub struct ValencyIface {
109    pub required: bool,
110    pub multiple: bool,
111}
112
113#[derive(Clone, PartialEq, Eq, Hash, Debug)]
114#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
115#[strict_type(lib = LIB_NAME_RGB_STD)]
116#[cfg_attr(
117    feature = "serde",
118    derive(Serialize, Deserialize),
119    serde(crate = "serde_crate", rename_all = "camelCase")
120)]
121pub struct GlobalIface {
122    pub sem_id: Option<SemId>,
123    pub required: bool,
124    pub multiple: bool,
125}
126
127impl GlobalIface {
128    pub fn any(req: Req) -> Self {
129        GlobalIface {
130            sem_id: None,
131            required: req.is_required(),
132            multiple: req.is_multiple(),
133        }
134    }
135    pub fn optional(sem_id: SemId) -> Self {
136        GlobalIface {
137            sem_id: Some(sem_id),
138            required: false,
139            multiple: false,
140        }
141    }
142    pub fn required(sem_id: SemId) -> Self {
143        GlobalIface {
144            sem_id: Some(sem_id),
145            required: true,
146            multiple: false,
147        }
148    }
149    pub fn none_or_many(sem_id: SemId) -> Self {
150        GlobalIface {
151            sem_id: Some(sem_id),
152            required: false,
153            multiple: true,
154        }
155    }
156    pub fn one_or_many(sem_id: SemId) -> Self {
157        GlobalIface {
158            sem_id: Some(sem_id),
159            required: true,
160            multiple: true,
161        }
162    }
163}
164
165#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
166#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
167#[strict_type(lib = LIB_NAME_RGB_STD, tags = order)]
168#[cfg_attr(
169    feature = "serde",
170    derive(Serialize, Deserialize),
171    serde(crate = "serde_crate", rename_all = "camelCase")
172)]
173pub struct AssignIface {
174    pub owned_state: OwnedIface,
175    pub public: bool,
176    pub required: bool,
177    pub multiple: bool,
178}
179
180impl AssignIface {
181    pub fn public(owned_state: OwnedIface, req: Req) -> Self {
182        AssignIface {
183            owned_state,
184            public: true,
185            required: req.is_required(),
186            multiple: req.is_multiple(),
187        }
188    }
189
190    pub fn private(owned_state: OwnedIface, req: Req) -> Self {
191        AssignIface {
192            owned_state,
193            public: false,
194            required: req.is_required(),
195            multiple: req.is_multiple(),
196        }
197    }
198}
199
200#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
201#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
202#[strict_type(lib = LIB_NAME_RGB_STD, tags = order)]
203#[cfg_attr(
204    feature = "serde",
205    derive(Serialize, Deserialize),
206    serde(crate = "serde_crate", rename_all = "camelCase")
207)]
208pub enum OwnedIface {
209    #[strict_type(dumb)]
210    Any,
211    Rights,
212    Amount,
213    AnyData,
214    AnyAttach,
215    Data(SemId),
216}
217
218pub type ArgMap = TinyOrdMap<FieldName, ArgSpec>;
219
220/// Structure providing information about state inputs and outputs for an RGB
221/// operation.
222#[derive(Clone, PartialEq, Eq, Debug, Default)]
223#[derive(StrictType, StrictEncode, StrictDecode)]
224#[strict_type(lib = LIB_NAME_RGB_STD)]
225#[cfg_attr(
226    feature = "serde",
227    derive(Serialize, Deserialize),
228    serde(crate = "serde_crate", rename_all = "camelCase")
229)]
230pub struct ArgSpec {
231    /// The name of the state field from the owned or global state fields
232    /// defined in the interface. Used only if this name is different from the
233    /// alias provided as [`ArgMap`] key.
234    pub name: Option<FieldName>,
235    /// Maximal number of occurrences of the input or output of this type.
236    pub req: Occurrences,
237}
238
239impl ArgSpec {
240    pub fn new(req: Occurrences) -> Self { ArgSpec { name: None, req } }
241
242    pub fn required() -> Self { ArgSpec::new(Occurrences::Once) }
243
244    pub fn optional() -> Self { ArgSpec::new(Occurrences::NoneOrOnce) }
245
246    pub fn non_empty() -> Self { ArgSpec::new(Occurrences::OnceOrMore) }
247
248    pub fn many() -> Self { ArgSpec::new(Occurrences::NoneOrMore) }
249
250    pub fn with(name: &'static str, req: Occurrences) -> Self {
251        ArgSpec {
252            name: Some(FieldName::from(name)),
253            req,
254        }
255    }
256
257    pub fn from_required(name: &'static str) -> Self { ArgSpec::with(name, Occurrences::Once) }
258
259    pub fn from_optional(name: &'static str) -> Self {
260        ArgSpec::with(name, Occurrences::NoneOrOnce)
261    }
262
263    pub fn from_non_empty(name: &'static str) -> Self {
264        ArgSpec::with(name, Occurrences::OnceOrMore)
265    }
266
267    pub fn from_many(name: &'static str) -> Self { ArgSpec::with(name, Occurrences::NoneOrMore) }
268}
269
270#[derive(Clone, PartialEq, Eq, Debug)]
271#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
272#[strict_type(lib = LIB_NAME_RGB_STD)]
273#[cfg_attr(
274    feature = "serde",
275    derive(Serialize, Deserialize),
276    serde(crate = "serde_crate", rename_all = "camelCase")
277)]
278pub struct GenesisIface {
279    pub metadata: Option<SemId>,
280    pub global: ArgMap,
281    pub assignments: ArgMap,
282    pub valencies: ArgMap,
283    pub errors: TinyOrdSet<u8>,
284}
285
286#[derive(Clone, PartialEq, Eq, Debug)]
287#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
288#[strict_type(lib = LIB_NAME_RGB_STD)]
289#[cfg_attr(
290    feature = "serde",
291    derive(Serialize, Deserialize),
292    serde(crate = "serde_crate", rename_all = "camelCase")
293)]
294pub struct ExtensionIface {
295    pub metadata: Option<SemId>,
296    pub globals: ArgMap,
297    pub redeems: ArgMap,
298    pub assignments: ArgMap,
299    pub valencies: ArgMap,
300    pub errors: TinyOrdSet<u8>,
301}
302
303#[derive(Clone, PartialEq, Eq, Debug)]
304#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
305#[strict_type(lib = LIB_NAME_RGB_STD)]
306#[cfg_attr(
307    feature = "serde",
308    derive(Serialize, Deserialize),
309    serde(crate = "serde_crate", rename_all = "camelCase")
310)]
311pub struct TransitionIface {
312    /// Defines whence schema may omit providing this operation.
313    pub optional: bool,
314    pub metadata: Option<SemId>,
315    pub globals: ArgMap,
316    pub inputs: ArgMap,
317    pub assignments: ArgMap,
318    pub valencies: ArgMap,
319    pub errors: TinyOrdSet<u8>,
320    pub default_assignment: Option<FieldName>,
321}
322
323/// Interface definition.
324#[derive(Clone, Eq, Debug)]
325#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
326#[strict_type(lib = LIB_NAME_RGB_STD)]
327#[cfg_attr(
328    feature = "serde",
329    derive(Serialize, Deserialize),
330    serde(crate = "serde_crate", rename_all = "camelCase")
331)]
332pub struct Iface {
333    pub version: VerNo,
334    pub name: TypeName,
335    pub global_state: TinyOrdMap<FieldName, GlobalIface>,
336    pub assignments: TinyOrdMap<FieldName, AssignIface>,
337    pub valencies: TinyOrdMap<FieldName, ValencyIface>,
338    pub genesis: GenesisIface,
339    pub transitions: TinyOrdMap<TypeName, TransitionIface>,
340    pub extensions: TinyOrdMap<TypeName, ExtensionIface>,
341    pub error_type: SemId,
342    pub default_operation: Option<TypeName>,
343    pub type_system: TypeSystem,
344}
345
346impl PartialEq for Iface {
347    fn eq(&self, other: &Self) -> bool { self.iface_id() == other.iface_id() }
348}
349
350impl Ord for Iface {
351    fn cmp(&self, other: &Self) -> Ordering { self.iface_id().cmp(&other.iface_id()) }
352}
353
354impl PartialOrd for Iface {
355    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
356}
357
358impl CommitStrategy for Iface {
359    type Strategy = commit_verify::strategies::Strict;
360}
361
362impl CommitmentId for Iface {
363    const TAG: [u8; 32] = *b"urn:lnpbp:rgb:interface:v01#2303";
364    type Id = IfaceId;
365}
366
367impl StrictSerialize for Iface {}
368impl StrictDeserialize for Iface {}
369
370impl Iface {
371    #[inline]
372    pub fn iface_id(&self) -> IfaceId { self.commitment_id() }
373}