diff --git a/Cargo.lock b/Cargo.lock index 80e12cd3..2420f764 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,12 +149,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "base58" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" - [[package]] name = "base64" version = "0.22.1" @@ -674,11 +668,12 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.9" -source = "git+https://github.com/RGB-WG/rgb-core?branch=feat/fungible-nonconf#75424505e4f24df90d9ee1d284c4ee25103c185b" +source = "git+https://github.com/RGB-WG/rgb-core?branch=feat/fungible-nonconf#b59f1ca4c13c62ce53152f7f4c34094e435a1be4" dependencies = [ "aluvm", "amplify", "baid64", + "base64", "bp-core", "chrono", "commit_verify", @@ -696,7 +691,6 @@ version = "0.11.0-beta.9" dependencies = [ "amplify", "baid64", - "base58", "bp-core", "bp-invoice", "fluent-uri", diff --git a/invoice/Cargo.toml b/invoice/Cargo.toml index ff70bc69..60607596 100644 --- a/invoice/Cargo.toml +++ b/invoice/Cargo.toml @@ -17,7 +17,6 @@ name = "rgbinvoice" [dependencies] amplify = { workspace = true } -base58 = "0.2.0" baid64 = { workspace = true } strict_encoding = { workspace = true } bp-core = { workspace = true } diff --git a/invoice/src/builder.rs b/invoice/src/builder.rs index 1805ee9e..9d84fb61 100644 --- a/invoice/src/builder.rs +++ b/invoice/src/builder.rs @@ -21,11 +21,10 @@ use std::str::FromStr; -use rgb::{AttachId, ContractId, State, StateData}; -use strict_encoding::{FieldName, StrictSerialize, TypeName}; +use rgb::{ContractId, StateData}; +use strict_encoding::{FieldName, SerializeError, StrictSerialize, TypeName}; -use crate::invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport, XChainNet}; -use crate::TransportParseError; +use crate::{Beneficiary, RgbInvoice, RgbTransport, TransportParseError, XChainNet}; #[derive(Clone, Eq, PartialEq, Debug)] pub struct RgbInvoiceBuilder(RgbInvoice); @@ -40,7 +39,7 @@ impl RgbInvoiceBuilder { operation: None, assignment: None, beneficiary: beneficiary.into(), - owned_state: InvoiceState::Any, + state: None, expiry: None, unknown_query: none!(), }) @@ -72,106 +71,26 @@ impl RgbInvoiceBuilder { self } - /// Set the invoiced state, which includes both state data and an optional attachment - /// information. - /// - /// # Panics - /// - /// If any state information or attachment requirements are already present in the invoice. - /// - /// # See also - /// - /// - [`Self::add_state_data`], adding just state data information, not affecting attachment - /// requirements; - /// - [`Self::serialize_state_data`], for adding state data by serializing them from a state - /// object; - /// - [`Self::add_attachment`], for adding attachment requirement to an existing invoiced state - /// information. - pub fn set_state(mut self, state: State) -> Self { - if !self.0.owned_state.is_any() { - panic!("invoice already has state information"); - } - self.0.owned_state = InvoiceState::Specific(state); - self - } - /// Add state data to the invoice. /// - /// NB: This keeps existing attachment requirements. - /// - /// # Panics - /// - /// If the invoice already have any state information (excluding attachment requirements). - /// - /// # See also - /// - /// - [`Self::set_state`], for redefining the whole of the invoiced state, including attachment - /// requirements; - /// - [`Self::serialize_state_data`], for adding state data by serializing them from a state - /// object; - /// - [`Self::add_attachment`], for adding attachment requirement to an existing invoiced state - /// information. - pub fn add_state_data(mut self, data: StateData) -> Self { - self.0.owned_state = match self.0.owned_state { - InvoiceState::Any => InvoiceState::Specific(State::from(data)), - InvoiceState::Specific(_) => panic!("invoice already has state information"), - InvoiceState::Attach(attach_id) => InvoiceState::Specific(State::with(data, attach_id)), - }; + /// See also [`Self::serialize_state_data`], which adds state data by serializing them from a + /// state object. + pub fn set_state(mut self, data: StateData) -> Self { + self.0.state = Some(data); self } /// Add state data to the invoice by strict-serializing the provided object. /// - /// NB: This keeps existing attachment requirements. - /// /// Use the function carefully, since the common pitfall here is to perform double serialization /// of an already serialized data type, like `SmallBlob`. This produces an invalid state object - /// which can't be properly parsed later. - /// - /// # Panics - /// - /// If the invoice already has any state information (excluding attachment requirements). - /// - /// # See also - /// - /// - [`Self::set_state`], for redefining the whole of the invoiced state, including attachment - /// requirements; - /// - [`Self::add_state_data`], adding just state data information, not affecting attachment - /// requirements; - /// - [`Self::add_attachment`], for adding attachment requirement to an existing invoiced state - /// information. - pub fn serialize_state_data(mut self, data: impl StrictSerialize) -> Self { - self.0.owned_state = InvoiceState::Specific(State::from_serialized(data)); - self - } - - /// Add attachment requirements to an invoice, keeping the rest of the invoice state information - /// unchanged. - /// - /// # Panics - /// - /// If the invoice already has attachment requirements defined. - /// - /// # See also - /// - /// - [`Self::set_state`], for redefining the whole of the invoiced state, including attachment - /// requirements; - /// - [`Self::add_state_data`], adding just state data information, not affecting attachment - /// requirements; - /// - [`Self::serialize_state_data`], for adding state data by serializing them from a state - /// object; - pub fn add_attachment(mut self, attach_id: AttachId) -> Result { - self.0.owned_state = match self.0.owned_state { - InvoiceState::Any => InvoiceState::Attach(attach_id), - InvoiceState::Attach(_) - | InvoiceState::Specific(State { - attach: Some(_), .. - }) => panic!("invoice already has attachment requirements"), - InvoiceState::Specific(mut state) => { - state.attach = Some(attach_id); - InvoiceState::Specific(state) - } - }; + /// which can't be properly parsed later. See also [`Self::set_state`], which sets state data + /// directly with no serialization. + pub fn serialize_state_data( + mut self, + data: &impl StrictSerialize, + ) -> Result { + self.0.state = Some(StateData::from_serialized(data)?); Ok(self) } diff --git a/invoice/src/invoice.rs b/invoice/src/invoice.rs index f48f5b75..41f1720d 100644 --- a/invoice/src/invoice.rs +++ b/invoice/src/invoice.rs @@ -24,7 +24,7 @@ use bp::seals::txout::CloseMethod; use bp::{InvalidPubkey, OutputPk, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash}; use indexmap::IndexMap; use invoice::{AddressNetwork, AddressPayload, Network}; -use rgb::{AttachId, ContractId, Layer1, SecretSeal, State}; +use rgb::{ContractId, Layer1, SecretSeal, StateData}; use strict_encoding::{FieldName, TypeName}; #[derive(Clone, Eq, PartialEq, Hash, Debug)] @@ -37,33 +37,6 @@ pub enum RgbTransport { UnspecifiedMeans, } -#[derive(Clone, Eq, PartialEq, Hash, Debug)] -pub enum InvoiceState { - Any, - Specific(State), - Attach(AttachId), -} - -impl InvoiceState { - pub fn is_any(&self) -> bool { matches!(self, InvoiceState::Any) } - - pub fn state(&self) -> Option<&State> { - match self { - InvoiceState::Any => None, - InvoiceState::Specific(s) => Some(s), - InvoiceState::Attach(_) => None, - } - } - - pub fn attach_id(&self) -> Option { - match self { - InvoiceState::Any => None, - InvoiceState::Specific(s) => s.attach, - InvoiceState::Attach(id) => Some(*id), - } - } -} - #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] #[non_exhaustive] pub enum ChainNet { @@ -237,9 +210,10 @@ pub struct RgbInvoice { pub operation: Option, pub assignment: Option, pub beneficiary: XChainNet, - pub owned_state: InvoiceState, + pub state: Option, /// UTC unix timestamp pub expiry: Option, + // Attachment requirements should go here pub unknown_query: IndexMap, } diff --git a/invoice/src/lib.rs b/invoice/src/lib.rs index d2820bc8..9ffe2999 100644 --- a/invoice/src/lib.rs +++ b/invoice/src/lib.rs @@ -37,6 +37,5 @@ pub use builder::RgbInvoiceBuilder; pub use parse::{InvoiceParseError, TransportParseError}; pub use crate::invoice::{ - Beneficiary, ChainNet, InvoiceState, Pay2Vout, Pay2VoutError, RgbInvoice, RgbTransport, - XChainNet, + Beneficiary, ChainNet, Pay2Vout, Pay2VoutError, RgbInvoice, RgbTransport, XChainNet, }; diff --git a/invoice/src/parse.rs b/invoice/src/parse.rs index 49a49dca..3ae6b546 100644 --- a/invoice/src/parse.rs +++ b/invoice/src/parse.rs @@ -24,21 +24,16 @@ use std::io::{Cursor, Write}; use std::num::ParseIntError; use std::str::FromStr; -use amplify::confinement::{self, SmallBlob}; -use amplify::Wrapper; use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; -use base58::{FromBase58, ToBase58}; use fluent_uri::enc::EStr; use fluent_uri::Uri; use indexmap::IndexMap; use invoice::{AddressPayload, UnknownNetwork}; use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; -use rgb::{ContractId, SecretSeal, State, StateData}; +use rgb::{ContractId, SecretSeal, StateData, StateParseError}; use strict_encoding::{InvalidRString, TypeName}; -use crate::invoice::{ - Beneficiary, ChainNet, InvoiceState, Pay2Vout, RgbInvoice, RgbTransport, XChainNet, -}; +use crate::invoice::{Beneficiary, ChainNet, Pay2Vout, RgbInvoice, RgbTransport, XChainNet}; const OMITTED: &str = "~"; const EXPIRY: &str = "expiry"; @@ -68,22 +63,6 @@ pub enum TransportParseError { InvalidTransportHost(String), } -#[derive(Debug, Display, Error, From)] -#[display(doc_comments)] -pub enum InvoiceStateError { - #[from] - /// invalid invoice state Base58 encoding. - Base58(base58::FromBase58Error), - - #[from] - /// invoice state size exceeded. - Len(confinement::Error), - - #[from] - /// invalid invoice state encoding - {0} - Deserialize(strict_encoding::DeserializeError), -} - #[derive(Debug, Display, Error, From)] #[display(doc_comments)] pub enum InvoiceParseError { @@ -112,7 +91,7 @@ pub enum InvoiceParseError { #[from] #[display(inner)] - InvalidState(InvoiceStateError), + InvalidState(StateParseError), /// no invoice transport has been provided. NoTransport, @@ -177,30 +156,6 @@ impl RgbInvoice { } } -impl Display for InvoiceState { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - InvoiceState::Any => Ok(()), - InvoiceState::Specific(state) => f.write_str(&state.data.to_base58()), - // TODO: Support attachment through invoice params - InvoiceState::Attach(_) => Ok(()), - } - } -} - -impl FromStr for InvoiceState { - type Err = InvoiceStateError; - fn from_str(s: &str) -> Result { - if s.is_empty() { - return Ok(InvoiceState::Any); - } - let data = s.from_base58()?; - let data = SmallBlob::try_from(data)?; - let data = StateData::from_inner(data); - Ok(InvoiceState::Specific(State::from(data))) - } -} - impl Display for RgbTransport { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { @@ -337,7 +292,6 @@ impl FromStr for XChainNet { impl Display for RgbInvoice { fn fmt(&self, f: &mut Formatter) -> fmt::Result { // TODO: Support attachment through invoice params - let amt = self.owned_state.to_string(); if let Some(contract) = self.contract { let id = if f.alternate() { contract.to_string().replace('-', "") @@ -359,8 +313,8 @@ impl Display for RgbInvoice { if let Some(ref assignment_name) = self.assignment { write!(f, "{assignment_name}/")?; } - if !amt.is_empty() { - write!(f, "{amt}+")?; + if let Some(state) = self.state.as_ref() { + write!(f, "{state}+")?; } let beneficiary = if f.alternate() { self.beneficiary.to_string().replace('-', "") @@ -442,9 +396,9 @@ impl FromStr for RgbInvoice { .split_once('+') .map(|(a, b)| (Some(a), Some(b))) .unwrap_or((Some(assignment.as_str()), None)); - let (beneficiary_str, value) = match (beneficiary, state) { - (Some(b), Some(a)) => (b, InvoiceState::from_str(a)?), - (None, Some(b)) => (b, InvoiceState::Any), + let (beneficiary_str, state) = match (beneficiary, state) { + (Some(b), Some(a)) => (b, Some(StateData::from_str(a)?)), + (None, Some(b)) => (b, None), _ => unreachable!(), }; @@ -480,7 +434,7 @@ impl FromStr for RgbInvoice { operation: None, assignment: None, beneficiary, - owned_state: value, + state, expiry, unknown_query: query_params, }) @@ -518,31 +472,22 @@ mod test { fn parse() { // rgb20/rgb25 parameters let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ - T5FhUZEHbQu4B+bc:utxob:\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); - assert_eq!( - invoice.owned_state, - InvoiceState::Specific(State::from(StateData::from_checked(vec![ - 8, 0, 100, 0, 0, 0, 0, 0, 0, 0 - ]))) - ); + assert_eq!(invoice.state, Some(StateData::from_checked(vec![100, 0, 0, 0, 0, 0, 0, 0]))); assert_eq!(invoice.to_string(), invoice_str); - assert_eq!(format!("{invoice:#}"), invoice_str.replace('-', "")); // rgb21 parameters let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB21/\ - 5QsfkEcyanohXadePHZ+bc:utxob:\ + -p----p---------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!( - invoice.owned_state, - InvoiceState::Specific(State::from(StateData::from_checked(vec![ - 12, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 - ]))) + invoice.state, + Some(StateData::from_checked(vec![1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0])) ); assert_eq!(invoice.to_string(), invoice_str); - assert_eq!(format!("{invoice:#}"), invoice_str.replace('-', "")); // no amount let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/bc:utxob:\ @@ -584,47 +529,51 @@ mod test { Err(InvoiceParseError::InvalidContractId(c)) if c == invalid_contract_id)); // with expiration - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?\ - expiry=1682086371"; + let invoice_str = + "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/y----------+bc:utxob:\ + zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?expiry=1682086371"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // bad expiration - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?expiry=six"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidExpiration(_)))); // with bad query parameter - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?expiry"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // with an unknown query parameter - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?unknown=new"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // with two unknown query parameters - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?unknown=new&\ - another=new"; + let invoice_str = + "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/y----------+bc:utxob:\ + zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?unknown=new&another=new"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // with expiration and an unknown query parameter - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?\ - expiry=1682086371&unknown=new"; + let invoice_str = + "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/y----------+bc:utxob:\ + zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?expiry=1682086371&unknown=new"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); assert_eq!(invoice.to_string(), invoice_str); // with an unknown query parameter containing percent-encoded text - let invoice_base = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:\ - utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?"; + let invoice_base = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ + zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?"; let query_key_encoded = ":@-%20%23"; let query_key_decoded = ":@- #"; let query_val_encoded = "?/.%26%3D"; @@ -652,26 +601,30 @@ mod test { assert!(matches!(result, Err(InvoiceParseError::InvalidScheme(_)))); // empty transport endpoint specification - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints="; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // invalid transport endpoint specification - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=bad"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // invalid transport variant - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpca:/\ /host.example.com"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // rgb-rpc variant - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc://\ host.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); @@ -682,7 +635,8 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb-rpc variant, host containing authentication, "-" characters and port - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpcs:/\ /user:pass@host-1.ex-ample.com:1234"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); @@ -693,7 +647,8 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb-rpc variant, IPv6 host - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpcs:/\ /%5B2001:db8::1%5D:1234"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); @@ -704,29 +659,30 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb-rpc variant with missing host - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc://"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // rgb-rpc variant with invalid separator - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc/\ - host.example.com"; + let invoice_str = + "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/y----------+bc:utxob:\ + zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc/host.example.com"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::InvalidQueryParam(_)))); // rgb-rpc variant with invalid transport host specification - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ - zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc://\ - ho]t"; + let invoice_str = + "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/y----------+bc:utxob:\ + zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpc://ho]t"; let result = RgbInvoice::from_str(invoice_str); assert!(matches!(result, Err(InvoiceParseError::Uri(_)))); // rgb+http variant let invoice_str = "rgb:\ 11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ - BF+bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=https://\ + y----------+bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=https://\ host.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); let transports = vec![RgbTransport::RestHttp { @@ -737,7 +693,8 @@ mod test { assert_eq!(invoice.to_string(), invoice_str); // rgb+ws variant - let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/BF+bc:utxob:\ + let invoice_str = "rgb:11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ + y----------+bc:utxob:\ zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=wss://\ host.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); @@ -753,7 +710,7 @@ mod test { // multiple transports let invoice_str = "rgb:\ 11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI/RGB20/\ - BF+bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpcs://\ + y----------+bc:utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F?endpoints=rpcs://\ host1.example.com,http://host2.example.com,ws://host3.example.com"; let invoice = RgbInvoice::from_str(invoice_str).unwrap(); let transports = vec![ diff --git a/src/interface/builder.rs b/src/interface/builder.rs index e794dad5..f11b0735 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -224,19 +224,19 @@ impl ContractBuilder { ) -> Result { let seal = seal.into(); self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_owned_state_raw(type_id, seal, state)?; + self.builder = self.builder.add_owned_state(type_id, seal, state)?; Ok(self) } - pub fn add_owned_state( + pub fn serialize_owned_state( mut self, name: impl Into, seal: impl Into>, - value: impl StrictSerialize, + value: &impl StrictSerialize, ) -> Result { let seal = seal.into(); self.check_layer1(seal.layer1())?; - self.builder = self.builder.add_owned_state(name, seal, value)?; + self.builder = self.builder.serialize_owned_state(name, seal, value)?; Ok(self) } @@ -447,7 +447,7 @@ impl TransitionBuilder { seal: impl Into>, state: State, ) -> Result { - self.builder = self.builder.add_owned_state_raw(type_id, seal, state)?; + self.builder = self.builder.add_owned_state(type_id, seal, state)?; Ok(self) } @@ -464,7 +464,7 @@ impl TransitionBuilder { let state = calc.calc_output(type_id, &state)?; self.builder = self .builder - .add_owned_state_raw(type_id, seal, state.sufficient)?; + .add_owned_state(type_id, seal, state.sufficient)?; Ok((self, state.insufficient)) } @@ -478,7 +478,7 @@ impl TransitionBuilder { .as_mut() .expect("you must not call add_owned_state_change for the blank transition builder"); if let Some(state) = calc.calc_change(type_id)? { - self.builder = self.builder.add_owned_state_raw(type_id, seal, state)?; + self.builder = self.builder.add_owned_state(type_id, seal, state)?; } Ok(self) } @@ -622,7 +622,7 @@ impl OperationBuilder { Ok(self) } - fn add_owned_state_raw( + fn add_owned_state( mut self, type_id: AssignmentType, seal: impl Into>, @@ -641,11 +641,11 @@ impl OperationBuilder { Ok(self) } - fn add_owned_state( + fn serialize_owned_state( self, name: impl Into, seal: impl Into>, - value: impl StrictSerialize, + value: &impl StrictSerialize, ) -> Result { let name = name.into(); @@ -653,7 +653,7 @@ impl OperationBuilder { .assignments_type(&name) .ok_or(BuilderError::AssignmentNotFound(name))?; - self.add_owned_state_raw(type_id, seal, State::from_serialized(value)) + self.add_owned_state(type_id, seal, State::from_serialized(value)?) } fn complete(self) -> (Schema, Iface, IfaceImpl, GlobalState, Assignments, TypeSystem) { diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index 273af067..75fbce5f 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -885,7 +885,7 @@ impl Stock { #[allow(clippy::result_large_err)] pub fn compose( &self, - invoice: &RgbInvoice, + invoice: RgbInvoice, prev_outputs: impl IntoIterator>, method: CloseMethod, beneficiary_vout: Option>, @@ -908,7 +908,7 @@ impl Stock { #[allow(clippy::too_many_arguments, clippy::result_large_err)] pub fn compose_deterministic( &self, - invoice: &RgbInvoice, + invoice: RgbInvoice, prev_outputs: impl IntoIterator>, method: CloseMethod, beneficiary_vout: Option>, @@ -917,11 +917,8 @@ impl Stock { seal_blinder: impl Fn(ContractId, AssignmentType) -> u64, ) -> Result> { let layer1 = invoice.layer1(); - let invoice_state = invoice - .owned_state - .state() - .ok_or(ComposeError::NoInvoiceState)? - .clone(); + let state_data = invoice.state.ok_or(ComposeError::NoInvoiceState)?; + let state = rgb::State::from(state_data); // TODO: Take account attachements when they will be supported by invoices let prev_outputs = prev_outputs .into_iter() .map(|o| o.into()) @@ -1029,7 +1026,7 @@ impl Stock { // Add payments to beneficiary let (builder, partial_state) = - main_builder.fulfill_owned_state(assignment_id, beneficiary, invoice_state)?; + main_builder.fulfill_owned_state(assignment_id, beneficiary, state)?; main_builder = builder; if let Some(partial_state) = partial_state { let (builder, remaining_state) =