Skip to content
This repository was archived by the owner on Apr 13, 2021. It is now read-only.

Fix linter nits #10

Merged
merged 2 commits into from
Nov 27, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/de.rs
Original file line number Diff line number Diff line change
@@ -80,11 +80,7 @@ mod hrp_sm {
}

fn is_final(&self) -> bool {
if *self == States::ParseL || *self == States::ParseN {
false
} else {
true
}
!(*self == States::ParseL || *self == States::ParseN)
}
}

@@ -586,6 +582,9 @@ impl FromBase32 for Route {
}
}

/// Errors that indicate what is wrong with the invoice. They have some granularity for debug
/// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
#[allow(missing_docs)]
#[derive(PartialEq, Debug, Clone)]
pub enum ParseError {
Bech32Error(bech32::Error),
@@ -605,13 +604,22 @@ pub enum ParseError {
InvalidScriptHashLength,
InvalidRecoveryId,
InvalidSliceLength(String),

/// Not an error, but used internally to signal that a part of the invoice should be ignored
/// according to BOLT11
Skip,
TimestampOverflow,
}

/// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
/// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
/// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
#[derive(PartialEq, Debug, Clone)]
pub enum ParseOrSemanticError {
/// The invoice couldn't be decoded
ParseError(ParseError),

/// The invoice could be decoded but violates the BOLT11 standard
SemanticError(::SemanticError),
}

@@ -710,7 +718,6 @@ mod test {
use de::ParseError;
use secp256k1::{PublicKey, Secp256k1};
use bech32::u5;
use SignedRawInvoice;
use bitcoin_hashes::hex::FromHex;
use bitcoin_hashes::sha256::Sha256Hash;

128 changes: 96 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
#![deny(missing_docs)]
#![deny(non_upper_case_globals)]
#![deny(non_camel_case_types)]
#![deny(non_snake_case)]
#![deny(unused_mut)]

#![cfg_attr(feature = "strict", deny(warnings))]

//! This crate provides data structures to represent
//! [lightning BOLT11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md)
//! invoices and functions to create, encode and decode these. If you just want to use the standard
//! en-/decoding functionality this should get you started:
//!
//! * For parsing use `str::parse::<Invoice>(&self)` (see the docs of `impl FromStr for Invoice`)
//! * For constructing invoices use the `InvoiceBuilder`
//! * For serializing invoices use the `Display`/`ToString` traits
extern crate bech32;
extern crate bitcoin_hashes;
extern crate num_traits;
@@ -37,8 +54,8 @@ const MAX_EXPIRY_TIME: u64 = 60 * 60 * 24 * 356;
/// please open an issue. If all tests pass you should be able to use this library safely by just
/// removing this function till we patch it accordingly.
fn __system_time_size_check() {
/// Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
/// a `Duration` since `SystemTime::UNIX_EPOCH`.
// Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
// a `Duration` since `SystemTime::UNIX_EPOCH`.
unsafe { std::mem::transmute::<SystemTime, [u8; 16]>(UNIX_EPOCH); }
}

@@ -156,10 +173,15 @@ pub struct Invoice {
signed_invoice: SignedRawInvoice,
}

/// Represents the description of an invoice which has to be either a directly included string or
/// a hash of a description provided out of band.
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum InvoiceDescription<'f> {
/// Reference to the directly supplied description in the invoice
Direct(&'f Description),
Hash(&'f Sha256)

/// Reference to the description's hash included in the invoice
Hash(&'f Sha256),
}

/// Represents a signed `RawInvoice` with cached hash. The signature is not checked and may be
@@ -188,6 +210,8 @@ pub struct SignedRawInvoice {
/// Represents an syntactically correct Invoice for a payment on the lightning network,
/// but without the signature information.
/// De- and encoding should not lead to information loss but may lead to different hashes.
///
/// For methods without docs see the corresponding methods in `Invoice`.
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct RawInvoice {
/// human readable part
@@ -246,11 +270,11 @@ impl SiPrefix {
/// Returns the multiplier to go from a BTC value to picoBTC implied by this SiPrefix.
/// This is effectively 10^12 * the prefix multiplier
pub fn multiplier(&self) -> u64 {
match self {
&SiPrefix::Milli => 1_000_000_000,
&SiPrefix::Micro => 1_000_000,
&SiPrefix::Nano => 1_000,
&SiPrefix::Pico => 1,
match *self {
SiPrefix::Milli => 1_000_000_000,
SiPrefix::Micro => 1_000_000,
SiPrefix::Nano => 1_000,
SiPrefix::Pico => 1,
}
}

@@ -263,6 +287,8 @@ impl SiPrefix {
}
}

/// Enum representing the crypto currencies supported by this library
#[allow(missing_docs)]
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum Currency {
Bitcoin,
@@ -279,6 +305,9 @@ pub enum RawTaggedField {
}

/// Tagged field with known tag
///
/// For descriptions of the enum values please refer to the enclosed type's docs.
#[allow(missing_docs)]
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum TaggedField {
PaymentHash(Sha256),
@@ -322,6 +351,7 @@ pub struct MinFinalCltvExpiry(pub u64);

// TODO: better types instead onf byte arrays
/// Fallback address in case no LN payment is possible
#[allow(missing_docs)]
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum Fallback {
SegWitProgram {
@@ -344,18 +374,28 @@ pub struct Signature(pub RecoverableSignature);
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct Route(Vec<RouteHop>);

/// Node on a private route
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct RouteHop {
/// Node's public key
pub pubkey: PublicKey,

/// Which channel of this node we would be using
pub short_channel_id: [u8; 8],

/// Fee charged by this node per transaction
pub fee_base_msat: u32,

/// Fee charged by this node proportional to the amount routed
pub fee_proportional_millionths: u32,

/// Delta substracted by this node from incoming cltv_expiry value
pub cltv_expiry_delta: u16,
}

/// Tag constants as specified in BOLT11
#[allow(missing_docs)]
pub mod constants {
use bech32::u5;

pub const TAG_PAYMENT_HASH: u8 = 1;
pub const TAG_DESCRIPTION: u8 = 13;
pub const TAG_PAYEE_PUB_KEY: u8 = 19;
@@ -366,17 +406,6 @@ pub mod constants {
pub const TAG_ROUTE: u8 = 3;
}

/// FOR INTERNAL USE ONLY! READ BELOW!
///
/// It's a convenience function to convert `u8` tags to `u5` tags. Therefore `tag` has to
/// be in range `[0..32]`.
///
/// # Panics
/// If the `tag` value is not in the range `[0..32]`.
fn as_u5(tag: u8) -> u5 {
u5::try_from_u8(tag).unwrap()
}

impl InvoiceBuilder<tb::False, tb::False, tb::False> {
/// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before
/// `InvoiceBuilder::build(self)` becomes available.
@@ -633,7 +662,7 @@ impl SignedRawInvoice {
recovered_pub_key = Some(recovered);
}

let pub_key = included_pub_key.or(recovered_pub_key.as_ref())
let pub_key = included_pub_key.or_else(|| recovered_pub_key.as_ref())
.expect("One is always present");

let hash = Message::from_slice(&self.hash[..])
@@ -671,13 +700,14 @@ impl SignedRawInvoice {
/// ```
macro_rules! find_extract {
($iter:expr, $enm:pat, $enm_var:ident) => {
$iter.filter_map(|tf| match tf {
&$enm => Some($enm_var),
$iter.filter_map(|tf| match *tf {
$enm => Some($enm_var),
_ => None,
}).next()
};
}

#[allow(missing_docs)]
impl RawInvoice {
/// Hash the HRP as bytes and signatureless data part.
fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
@@ -741,8 +771,8 @@ impl RawInvoice {
// function's type signature.
// TODO: refactor once impl Trait is available
fn match_raw(raw: &RawTaggedField) -> Option<&TaggedField> {
match raw {
&RawTaggedField::KnownSemantics(ref tf) => Some(tf),
match *raw {
RawTaggedField::KnownSemantics(ref tf) => Some(tf),
_ => None,
}
}
@@ -777,14 +807,14 @@ impl RawInvoice {
pub fn fallbacks(&self) -> Vec<&Fallback> {
self.known_tagged_fields().filter_map(|tf| match tf {
&TaggedField::Fallback(ref f) => Some(f),
num_traits => None,
_ => None,
}).collect::<Vec<&Fallback>>()
}

pub fn routes(&self) -> Vec<&Route> {
self.known_tagged_fields().filter_map(|tf| match tf {
&TaggedField::Route(ref r) => Some(r),
num_traits => None,
_ => None,
}).collect::<Vec<&Route>>()
}

@@ -854,7 +884,8 @@ impl Deref for PositiveTimestamp {
}

impl Invoice {
fn into_signed_raw(self) -> SignedRawInvoice {
/// Transform the `Invoice` into it's unchecked version
pub fn into_signed_raw(self) -> SignedRawInvoice {
self.signed_invoice
}

@@ -924,6 +955,7 @@ impl Invoice {
Ok(invoice)
}

/// Returns the `Invoice`'s timestamp (should equal it's creation time)
pub fn timestamp(&self) -> &SystemTime {
self.signed_invoice.raw_invoice().data.timestamp.as_time()
}
@@ -934,10 +966,12 @@ impl Invoice {
self.signed_invoice.raw_invoice().known_tagged_fields()
}

/// Returns the hash to which we will receive the preimage on completion of the payment
pub fn payment_hash(&self) -> &Sha256 {
self.signed_invoice.payment_hash().expect("checked by constructor")
}

/// Return the description or a hash of it for longer ones
pub fn description(&self) -> InvoiceDescription {
if let Some(ref direct) = self.signed_invoice.description() {
return InvoiceDescription::Direct(direct);
@@ -947,34 +981,42 @@ impl Invoice {
unreachable!("ensured by constructor");
}

/// Get the payee's public key if one was included in the invoice
pub fn payee_pub_key(&self) -> Option<&PayeePubKey> {
self.signed_invoice.payee_pub_key()
}

/// Recover the payee's public key (only to be used if none was included in the invoice)
pub fn recover_payee_pub_key(&self) -> PayeePubKey {
self.signed_invoice.recover_payee_pub_key().expect("was checked by constructor")
}

/// Returns the invoice's expiry time if present
pub fn expiry_time(&self) -> Option<&ExpiryTime> {
self.signed_invoice.expiry_time()
}

/// Returns the invoice's `min_cltv_expiry` time if present
pub fn min_final_cltv_expiry(&self) -> Option<&MinFinalCltvExpiry> {
self.signed_invoice.min_final_cltv_expiry()
}

/// Returns a list of all fallback addresses
pub fn fallbacks(&self) -> Vec<&Fallback> {
self.signed_invoice.fallbacks()
}

/// Returns a list of all routes included in the invoice
pub fn routes(&self) -> Vec<&Route> {
self.signed_invoice.routes()
}

/// Returns the currency for which the invoice was issued
pub fn currency(&self) -> Currency {
self.signed_invoice.currency()
}

/// Returns the amount if specified in the invoice as pico <currency>.
pub fn amount_pico_btc(&self) -> Option<u64> {
self.signed_invoice.amount_pico_btc()
}
@@ -1018,6 +1060,7 @@ impl Description {
}
}

/// Returns the underlying description `String`
pub fn into_inner(self) -> String {
self.0
}
@@ -1052,6 +1095,9 @@ impl Deref for PayeePubKey {
}

impl ExpiryTime {
/// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
/// overflow on adding the `EpiryTime` to it then this function will return a
/// `CreationError::ExpiryTimeOutOfBounds`.
pub fn from_seconds(seconds: u64) -> Result<ExpiryTime, CreationError> {
if seconds <= MAX_EXPIRY_TIME {
Ok(ExpiryTime(Duration::from_secs(seconds)))
@@ -1060,6 +1106,9 @@ impl ExpiryTime {
}
}

/// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
/// would overflow on adding the `EpiryTime` to it then this function will return a
/// `CreationError::ExpiryTimeOutOfBounds`.
pub fn from_duration(duration: Duration) -> Result<ExpiryTime, CreationError> {
if duration.as_secs() <= MAX_EXPIRY_TIME {
Ok(ExpiryTime(duration))
@@ -1068,16 +1117,19 @@ impl ExpiryTime {
}
}

/// Returns the expiry time in seconds
pub fn as_seconds(&self) -> u64 {
self.0.as_secs()
}

/// Returns a reference to the underlying `Duration` (=expiry time)
pub fn as_duration(&self) -> &Duration {
&self.0
}
}

impl Route {
/// Create a new (partial) route from a list of hops
pub fn new(hops: Vec<RouteHop>) -> Result<Route, CreationError> {
if hops.len() <= 12 {
Ok(Route(hops))
@@ -1086,7 +1138,8 @@ impl Route {
}
}

fn into_inner(self) -> Vec<RouteHop> {
/// Returrn the underlying vector of hops
pub fn into_inner(self) -> Vec<RouteHop> {
self.0
}
}
@@ -1141,21 +1194,33 @@ pub enum CreationError {
/// requirements sections in BOLT #11
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum SemanticError {
/// The invoice is missing the mandatory payment hash
NoPaymentHash,

/// The invoice has multiple payment hashes which isn't allowed
MultiplePaymentHashes,

/// No description or description hash are part of the invoice
NoDescription,

/// The invoice contains multiple descriptions and/or description hashes which isn't allowed
MultipleDescriptions,

/// The recovery id doesn't fit the signature/pub key
InvalidRecoveryId,

/// The invoice's signature is invalid
InvalidSignature,
}

/// When signing using a fallible method either an user-supplied `SignError` or a `CreationError`
/// may occur.
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum SignOrCreationError<S> {
/// An error occurred during signing
SignError(S),

/// An error occurred while building the transaction
CreationError(CreationError),
}

@@ -1183,7 +1248,6 @@ mod test {
#[test]
fn test_calc_invoice_hash() {
use ::{RawInvoice, RawHrp, RawDataPart, Currency, PositiveTimestamp};
use secp256k1::*;
use ::TaggedField::*;

let invoice = RawInvoice {
@@ -1222,7 +1286,7 @@ mod test {
use {SignedRawInvoice, Signature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
PositiveTimestamp};

let mut invoice = SignedRawInvoice {
let invoice = SignedRawInvoice {
raw_invoice: RawInvoice {
hrp: RawHrp {
currency: Currency::Bitcoin,
6 changes: 3 additions & 3 deletions src/ser.rs
Original file line number Diff line number Diff line change
@@ -46,9 +46,9 @@ impl Display for RawHrp {

impl Display for Currency {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let currency_code = match self {
&Currency::Bitcoin => "bc",
&Currency::BitcoinTestnet => "tb",
let currency_code = match *self {
Currency::Bitcoin => "bc",
Currency::BitcoinTestnet => "tb",
};
write!(f, "{}", currency_code)
}