1use std::ops::Deref;
23use std::str::FromStr;
24
25use indexmap::IndexMap;
26use rgb::bitcoin::hashes::{hash160, sha256};
27use rgb::bitcoin::key::{TweakedPublicKey, UntweakedPublicKey};
28use rgb::bitcoin::{KnownHrp, Network, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash};
29use rgb::{ChainNet, ContractId, Layer1, SchemaId, SecretSeal, StateType};
30use strict_types::FieldName;
31
32use crate::parse::AddressPayload;
33use crate::{Amount, NonFungible};
34
35#[derive(Clone, Eq, PartialEq, Hash, Debug)]
36#[non_exhaustive]
37pub enum RgbTransport {
38 JsonRpc { tls: bool, host: String },
39 RestHttp { tls: bool, host: String },
40 WebSockets { tls: bool, host: String },
41 Storm {},
42 UnspecifiedMeans,
43}
44
45#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
46#[display(inner)]
47pub enum InvoiceStateError {
48 #[display(doc_comments)]
49 ParseError(String),
51}
52
53#[derive(Clone, Eq, PartialEq, Hash, Debug, Display)]
54pub enum InvoiceState {
55 #[display("")]
56 Void,
57 #[display("{0}")]
58 Amount(Amount),
59 #[display(inner)]
60 Data(NonFungible),
61}
62
63impl FromStr for InvoiceState {
64 type Err = InvoiceStateError;
65 fn from_str(s: &str) -> Result<Self, Self::Err> {
66 if s.is_empty() {
67 Ok(InvoiceState::Void)
68 } else if let Ok(amount) = Amount::from_str(s) {
69 Ok(InvoiceState::Amount(amount))
70 } else if let Ok(data) = NonFungible::from_str(s) {
71 Ok(InvoiceState::Data(data))
72 } else {
73 Err(InvoiceStateError::ParseError(s.to_owned()))
74 }
75 }
76}
77
78impl From<InvoiceState> for StateType {
79 fn from(val: InvoiceState) -> Self {
80 match val {
81 InvoiceState::Void => StateType::Void,
82 InvoiceState::Amount(_) => StateType::Fungible,
83 InvoiceState::Data(_) => StateType::Structured,
84 }
85 }
86}
87
88#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
89#[non_exhaustive]
90pub enum XChainNet<T> {
91 BitcoinMainnet(T),
92 BitcoinTestnet3(T),
93 BitcoinTestnet4(T),
94 BitcoinSignet(T),
95 BitcoinRegtest(T),
96 LiquidMainnet(T),
97 LiquidTestnet(T),
98}
99
100impl<T> XChainNet<T> {
101 pub fn with(cn: ChainNet, data: T) -> Self {
102 match cn {
103 ChainNet::BitcoinMainnet => XChainNet::BitcoinMainnet(data),
104 ChainNet::BitcoinTestnet3 => XChainNet::BitcoinTestnet3(data),
105 ChainNet::BitcoinTestnet4 => XChainNet::BitcoinTestnet4(data),
106 ChainNet::BitcoinSignet => XChainNet::BitcoinSignet(data),
107 ChainNet::BitcoinRegtest => XChainNet::BitcoinRegtest(data),
108 ChainNet::LiquidMainnet => XChainNet::LiquidMainnet(data),
109 ChainNet::LiquidTestnet => XChainNet::LiquidTestnet(data),
110 }
111 }
112
113 pub fn bitcoin(network: Network, data: T) -> Self {
114 match network {
115 Network::Bitcoin => Self::BitcoinMainnet(data),
116 Network::Testnet => Self::BitcoinTestnet3(data),
117 Network::Testnet4 => Self::BitcoinTestnet4(data),
118 Network::Signet => Self::BitcoinSignet(data),
119 Network::Regtest => Self::BitcoinRegtest(data),
120 }
121 }
122
123 pub fn chain_network(&self) -> ChainNet {
124 match self {
125 XChainNet::BitcoinMainnet(_) => ChainNet::BitcoinMainnet,
126 XChainNet::BitcoinTestnet3(_) => ChainNet::BitcoinTestnet3,
127 XChainNet::BitcoinTestnet4(_) => ChainNet::BitcoinTestnet4,
128 XChainNet::BitcoinSignet(_) => ChainNet::BitcoinSignet,
129 XChainNet::BitcoinRegtest(_) => ChainNet::BitcoinRegtest,
130 XChainNet::LiquidMainnet(_) => ChainNet::LiquidMainnet,
131 XChainNet::LiquidTestnet(_) => ChainNet::LiquidTestnet,
132 }
133 }
134
135 pub fn into_inner(self) -> T {
136 match self {
137 XChainNet::BitcoinMainnet(inner)
138 | XChainNet::BitcoinTestnet3(inner)
139 | XChainNet::BitcoinTestnet4(inner)
140 | XChainNet::BitcoinSignet(inner)
141 | XChainNet::BitcoinRegtest(inner)
142 | XChainNet::LiquidMainnet(inner)
143 | XChainNet::LiquidTestnet(inner) => inner,
144 }
145 }
146
147 pub fn layer1(&self) -> Layer1 { self.chain_network().layer1() }
148
149 pub fn address_network(&self) -> KnownHrp {
150 match self.chain_network() {
151 ChainNet::BitcoinMainnet => KnownHrp::Mainnet,
152 ChainNet::BitcoinTestnet3 | ChainNet::BitcoinTestnet4 | ChainNet::BitcoinSignet => {
153 KnownHrp::Testnets
154 }
155 ChainNet::BitcoinRegtest => KnownHrp::Regtest,
156 ChainNet::LiquidMainnet => KnownHrp::Mainnet,
157 ChainNet::LiquidTestnet => KnownHrp::Testnets,
158 }
159 }
160}
161
162#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)]
163#[display(doc_comments)]
164pub enum Pay2VoutError {
165 InvalidAddressType(u8),
167 InvalidTapkey,
169}
170
171#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, From)]
172pub struct Pay2Vout(AddressPayload);
173
174impl Pay2Vout {
175 pub fn new(address_payload: AddressPayload) -> Self { Pay2Vout(address_payload) }
176}
177
178impl Deref for Pay2Vout {
179 type Target = AddressPayload;
180
181 fn deref(&self) -> &'_ Self::Target { &self.0 }
182}
183
184impl Pay2Vout {
185 pub(crate) const P2PKH: u8 = 1;
186 pub(crate) const P2SH: u8 = 2;
187 pub(crate) const P2WPKH: u8 = 3;
188 pub(crate) const P2WSH: u8 = 4;
189 pub(crate) const P2TR: u8 = 5;
190}
191
192impl TryFrom<[u8; 33]> for Pay2Vout {
193 type Error = Pay2VoutError;
194
195 fn try_from(data: [u8; 33]) -> Result<Self, Self::Error> {
196 let addr_bytes: [u8; 20] = data[1..21].try_into().unwrap();
197 let address = match data[0] {
198 Self::P2PKH => AddressPayload::Pkh(PubkeyHash::from_raw_hash(
199 *hash160::Hash::from_bytes_ref(&addr_bytes),
200 )),
201 Self::P2SH => AddressPayload::Sh(ScriptHash::from_raw_hash(
202 *hash160::Hash::from_bytes_ref(&addr_bytes),
203 )),
204 Self::P2WPKH => AddressPayload::Wpkh(WPubkeyHash::from_raw_hash(
205 *hash160::Hash::from_bytes_ref(&addr_bytes),
206 )),
207 Self::P2WSH => AddressPayload::Wsh(WScriptHash::from_raw_hash(
208 *sha256::Hash::from_bytes_ref(&data[1..].try_into().unwrap()),
209 )),
210 Self::P2TR => AddressPayload::Tr(TweakedPublicKey::dangerous_assume_tweaked(
211 UntweakedPublicKey::from_slice(&data[1..33])
212 .map_err(|_| Pay2VoutError::InvalidTapkey)?,
213 )),
214 wrong => return Err(Pay2VoutError::InvalidAddressType(wrong)),
215 };
216 Ok(Pay2Vout(address))
217 }
218}
219
220#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, From)]
221pub enum Beneficiary {
222 #[from]
223 BlindedSeal(SecretSeal),
224 WitnessVout(Pay2Vout, Option<UntweakedPublicKey>),
225}
226
227#[derive(Clone, Eq, PartialEq, Debug)]
228#[non_exhaustive]
229pub struct RgbInvoice {
230 pub transports: Vec<RgbTransport>,
231 pub contract: Option<ContractId>,
232 pub schema: Option<SchemaId>,
233 pub assignment_name: Option<FieldName>,
234 pub assignment_state: Option<InvoiceState>,
235 pub beneficiary: XChainNet<Beneficiary>,
236 pub expiry: Option<i64>,
238 pub unknown_query: IndexMap<String, String>,
239}
240
241impl RgbInvoice {
242 pub fn chain_network(&self) -> ChainNet { self.beneficiary.chain_network() }
243 pub fn address_network(&self) -> KnownHrp { self.beneficiary.address_network() }
244 pub fn layer1(&self) -> Layer1 { self.beneficiary.layer1() }
245}