rgbcore/
seals.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// RGB Core Library: consensus layer for RGB smart contracts.
//
// SPDX-License-Identifier: Apache-2.0
//
// Designed in 2019-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
//
// Copyright (C) 2019-2024 LNP/BP Standards Association, Switzerland.
// Copyright (C) 2024-2025 LNP/BP Laboratories,
//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
// Copyright (C) 2025 RGB Consortium, Switzerland.
// Copyright (C) 2019-2025 Dr Maxim Orlovsky.
// All rights under the above copyrights are reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under
// the License.

use core::str::FromStr;

use bp::seals::mmb;
use single_use_seals::SingleUseSeal;
use ultrasonic::AuthToken;

pub trait SealAuthToken {
    fn auth_token(&self) -> AuthToken;
}

pub trait RgbSeal: SingleUseSeal<Message = mmb::Message> + SealAuthToken {}

// Below are capabilities constants used in the standard library:

#[cfg(feature = "bitcoin")]
pub const BITCOIN_OPRET: u32 = 0x0001_0001_u32;
#[cfg(feature = "bitcoin")]
pub const BITCOIN_TAPRET: u32 = 0x0001_0002_u32;
#[cfg(feature = "liquid")]
pub const LIQUID_OPRET: u32 = 0x0002_0001_u32;
#[cfg(feature = "liquid")]
pub const LIQUID_TAPRET: u32 = 0x0002_0002_u32;
#[cfg(feature = "prime")]
pub const PRIME_SEALS: u32 = 0x0010_0001_u32;

#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u32)]
pub enum SealType {
    #[cfg(feature = "bitcoin")]
    #[display("bcor")]
    BitcoinOpret = BITCOIN_OPRET,

    #[cfg(feature = "bitcoin")]
    #[display("bctr")]
    BitcoinTapret = BITCOIN_TAPRET,

    #[cfg(feature = "liquid")]
    #[display("lqor")]
    LiquidOpret = LIQUID_OPRET,

    #[cfg(feature = "liquid")]
    #[display("lqtr")]
    LiquidTapret = LIQUID_TAPRET,

    #[cfg(feature = "prime")]
    #[display("prime")]
    Prime = PRIME_SEALS,
}

#[derive(Clone, Eq, PartialEq, Debug, Display, Error)]
#[display("unknown seal type `{0}`")]
pub struct UnknownType(String);

impl FromStr for SealType {
    type Err = UnknownType;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            #[cfg(feature = "bitcoin")]
            "bcor" => Ok(SealType::BitcoinOpret),
            #[cfg(feature = "bitcoin")]
            "bctr" => Ok(SealType::BitcoinTapret),
            #[cfg(feature = "liquid")]
            "lqtr" => Ok(SealType::LiquidTapret),
            #[cfg(feature = "prime")]
            "prime" => Ok(SealType::Prime),
            _ => Err(UnknownType(s.to_string())),
        }
    }
}

impl TryFrom<u32> for SealType {
    type Error = UnknownType;
    fn try_from(caps: u32) -> Result<Self, Self::Error> {
        Ok(match caps {
            #[cfg(feature = "bitcoin")]
            BITCOIN_OPRET => Self::BitcoinOpret,
            #[cfg(feature = "bitcoin")]
            BITCOIN_TAPRET => Self::BitcoinTapret,
            #[cfg(feature = "liquid")]
            LIQUID_TAPRET => Self::LiquidTapret,
            #[cfg(feature = "prime")]
            PRIME_SEALS => Self::Prime,
            unknown => return Err(UnknownType(format!("unknown seal type {unknown:#10x}"))),
        })
    }
}

#[cfg(any(feature = "bitcoin", feature = "liquid"))]
pub mod bitcoin {
    use bp::dbc;
    use bp::dbc::opret::OpretProof;
    use bp::dbc::tapret::TapretProof;
    use bp::seals::{TxoSeal, TxoSealDef};
    use commit_verify::CommitId;

    use super::*;

    pub type OpretSeal = TxoSeal<OpretProof>;
    pub type TapretSeal = TxoSeal<TapretProof>;

    impl<D: dbc::Proof> SealAuthToken for TxoSeal<D> {
        fn auth_token(&self) -> AuthToken { self.to_definition().auth_token() }
    }

    impl<D: dbc::Proof> RgbSeal for TxoSeal<D> {}

    impl SealAuthToken for TxoSealDef {
        // SECURITY: Here we cut SHA256 tagged hash of a single-use seal definition to 30 bytes in order
        // to fit it into a field element with no overflows. This must be a secure operation since we
        // still have a sufficient 120-bit collision resistance.
        fn auth_token(&self) -> AuthToken {
            let id = self.commit_id().to_byte_array();
            let mut shortened_id = [0u8; 30];
            shortened_id.copy_from_slice(&id[0..30]);
            AuthToken::from_byte_array(shortened_id)
        }
    }
}