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 BitcoinSignetCustom(T),
96 BitcoinRegtest(T),
97 LiquidMainnet(T),
98 LiquidTestnet(T),
99}
100
101impl<T> XChainNet<T> {
102 pub fn with(cn: ChainNet, data: T) -> Self {
103 match cn {
104 ChainNet::BitcoinMainnet => XChainNet::BitcoinMainnet(data),
105 ChainNet::BitcoinTestnet3 => XChainNet::BitcoinTestnet3(data),
106 ChainNet::BitcoinTestnet4 => XChainNet::BitcoinTestnet4(data),
107 ChainNet::BitcoinSignet => XChainNet::BitcoinSignet(data),
108 ChainNet::BitcoinSignetCustom => XChainNet::BitcoinSignetCustom(data),
109 ChainNet::BitcoinRegtest => XChainNet::BitcoinRegtest(data),
110 ChainNet::LiquidMainnet => XChainNet::LiquidMainnet(data),
111 ChainNet::LiquidTestnet => XChainNet::LiquidTestnet(data),
112 }
113 }
114
115 pub fn bitcoin(network: Network, data: T) -> Self {
116 match network {
117 Network::Bitcoin => Self::BitcoinMainnet(data),
118 Network::Testnet => Self::BitcoinTestnet3(data),
119 Network::Testnet4 => Self::BitcoinTestnet4(data),
120 Network::Signet => Self::BitcoinSignet(data),
121 Network::Regtest => Self::BitcoinRegtest(data),
122 }
123 }
124
125 pub fn chain_network(&self) -> ChainNet {
126 match self {
127 XChainNet::BitcoinMainnet(_) => ChainNet::BitcoinMainnet,
128 XChainNet::BitcoinTestnet3(_) => ChainNet::BitcoinTestnet3,
129 XChainNet::BitcoinTestnet4(_) => ChainNet::BitcoinTestnet4,
130 XChainNet::BitcoinSignet(_) => ChainNet::BitcoinSignet,
131 XChainNet::BitcoinSignetCustom(_) => ChainNet::BitcoinSignetCustom,
132 XChainNet::BitcoinRegtest(_) => ChainNet::BitcoinRegtest,
133 XChainNet::LiquidMainnet(_) => ChainNet::LiquidMainnet,
134 XChainNet::LiquidTestnet(_) => ChainNet::LiquidTestnet,
135 }
136 }
137
138 pub fn into_inner(self) -> T {
139 match self {
140 XChainNet::BitcoinMainnet(inner)
141 | XChainNet::BitcoinTestnet3(inner)
142 | XChainNet::BitcoinTestnet4(inner)
143 | XChainNet::BitcoinSignet(inner)
144 | XChainNet::BitcoinSignetCustom(inner)
145 | XChainNet::BitcoinRegtest(inner)
146 | XChainNet::LiquidMainnet(inner)
147 | XChainNet::LiquidTestnet(inner) => inner,
148 }
149 }
150
151 pub fn layer1(&self) -> Layer1 { self.chain_network().layer1() }
152
153 pub fn address_network(&self) -> KnownHrp {
154 match self.chain_network() {
155 ChainNet::BitcoinMainnet => KnownHrp::Mainnet,
156 ChainNet::BitcoinTestnet3
157 | ChainNet::BitcoinTestnet4
158 | ChainNet::BitcoinSignet
159 | ChainNet::BitcoinSignetCustom => KnownHrp::Testnets,
160 ChainNet::BitcoinRegtest => KnownHrp::Regtest,
161 ChainNet::LiquidMainnet => KnownHrp::Mainnet,
162 ChainNet::LiquidTestnet => KnownHrp::Testnets,
163 }
164 }
165}
166
167#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)]
168#[display(doc_comments)]
169pub enum Pay2VoutError {
170 InvalidAddressType(u8),
172 InvalidTapkey,
174}
175
176#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, From)]
177pub struct Pay2Vout(AddressPayload);
178
179impl Pay2Vout {
180 pub fn new(address_payload: AddressPayload) -> Self { Pay2Vout(address_payload) }
181}
182
183impl Deref for Pay2Vout {
184 type Target = AddressPayload;
185
186 fn deref(&self) -> &'_ Self::Target { &self.0 }
187}
188
189impl Pay2Vout {
190 pub(crate) const P2PKH: u8 = 1;
191 pub(crate) const P2SH: u8 = 2;
192 pub(crate) const P2WPKH: u8 = 3;
193 pub(crate) const P2WSH: u8 = 4;
194 pub(crate) const P2TR: u8 = 5;
195}
196
197impl TryFrom<[u8; 33]> for Pay2Vout {
198 type Error = Pay2VoutError;
199
200 fn try_from(data: [u8; 33]) -> Result<Self, Self::Error> {
201 let addr_bytes: [u8; 20] = data[1..21].try_into().unwrap();
202 let address = match data[0] {
203 Self::P2PKH => AddressPayload::Pkh(PubkeyHash::from_raw_hash(
204 *hash160::Hash::from_bytes_ref(&addr_bytes),
205 )),
206 Self::P2SH => AddressPayload::Sh(ScriptHash::from_raw_hash(
207 *hash160::Hash::from_bytes_ref(&addr_bytes),
208 )),
209 Self::P2WPKH => AddressPayload::Wpkh(WPubkeyHash::from_raw_hash(
210 *hash160::Hash::from_bytes_ref(&addr_bytes),
211 )),
212 Self::P2WSH => AddressPayload::Wsh(WScriptHash::from_raw_hash(
213 *sha256::Hash::from_bytes_ref(&data[1..].try_into().unwrap()),
214 )),
215 Self::P2TR => AddressPayload::Tr(TweakedPublicKey::dangerous_assume_tweaked(
216 UntweakedPublicKey::from_slice(&data[1..33])
217 .map_err(|_| Pay2VoutError::InvalidTapkey)?,
218 )),
219 wrong => return Err(Pay2VoutError::InvalidAddressType(wrong)),
220 };
221 Ok(Pay2Vout(address))
222 }
223}
224
225#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, From)]
226pub enum Beneficiary {
227 #[from]
228 BlindedSeal(SecretSeal),
229 WitnessVout(Pay2Vout, Option<UntweakedPublicKey>),
230}
231
232#[derive(Clone, Eq, PartialEq, Debug)]
233#[non_exhaustive]
234pub struct RgbInvoice {
235 pub transports: Vec<RgbTransport>,
236 pub contract: Option<ContractId>,
237 pub schema: Option<SchemaId>,
238 pub assignment_name: Option<FieldName>,
239 pub assignment_state: Option<InvoiceState>,
240 pub beneficiary: XChainNet<Beneficiary>,
241 pub expiry: Option<i64>,
243 pub unknown_query: IndexMap<String, String>,
244}
245
246impl RgbInvoice {
247 pub fn chain_network(&self) -> ChainNet { self.beneficiary.chain_network() }
248 pub fn address_network(&self) -> KnownHrp { self.beneficiary.address_network() }
249 pub fn layer1(&self) -> Layer1 { self.beneficiary.layer1() }
250}