Skip to content

Commit

Permalink
Update bitcoin to 0.30
Browse files Browse the repository at this point in the history
  • Loading branch information
Kixunil committed Mar 22, 2023
1 parent 8158b32 commit da1a87b
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ non-compliant-bytes = ["either"]
[dependencies]
either = { version = "1.6.1", optional = true }
percent-encoding-rfc3986 = "0.1.3"
bitcoin = "0.29.0"
bitcoin = "0.30.0"
19 changes: 10 additions & 9 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ use alloc::borrow::ToOwned;
use alloc::borrow::Cow;
use alloc::string::String;
use core::convert::{TryFrom, TryInto};
use bitcoin::util::amount::{Denomination, ParseAmountError};
use bitcoin::util::address::Error as AddressError;
use bitcoin::amount::{Denomination, ParseAmountError};
use bitcoin::address::Error as AddressError;
use bitcoin::address::NetworkValidation;
use core::fmt;
use super::{Uri, Param};
use percent_encoding_rfc3986::PercentDecodeError;

impl<'a, T: DeserializeParams<'a>> Uri<'a, T> {
impl<'a, T: DeserializeParams<'a>> Uri<'a, bitcoin::address::NetworkUnchecked, T> {
/// Implements deserialization.
fn deserialize_raw(string: &'a str) -> Result<Self, Error<T::Error>> {
const SCHEME: &str = "bitcoin:";
Expand Down Expand Up @@ -82,11 +83,11 @@ impl<'a, T: DeserializeParams<'a>> Uri<'a, T> {
}
}

