@@ -23,20 +23,21 @@ use std::fmt::{self, Debug, Display, Formatter};
23
23
use std:: num:: ParseIntError ;
24
24
use std:: str:: FromStr ;
25
25
26
- use bp:: { Address , AddressNetwork } ;
26
+ use bp:: { Address , AddressNetwork , Chain , ChainParseError } ;
27
27
use fluent_uri:: enc:: EStr ;
28
28
use fluent_uri:: Uri ;
29
29
use indexmap:: IndexMap ;
30
30
use percent_encoding:: { utf8_percent_encode, AsciiSet , CONTROLS } ;
31
31
use rgb:: interface:: TypedState ;
32
- use rgb:: { Chain , ContractId , SecretSeal } ;
32
+ use rgb:: { ContractId , SecretSeal } ;
33
33
use strict_encoding:: { InvalidIdent , TypeName } ;
34
34
35
35
use super :: { Beneficiary , RgbInvoice , RgbTransport } ;
36
36
37
37
const OMITTED : char = '~' ;
38
38
const EXPIRY : & str = "expiry" ;
39
39
const ENDPOINTS : & str = "endpoints" ;
40
+ const CHAIN : & str = "chain" ;
40
41
const TRANSPORT_SEP : char = ',' ;
41
42
const TRANSPORT_HOST_SEP : & str = "://" ;
42
43
const QUERY_ENCODE : & AsciiSet = & CONTROLS
@@ -106,7 +107,16 @@ pub enum InvoiceParseError {
106
107
/// network {0:?} is not supported.
107
108
UnsupportedNetwork ( AddressNetwork ) ,
108
109
110
+ /// chain `{chain}` explicitly specified in the invoice doesn't match
111
+ /// network `{addr_chain}` used in the provided beneficiary address.
112
+ ChainMismatch { chain : Chain , addr_chain : Chain } ,
113
+
109
114
#[ from]
115
+ #[ display( inner) ]
116
+ InvalidChain ( ChainParseError ) ,
117
+
118
+ #[ from]
119
+ #[ display( inner) ]
110
120
Num ( ParseIntError ) ,
111
121
112
122
#[ from]
@@ -115,14 +125,32 @@ pub enum InvoiceParseError {
115
125
}
116
126
117
127
impl RgbInvoice {
128
+ #[ inline]
129
+ fn non_default_chain ( & self ) -> Option < Chain > {
130
+ if self . beneficiary . has_chain_info ( ) {
131
+ return None ;
132
+ }
133
+ if let Some ( chain) = self . chain {
134
+ if chain != Chain :: Bitcoin {
135
+ return Some ( chain) ;
136
+ }
137
+ }
138
+ None
139
+ }
140
+
141
+ #[ inline]
118
142
fn has_params ( & self ) -> bool {
119
143
self . expiry . is_some ( ) ||
120
144
self . transports != vec ! [ RgbTransport :: UnspecifiedMeans ] ||
145
+ self . non_default_chain ( ) . is_some ( ) ||
121
146
!self . unknown_query . is_empty ( )
122
147
}
123
148
124
149
fn query_params ( & self ) -> IndexMap < String , String > {
125
150
let mut query_params: IndexMap < String , String > = IndexMap :: new ( ) ;
151
+ if let Some ( chain) = self . non_default_chain ( ) {
152
+ query_params. insert ( CHAIN . to_string ( ) , chain. to_string ( ) ) ;
153
+ }
126
154
if let Some ( expiry) = self . expiry {
127
155
query_params. insert ( EXPIRY . to_string ( ) , expiry. to_string ( ) ) ;
128
156
}
@@ -306,6 +334,26 @@ impl FromStr for RgbInvoice {
306
334
307
335
let mut query_params = map_query_params ( & uri) ?;
308
336
337
+ let chain = if let Some ( chain_str) = query_params. remove ( CHAIN ) {
338
+ match ( Chain :: from_str ( & chain_str) ?, chain) {
339
+ ( chain, None ) => Some ( chain) ,
340
+ ( chain, Some ( addr_chain) ) if chain == addr_chain => Some ( chain) ,
341
+ ( chain, Some ( addr_chain) )
342
+ if chain. is_testnet ( ) &&
343
+ addr_chain. is_testnet ( ) &&
344
+ chain != Chain :: Regtest &&
345
+ addr_chain != Chain :: Regtest =>
346
+ {
347
+ Some ( chain)
348
+ }
349
+ ( chain, Some ( addr_chain) ) => {
350
+ return Err ( InvoiceParseError :: ChainMismatch { chain, addr_chain } ) ;
351
+ }
352
+ }
353
+ } else {
354
+ None
355
+ } ;
356
+
309
357
let transports = if let Some ( endpoints) = query_params. remove ( ENDPOINTS ) {
310
358
let tokens: Vec < & str > = endpoints. split ( TRANSPORT_SEP ) . collect ( ) ;
311
359
let mut transport_vec: Vec < RgbTransport > = vec ! [ ] ;
0 commit comments