Skip to content

Commit da5b58b

Browse files
fix(invoice): correctly parse assignment_name in RgbInvoice::from_str
1 parent 24655fd commit da5b58b

File tree

1 file changed

+54
-5
lines changed

1 file changed

+54
-5
lines changed

invoice/src/parse.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use indexmap::IndexMap;
3232
use invoice::{AddressPayload, UnknownNetwork};
3333
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
3434
use rgb::{ChainNet, ContractId, SchemaId, SecretSeal};
35+
use strict_types::FieldName;
3536

3637
use crate::invoice::{Beneficiary, InvoiceState, Pay2Vout, RgbInvoice, RgbTransport, XChainNet};
3738

@@ -382,14 +383,25 @@ impl FromStr for RgbInvoice {
382383
Err(_) => return Err(InvoiceParseError::InvalidSchemaId(schema_str.to_string())),
383384
};
384385

385-
let Some(assignment) = path.next() else {
386-
return Err(InvoiceParseError::AssignmentMissed);
386+
let (assignment_name, assignment) = match (path.next(), path.next()) {
387+
(None, _) => return Err(InvoiceParseError::AssignmentMissed),
388+
(Some(first), Some(second)) => {
389+
let name = FieldName::try_from(first.as_str().to_owned())
390+
.map_err(|e| InvoiceParseError::InvalidQueryParam(e.to_string()))?;
391+
(Some(name), second.as_str().to_owned())
392+
}
393+
(Some(first), None) => (None, first.as_str().to_owned()),
387394
};
395+
396+
// Check no extra path segments remain
397+
if path.next().is_some() {
398+
return Err(InvoiceParseError::Invalid);
399+
}
400+
388401
let (amount, beneficiary) = assignment
389-
.as_str()
390402
.split_once('+')
391403
.map(|(a, b)| (Some(a), b))
392-
.unwrap_or((None, assignment.as_str()));
404+
.unwrap_or((None, &assignment));
393405
let (value, beneficiary_str) = match (amount, beneficiary) {
394406
(Some(a), b) => (
395407
InvoiceState::from_str(a).map_err(|_| InvoiceParseError::Data(a.to_string()))?,
@@ -427,7 +439,7 @@ impl FromStr for RgbInvoice {
427439
transports,
428440
contract,
429441
schema,
430-
assignment_name: None,
442+
assignment_name,
431443
beneficiary,
432444
assignment_state: value,
433445
expiry,
@@ -835,4 +847,41 @@ mod test {
835847
));
836848
assert_eq!(Pay2Vout::from_str(&p.to_string()).unwrap(), p);
837849
}
850+
851+
#[test]
852+
fn parse_with_assignment_name() {
853+
let original_invoice = RgbInvoice {
854+
transports: vec![RgbTransport::UnspecifiedMeans],
855+
contract: ContractId::from_str("11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI").ok(),
856+
schema: SchemaId::from_str("Il7xqc$4yuca6X0oy0kN27e13ieIAxAv43uVfpG$gYE").ok(),
857+
assignment_name: Some("owner".into()),
858+
beneficiary: XChainNet::with(
859+
ChainNet::BitcoinMainnet,
860+
Beneficiary::BlindedSeal(
861+
SecretSeal::from_str("zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F")
862+
.unwrap(),
863+
),
864+
),
865+
assignment_state: InvoiceState::Amount(Amount::from(100u64)), // Example amount
866+
expiry: None,
867+
unknown_query: IndexMap::new(),
868+
};
869+
870+
let invoice_str = original_invoice.to_string();
871+
872+
assert!(invoice_str.contains("/owner/"));
873+
let expected_str_fragment = "Il7xqc$4yuca6X0oy0kN27e13ieIAxAv43uVfpG$gYE/owner/BF+bc:\
874+
utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F";
875+
assert!(invoice_str.contains(expected_str_fragment));
876+
877+
let deserialized_invoice =
878+
RgbInvoice::from_str(&invoice_str).expect("Failed to deserialize invoice string");
879+
880+
assert_eq!(deserialized_invoice.assignment_name, original_invoice.assignment_name,);
881+
882+
assert_eq!(deserialized_invoice.contract, original_invoice.contract,);
883+
assert_eq!(deserialized_invoice.schema, original_invoice.schema,);
884+
assert_eq!(deserialized_invoice.beneficiary, original_invoice.beneficiary,);
885+
assert_eq!(deserialized_invoice.assignment_state, original_invoice.assignment_state,);
886+
}
838887
}

0 commit comments

Comments
 (0)