impl<'a, T> Uri<'a, T> {
impl<'a, NetVal: NetworkValidation, T> Uri<'a, NetVal, T> {
/// Makes the lifetime `'static` by converting all fields to owned.
///
/// Note that this does **not** affect `extras`!
fn into_static(self) -> Uri<'static, T> {
fn into_static(self) -> Uri<'static, NetVal, T> {
Uri {
address: self.address,
amount: self.amount,
Expand Down Expand Up @@ -282,15 +283,15 @@ impl std::error::Error for UriError {
}

/// **Warning**: this implementation may needlessly allocate, consider using `TryFrom<&str>` instead.
impl<'a, T: for<'de> DeserializeParams<'de>> core::str::FromStr for Uri<'a, T> {
impl<'a, T: for<'de> DeserializeParams<'de>> core::str::FromStr for Uri<'a, bitcoin::address::NetworkUnchecked, T> {
type Err = Error<T::Error>;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Uri::deserialize_raw(s).map(Uri::into_static)
}
}

impl<'a, T: DeserializeParams<'a>> TryFrom<&'a str> for Uri<'a, T> {
impl<'a, T: DeserializeParams<'a>> TryFrom<&'a str> for Uri<'a, bitcoin::address::NetworkUnchecked, T> {
type Error = Error<T::Error>;

fn try_from(s: &'a str) -> Result<Self, Self::Error> {
Expand All @@ -299,7 +300,7 @@ impl<'a, T: DeserializeParams<'a>> TryFrom<&'a str> for Uri<'a, T> {
}

/// **Warning**: this implementation may needlessly allocate, consider using `TryFrom<&str>` instead.
impl<'a, T: for<'de> DeserializeParams<'de>> TryFrom<String> for Uri<'a, T> {
impl<'a, T: for<'de> DeserializeParams<'de>> TryFrom<String> for Uri<'a, bitcoin::address::NetworkUnchecked, T> {
type Error = Error<T::Error>;

fn try_from(s: String) -> Result<Self, Self::Error> {
Expand All @@ -308,7 +309,7 @@ impl<'a, T: for<'de> DeserializeParams<'de>> TryFrom<String> for Uri<'a, T> {
}

/// **Warning**: this implementation may needlessly allocate, consider using `TryFrom<&str>` instead.
impl<'a, T: for<'de> DeserializeParams<'de>> TryFrom<Cow<'a, str>> for Uri<'a, T> {
impl<'a, T: for<'de> DeserializeParams<'de>> TryFrom<Cow<'a, str>> for Uri<'a, bitcoin::address::NetworkUnchecked, T> {
type Error = Error<T::Error>;

fn try_from(s: Cow<'a, str>) -> Result<Self, Self::Error> {
Expand Down
60 changes: 48 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use percent_encoding_rfc3986::{PercentDecode, PercentDecodeError};
#[cfg(feature = "non-compliant-bytes")]
use either::Either;
use core::convert::{TryFrom, TryInto};
use bitcoin::address::NetworkValidation;

pub use de::{DeserializeParams, DeserializationState, DeserializationError};
pub use ser::{SerializeParams};
Expand All @@ -78,11 +79,14 @@ pub use ser::{SerializeParams};
/// [See compatibility table.](https://github.com/btcpayserver/btcpayserver/issues/2110)
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct Uri<'a, Extras = NoExtras> {
pub struct Uri<'a, NetVal = bitcoin::address::NetworkChecked, Extras = NoExtras>
where
NetVal: NetworkValidation,
{
/// The address provided in the URI.
///
/// This field is mandatory because the address is mandatory in BIP21.
pub address: bitcoin::Address,
pub address: bitcoin::Address<NetVal>,

/// Number of satoshis requested as payment.
pub amount: Option<bitcoin::Amount>,
Expand All @@ -97,12 +101,12 @@ pub struct Uri<'a, Extras = NoExtras> {
pub extras: Extras,
}

impl<'a, T: Default> Uri<'a, T> {
impl<'a, NetVal: NetworkValidation, T: Default> Uri<'a, NetVal, T> {
/// Creates an URI with defaults.
///
/// This sets all fields except `address` to default values.
/// They can be overwritten in subsequent assignments before displaying the URI.
pub fn new(address: bitcoin::Address) -> Self {
pub fn new(address: bitcoin::Address<NetVal>) -> Self {
Uri {
address,
amount: None,
Expand All @@ -113,12 +117,12 @@ impl<'a, T: Default> Uri<'a, T> {
}
}

impl<'a, T> Uri<'a, T> {
impl<'a, NetVal: NetworkValidation, T> Uri<'a, NetVal, T> {
/// Creates an URI with defaults.
///
/// This sets all fields except `address` and `extras` to default values.
/// They can be overwritten in subsequent assignments before displaying the URI.
pub fn with_extras(address: bitcoin::Address, extras: T) -> Self {
pub fn with_extras(address: bitcoin::Address<NetVal>, extras: T) -> Self {
Uri {
address,
amount: None,
Expand All @@ -129,6 +133,38 @@ impl<'a, T> Uri<'a, T> {
}
}

impl<'a, T> Uri<'a, bitcoin::address::NetworkUnchecked, T> {
/// Checks that the bitcoin network in the URI is `network`.
pub fn require_network(self, network: bitcoin::Network) -> Result<Uri<'a, bitcoin::address::NetworkChecked, T>, InvalidNetworkError> {
if self.address.is_valid_for_network(network) {
Ok(self.assume_checked())
} else {
Err(InvalidNetworkError {
required: network,
found: self.address.network,
})
}
}

/// Marks URI validated without checks.
pub fn assume_checked(self) -> Uri<'a, bitcoin::address::NetworkChecked, T> {
Uri {
address: self.address.assume_checked(),
amount: self.amount,
label: self.label,
message: self.message,
extras: self.extras,
}
}
}

/// An error returned when network validation fails.
#[derive(Debug, Clone)]
pub struct InvalidNetworkError {
required: bitcoin::Network,
found: bitcoin::Network,
}

/// Abstracted stringly parameter in the URI.
///
/// This type abstracts the parameter that may be encoded allowing lazy decoding, possibly even
Expand Down Expand Up @@ -355,7 +391,7 @@ mod tests {
#[test]
fn just_address() {
let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd";
let uri = input.parse::<Uri<'_>>().unwrap();
let uri = input.parse::<Uri<'_, _>>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap();
assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd");
assert!(uri.amount.is_none());
assert!(uri.label.is_none());
Expand All @@ -367,7 +403,7 @@ mod tests {
#[test]
fn address_with_name() {
let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?label=Luke-Jr";
let uri = input.parse::<Uri<'_>>().unwrap();
let uri = input.parse::<Uri<'_, _>>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap();
let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap();
assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd");
assert_eq!(label, "Luke-Jr");
Expand All @@ -381,7 +417,7 @@ mod tests {
#[test]
fn request_20_point_30_btc_to_luke_dash_jr() {
let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?amount=20.3&label=Luke-Jr";
let uri = input.parse::<Uri<'_>>().unwrap();
let uri = input.parse::<Uri<'_, _>>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap();
let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap();
assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd");
assert_eq!(label, "Luke-Jr");
Expand All @@ -395,7 +431,7 @@ mod tests {
#[test]
fn request_50_btc_with_message() {
let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz";
let uri = input.parse::<Uri<'_>>().unwrap();
let uri = input.parse::<Uri<'_, _>>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap();
let label: Cow<'_, str> = uri.label.clone().unwrap().try_into().unwrap();
let message: Cow<'_, str> = uri.message.clone().unwrap().try_into().unwrap();
assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd");
Expand All @@ -409,14 +445,14 @@ mod tests {
#[test]
fn required_not_understood() {
let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?req-somethingyoudontunderstand=50&req-somethingelseyoudontget=999";
let uri = input.parse::<Uri<'_>>();
let uri = input.parse::<Uri<'_, _>>();
assert!(uri.is_err());
}

#[test]
fn required_understood() {
let input = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd?somethingyoudontunderstand=50&somethingelseyoudontget=999";
let uri = input.parse::<Uri<'_>>().unwrap();
let uri = input.parse::<Uri<'_, _>>().unwrap().require_network(bitcoin::Network::Bitcoin).unwrap();
assert_eq!(uri.address.to_string(), "1andreas3batLhQa2FawWjeyjCqyBzypd");
assert!(uri.amount.is_none());
assert!(uri.label.is_none());
Expand Down
4 changes: 2 additions & 2 deletions src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! Check [`SerializeParams`] to get started.
use alloc::borrow::Cow;
use bitcoin::util::amount::Denomination;
use bitcoin::amount::Denomination;
use core::fmt;
use super::{Uri, Param, ParamInner};

Expand Down Expand Up @@ -121,7 +121,7 @@ fn maybe_display_param(writer: &mut impl fmt::Write, key: impl fmt::Display, val

/// Formats QR-code-optimized URI if alternate form (`{:#}`) is used.
#[rustfmt::skip]
impl<'a, T> fmt::Display for Uri<'a, T> where for<'b> &'b T: SerializeParams {
impl<'a, T> fmt::Display for Uri<'a, bitcoin::address::NetworkChecked, T> where for<'b> &'b T: SerializeParams {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if f.alternate() {
write!(f, "BITCOIN:{:#}", self.address)?;
Expand Down

0 comments on commit da1a87b

Please sign in to comment.