Merge pull request #1223 from lightning-signer/2021-12-invoice-nostd

Adapt lightning-invoice to no_std
This commit is contained in:
Matt Corallo 2022-01-06 19:25:36 +00:00 committed by GitHub
commit b62b244c3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 367 additions and 156 deletions

View file

@ -118,6 +118,11 @@ jobs:
# check if there is a conflict between no-std and the c_bindings cfg
RUSTFLAGS="--cfg=c_bindings" cargo test --verbose --color always --no-default-features --features=no-std
cd ..
cd lightning-invoice
cargo test --verbose --color always --no-default-features --features no-std
# check if there is a conflict between no-std and the default std feature
cargo test --verbose --color always --features no-std
cd ..
- name: Test on no-std builds Rust ${{ matrix.toolchain }} and full code-linking for coverage generation
if: "matrix.build-no-std && matrix.coverage"
run: |

View file

@ -8,13 +8,20 @@ license = "MIT OR Apache-2.0"
keywords = [ "lightning", "bitcoin", "invoice", "BOLT11" ]
readme = "README.md"
[features]
default = ["std"]
no-std = ["hashbrown", "lightning/no-std", "core2/alloc"]
std = ["bitcoin_hashes/std", "num-traits/std", "lightning/std"]
[dependencies]
bech32 = "0.8"
lightning = { version = "0.0.104", path = "../lightning" }
secp256k1 = { version = "0.20", features = ["recovery"] }
num-traits = "0.2.8"
bitcoin_hashes = "0.10"
lightning = { version = "0.0.104", path = "../lightning", default-features = false }
secp256k1 = { version = "0.20", default-features = false, features = ["recovery", "alloc"] }
num-traits = { version = "0.2.8", default-features = false }
bitcoin_hashes = { version = "0.10", default-features = false }
hashbrown = { version = "0.11", optional = true }
core2 = { version = "0.3.0", default-features = false, optional = true }
[dev-dependencies]
lightning = { version = "0.0.104", path = "../lightning", default-features = false, features = ["_test_utils"] }
hex = "0.3"
lightning = { version = "0.0.104", path = "../lightning", features = ["_test_utils"] }

View file

@ -1,15 +1,17 @@
#[cfg(feature = "std")]
use std::error;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::num::ParseIntError;
use std::str;
use std::str::FromStr;
use core::fmt;
use core::fmt::{Display, Formatter};
use core::num::ParseIntError;
use core::str;
use core::str::FromStr;
use bech32;
use bech32::{u5, FromBase32};
use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256;
use crate::prelude::*;
use lightning::ln::PaymentSecret;
use lightning::routing::network_graph::RoutingFees;
use lightning::routing::router::{RouteHint, RouteHintHop};
@ -28,7 +30,7 @@ use self::hrp_sm::parse_hrp;
/// State machine to parse the hrp
mod hrp_sm {
use std::ops::Range;
use core::ops::Range;
#[derive(PartialEq, Eq, Debug)]
enum States {
@ -723,8 +725,10 @@ impl Display for ParseOrSemanticError {
}
}
#[cfg(feature = "std")]
impl error::Error for ParseError {}
#[cfg(feature = "std")]
impl error::Error for ParseOrSemanticError {}
macro_rules! from_error {

View file

@ -6,6 +6,7 @@
#![deny(broken_intra_doc_links)]
#![cfg_attr(feature = "strict", deny(warnings))]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
//! This crate provides data structures to represent
//! [lightning BOLT11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md)
@ -15,6 +16,10 @@
//! * 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
#[cfg(not(any(feature = "std", feature = "no-std")))]
compile_error!("at least one of the `std` or `no-std` features must be enabled");
pub mod payment;
pub mod utils;
@ -23,6 +28,12 @@ extern crate bitcoin_hashes;
#[macro_use] extern crate lightning;
extern crate num_traits;
extern crate secp256k1;
extern crate alloc;
#[cfg(any(test, feature = "std"))]
extern crate core;
#[cfg(feature = "std")]
use std::time::SystemTime;
use bech32::u5;
use bitcoin_hashes::Hash;
@ -37,22 +48,47 @@ use secp256k1::key::PublicKey;
use secp256k1::{Message, Secp256k1};
use secp256k1::recovery::RecoverableSignature;
use std::fmt::{Display, Formatter, self};
use std::iter::FilterMap;
use std::ops::Deref;
use std::slice::Iter;
use std::time::{SystemTime, Duration, UNIX_EPOCH};
use core::fmt::{Display, Formatter, self};
use core::iter::FilterMap;
use core::ops::Deref;
use core::slice::Iter;
use core::time::Duration;
mod de;
mod ser;
mod tb;
mod prelude {
#[cfg(feature = "hashbrown")]
extern crate hashbrown;
pub use alloc::{vec, vec::Vec, string::String, collections::VecDeque, boxed::Box};
#[cfg(not(feature = "hashbrown"))]
pub use std::collections::{HashMap, HashSet, hash_map};
#[cfg(feature = "hashbrown")]
pub use self::hashbrown::{HashMap, HashSet, hash_map};
pub use alloc::string::ToString;
}
use prelude::*;
/// Sync compat for std/no_std
#[cfg(feature = "std")]
mod sync {
pub use ::std::sync::{Mutex, MutexGuard};
}
/// Sync compat for std/no_std
#[cfg(not(feature = "std"))]
mod sync;
pub use de::{ParseError, ParseOrSemanticError};
// TODO: fix before 2037 (see rust PR #55527)
/// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
/// one of the unit tests, please run them.
const SYSTEM_TIME_MAX_UNIX_TIMESTAMP: u64 = std::i32::MAX as u64;
const SYSTEM_TIME_MAX_UNIX_TIMESTAMP: u64 = core::i32::MAX as u64;
/// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
/// it should be rather low as long as we still have to support 32bit time representations
@ -77,10 +113,11 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY: u64 = 18;
/// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
/// 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.
#[cfg(feature = "std")]
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`.
unsafe { std::mem::transmute_copy::<SystemTime, [u8; 16]>(&UNIX_EPOCH); }
unsafe { core::mem::transmute_copy::<SystemTime, [u8; 16]>(&SystemTime::UNIX_EPOCH); }
}
@ -94,32 +131,41 @@ fn __system_time_size_check() {
/// If this function fails this is considered a bug. Please open an issue describing your
/// platform and stating your current system time.
///
/// Note that this currently does nothing in `no_std` environments, because they don't have
/// a `SystemTime` implementation.
///
/// # Panics
/// If the check fails this function panics. By calling this function on startup you ensure that
/// this wont happen at an arbitrary later point in time.
pub fn check_platform() {
// The upper and lower bounds of `SystemTime` are not part of its public contract and are
// platform specific. That's why we have to test if our assumptions regarding these bounds
// hold on the target platform.
//
// If this test fails on your platform, please don't use the library and open an issue
// instead so we can resolve the situation. Currently this library is tested on:
// * Linux (64bit)
let fail_date = UNIX_EPOCH + Duration::from_secs(SYSTEM_TIME_MAX_UNIX_TIMESTAMP);
let year = Duration::from_secs(60 * 60 * 24 * 365);
#[cfg(feature = "std")]
check_system_time_bounds();
}
// Make sure that the library will keep working for another year
assert!(fail_date.duration_since(SystemTime::now()).unwrap() > year);
#[cfg(feature = "std")]
fn check_system_time_bounds() {
// The upper and lower bounds of `SystemTime` are not part of its public contract and are
// platform specific. That's why we have to test if our assumptions regarding these bounds
// hold on the target platform.
//
// If this test fails on your platform, please don't use the library and open an issue
// instead so we can resolve the situation. Currently this library is tested on:
// * Linux (64bit)
let fail_date = SystemTime::UNIX_EPOCH + Duration::from_secs(SYSTEM_TIME_MAX_UNIX_TIMESTAMP);
let year = Duration::from_secs(60 * 60 * 24 * 365);
let max_ts = PositiveTimestamp::from_unix_timestamp(
SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
).unwrap();
let max_exp = ::ExpiryTime::from_seconds(MAX_EXPIRY_TIME).unwrap();
// Make sure that the library will keep working for another year
assert!(fail_date.duration_since(SystemTime::now()).unwrap() > year);
assert_eq!(
(*max_ts.as_time() + *max_exp.as_duration()).duration_since(UNIX_EPOCH).unwrap().as_secs(),
SYSTEM_TIME_MAX_UNIX_TIMESTAMP
);
let max_ts = PositiveTimestamp::from_unix_timestamp(
SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
).unwrap();
let max_exp = ::ExpiryTime::from_seconds(MAX_EXPIRY_TIME).unwrap();
assert_eq!(
(max_ts.as_time() + *max_exp.as_duration()).duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(),
SYSTEM_TIME_MAX_UNIX_TIMESTAMP
);
}
@ -142,6 +188,9 @@ pub fn check_platform() {
///
/// use lightning_invoice::{Currency, InvoiceBuilder};
///
/// # #[cfg(not(feature = "std"))]
/// # fn main() {}
/// # #[cfg(feature = "std")]
/// # fn main() {
/// let private_key = SecretKey::from_slice(
/// &[
@ -186,11 +235,11 @@ pub struct InvoiceBuilder<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S:
tagged_fields: Vec<TaggedField>,
error: Option<CreationError>,
phantom_d: std::marker::PhantomData<D>,
phantom_h: std::marker::PhantomData<H>,
phantom_t: std::marker::PhantomData<T>,
phantom_c: std::marker::PhantomData<C>,
phantom_s: std::marker::PhantomData<S>,
phantom_d: core::marker::PhantomData<D>,
phantom_h: core::marker::PhantomData<H>,
phantom_t: core::marker::PhantomData<T>,
phantom_c: core::marker::PhantomData<C>,
phantom_s: core::marker::PhantomData<S>,
}
/// Represents a syntactically and semantically correct lightning BOLT11 invoice.
@ -285,9 +334,9 @@ pub struct RawDataPart {
///
/// # Invariants
/// The UNIX timestamp representing the stored time has to be positive and small enough so that
/// a `EpiryTime` can be added to it without an overflow.
/// a `ExpiryTime` can be added to it without an overflow.
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct PositiveTimestamp(SystemTime);
pub struct PositiveTimestamp(Duration);
/// SI prefixes for the human readable part
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
@ -459,11 +508,11 @@ impl InvoiceBuilder<tb::False, tb::False, tb::False, tb::False, tb::False> {
tagged_fields: Vec::new(),
error: None,
phantom_d: std::marker::PhantomData,
phantom_h: std::marker::PhantomData,
phantom_t: std::marker::PhantomData,
phantom_c: std::marker::PhantomData,
phantom_s: std::marker::PhantomData,
phantom_d: core::marker::PhantomData,
phantom_h: core::marker::PhantomData,
phantom_t: core::marker::PhantomData,
phantom_c: core::marker::PhantomData,
phantom_s: core::marker::PhantomData,
}
}
}
@ -479,11 +528,11 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
tagged_fields: self.tagged_fields,
error: self.error,
phantom_d: std::marker::PhantomData,
phantom_h: std::marker::PhantomData,
phantom_t: std::marker::PhantomData,
phantom_c: std::marker::PhantomData,
phantom_s: std::marker::PhantomData,
phantom_d: core::marker::PhantomData,
phantom_h: core::marker::PhantomData,
phantom_t: core::marker::PhantomData,
phantom_c: core::marker::PhantomData,
phantom_s: core::marker::PhantomData,
}
}
@ -589,7 +638,8 @@ impl<D: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, tb::F
}
impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb::False, C, S> {
/// Sets the timestamp.
/// Sets the timestamp to a specific [`SystemTime`].
#[cfg(feature = "std")]
pub fn timestamp(mut self, time: SystemTime) -> InvoiceBuilder<D, H, tb::True, C, S> {
match PositiveTimestamp::from_system_time(time) {
Ok(t) => self.timestamp = Some(t),
@ -599,7 +649,18 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb
self.set_flags()
}
/// Sets the timestamp to the current UNIX timestamp.
/// Sets the timestamp to a duration since the UNIX epoch.
pub fn duration_since_epoch(mut self, time: Duration) -> InvoiceBuilder<D, H, tb::True, C, S> {
match PositiveTimestamp::from_duration_since_epoch(time) {
Ok(t) => self.timestamp = Some(t),
Err(e) => self.error = Some(e),
}
self.set_flags()
}
/// Sets the timestamp to the current system time.
#[cfg(feature = "std")]
pub fn current_timestamp(mut self) -> InvoiceBuilder<D, H, tb::True, C, S> {
let now = PositiveTimestamp::from_system_time(SystemTime::now());
self.timestamp = Some(now.expect("for the foreseeable future this shouldn't happen"));
@ -776,7 +837,7 @@ impl SignedRawInvoice {
macro_rules! find_extract {
($iter:expr, $enm:pat, $enm_var:ident) => {
find_all_extract!($iter, $enm, $enm_var).next()
};
};
}
/// Finds the all elements of an enum stream of a given variant and extracts one member of the
@ -799,11 +860,11 @@ macro_rules! find_extract {
/// ```
macro_rules! find_all_extract {
($iter:expr, $enm:pat, $enm_var:ident) => {
$iter.filter_map(|tf| match *tf {
$iter.filter_map(|tf| match *tf {
$enm => Some($enm_var),
_ => None,
})
};
};
}
#[allow(missing_docs)]
@ -949,49 +1010,52 @@ impl PositiveTimestamp {
if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
Err(CreationError::TimestampOutOfBounds)
} else {
Ok(PositiveTimestamp(UNIX_EPOCH + Duration::from_secs(unix_seconds)))
Ok(PositiveTimestamp(Duration::from_secs(unix_seconds)))
}
}
/// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
/// the Range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
/// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
/// `CreationError::TimestampOutOfBounds`.
#[cfg(feature = "std")]
pub fn from_system_time(time: SystemTime) -> Result<Self, CreationError> {
if time
.duration_since(UNIX_EPOCH)
.map(|t| t.as_secs() <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)
.unwrap_or(true)
{
Ok(PositiveTimestamp(time))
} else {
time.duration_since(SystemTime::UNIX_EPOCH)
.map(Self::from_duration_since_epoch)
.unwrap_or(Err(CreationError::TimestampOutOfBounds))
}
/// Create a new `PositiveTimestamp` from a `Duration` since the UNIX epoch in
/// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
/// `CreationError::TimestampOutOfBounds`.
pub fn from_duration_since_epoch(duration: Duration) -> Result<Self, CreationError> {
if duration.as_secs() <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
Ok(PositiveTimestamp(duration))
} else {
Err(CreationError::TimestampOutOfBounds)
}
}
/// Returns the UNIX timestamp representing the stored time
pub fn as_unix_timestamp(&self) -> u64 {
self.0.duration_since(UNIX_EPOCH)
.expect("ensured by type contract/constructors")
.as_secs()
self.0.as_secs()
}
/// Returns a reference to the internal `SystemTime` time representation
pub fn as_time(&self) -> &SystemTime {
&self.0
}
}
impl Into<SystemTime> for PositiveTimestamp {
fn into(self) -> SystemTime {
/// Returns the duration of the stored time since the UNIX epoch
pub fn as_duration_since_epoch(&self) -> Duration {
self.0
}
/// Returns the `SystemTime` representing the stored time
#[cfg(feature = "std")]
pub fn as_time(&self) -> SystemTime {
SystemTime::UNIX_EPOCH + self.0
}
}
impl Deref for PositiveTimestamp {
type Target = SystemTime;
fn deref(&self) -> &Self::Target {
&self.0
#[cfg(feature = "std")]
impl Into<SystemTime> for PositiveTimestamp {
fn into(self) -> SystemTime {
SystemTime::UNIX_EPOCH + self.0
}
}
@ -1132,11 +1196,17 @@ impl Invoice {
Ok(invoice)
}
/// Returns the `Invoice`'s timestamp (should equal it's creation time)
pub fn timestamp(&self) -> &SystemTime {
/// Returns the `Invoice`'s timestamp (should equal its creation time)
#[cfg(feature = "std")]
pub fn timestamp(&self) -> SystemTime {
self.signed_invoice.raw_invoice().data.timestamp.as_time()
}
/// Returns the `Invoice`'s timestamp as a duration since the UNIX epoch
pub fn duration_since_epoch(&self) -> Duration {
self.signed_invoice.raw_invoice().data.timestamp.0
}
/// Returns an iterator over all tagged fields of this Invoice.
///
/// (C-not exported) As there is not yet a manual mapping for a FilterMap
@ -1190,11 +1260,13 @@ impl Invoice {
}
/// Returns whether the invoice has expired.
#[cfg(feature = "std")]
pub fn is_expired(&self) -> bool {
Self::is_expired_from_epoch(self.timestamp(), self.expiry_time())
Self::is_expired_from_epoch(&self.timestamp(), self.expiry_time())
}
/// Returns whether the expiry time from the given epoch has passed.
#[cfg(feature = "std")]
pub(crate) fn is_expired_from_epoch(epoch: &SystemTime, expiry_time: Duration) -> bool {
match epoch.elapsed() {
Ok(elapsed) => elapsed > expiry_time,
@ -1202,6 +1274,12 @@ impl Invoice {
}
}
/// Returns whether the expiry time would pass at the given point in time.
/// `at_time` is the timestamp as a duration since the UNIX epoch.
pub fn would_expire(&self, at_time: Duration) -> bool {
self.duration_since_epoch() + self.expiry_time() < at_time
}
/// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
/// [`DEFAULT_MIN_FINAL_CLTV_EXPIRY`].
pub fn min_final_cltv_expiry(&self) -> u64 {
@ -1430,6 +1508,7 @@ impl Display for CreationError {
}
}
#[cfg(feature = "std")]
impl std::error::Error for CreationError { }
/// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
@ -1485,6 +1564,7 @@ impl Display for SemanticError {
}
}
#[cfg(feature = "std")]
impl std::error::Error for SemanticError { }
/// When signing using a fallible method either an user-supplied `SignError` or a `CreationError`
@ -1516,15 +1596,15 @@ mod test {
fn test_system_time_bounds_assumptions() {
::check_platform();
assert_eq!(
::PositiveTimestamp::from_unix_timestamp(::SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1),
Err(::CreationError::TimestampOutOfBounds)
);
assert_eq!(
::PositiveTimestamp::from_unix_timestamp(::SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1),
Err(::CreationError::TimestampOutOfBounds)
);
assert_eq!(
::ExpiryTime::from_seconds(::MAX_EXPIRY_TIME + 1),
Err(::CreationError::ExpiryTimeOutOfBounds)
);
assert_eq!(
::ExpiryTime::from_seconds(::MAX_EXPIRY_TIME + 1),
Err(::CreationError::ExpiryTimeOutOfBounds)
);
}
#[test]
@ -1727,7 +1807,7 @@ mod test {
let builder = InvoiceBuilder::new(Currency::Bitcoin)
.description("Test".into())
.payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
.current_timestamp();
.duration_since_epoch(Duration::from_secs(1234567));
let invoice = builder.clone()
.amount_milli_satoshis(1500)
@ -1756,7 +1836,7 @@ mod test {
let builder = InvoiceBuilder::new(Currency::Bitcoin)
.payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
.current_timestamp()
.duration_since_epoch(Duration::from_secs(1234567))
.min_final_cltv_expiry(144);
let too_long_string = String::from_iter(
@ -1872,7 +1952,7 @@ mod test {
let builder = InvoiceBuilder::new(Currency::BitcoinTestnet)
.amount_milli_satoshis(123)
.timestamp(UNIX_EPOCH + Duration::from_secs(1234567))
.duration_since_epoch(Duration::from_secs(1234567))
.payee_pub_key(public_key.clone())
.expiry_time(Duration::from_secs(54321))
.min_final_cltv_expiry(144)
@ -1894,6 +1974,7 @@ mod test {
assert_eq!(invoice.amount_milli_satoshis(), Some(123));
assert_eq!(invoice.amount_pico_btc(), Some(1230));
assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
#[cfg(feature = "std")]
assert_eq!(
invoice.timestamp().duration_since(UNIX_EPOCH).unwrap().as_secs(),
1234567
@ -1925,7 +2006,7 @@ mod test {
.description("Test".into())
.payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
.payment_secret(PaymentSecret([0; 32]))
.current_timestamp()
.duration_since_epoch(Duration::from_secs(1234567))
.build_raw()
.unwrap()
.sign::<_, ()>(|hash| {
@ -1938,7 +2019,7 @@ mod test {
assert_eq!(invoice.min_final_cltv_expiry(), DEFAULT_MIN_FINAL_CLTV_EXPIRY);
assert_eq!(invoice.expiry_time(), Duration::from_secs(DEFAULT_EXPIRY_TIME));
assert!(!invoice.is_expired());
assert!(!invoice.would_expire(Duration::from_secs(1234568)));
}
#[test]
@ -1947,14 +2028,11 @@ mod test {
use secp256k1::Secp256k1;
use secp256k1::key::SecretKey;
let timestamp = SystemTime::now()
.checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME * 2))
.unwrap();
let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
.description("Test".into())
.payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
.payment_secret(PaymentSecret([0; 32]))
.timestamp(timestamp)
.duration_since_epoch(Duration::from_secs(1234567))
.build_raw()
.unwrap()
.sign::<_, ()>(|hash| {
@ -1965,6 +2043,6 @@ mod test {
.unwrap();
let invoice = Invoice::from_signed(signed_invoice).unwrap();
assert!(invoice.is_expired());
assert!(invoice.would_expire(Duration::from_secs(1234567 + DEFAULT_EXPIRY_TIME + 1)));
}
}

View file

@ -32,6 +32,9 @@
//! # extern crate lightning_invoice;
//! # extern crate secp256k1;
//! #
//! # #[cfg(feature = "no-std")]
//! # extern crate core2;
//! #
//! # use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
//! # use lightning::ln::msgs::LightningError;
@ -47,6 +50,11 @@
//! # use std::cell::RefCell;
//! # use std::ops::Deref;
//! #
//! # #[cfg(not(feature = "std"))]
//! # use core2::io;
//! # #[cfg(feature = "std")]
//! # use std::io;
//! #
//! # struct FakeEventProvider {}
//! # impl EventsProvider for FakeEventProvider {
//! # fn process_pending_events<H: Deref>(&self, handler: H) where H::Target: EventHandler {}
@ -78,7 +86,7 @@
//! #
//! # struct FakeScorer {}
//! # impl Writeable for FakeScorer {
//! # fn write<W: Writer>(&self, w: &mut W) -> Result<(), std::io::Error> { unimplemented!(); }
//! # fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { unimplemented!(); }
//! # }
//! # impl Score for FakeScorer {
//! # fn channel_penalty_msat(
@ -130,6 +138,7 @@ use crate::Invoice;
use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256::Hash as Sha256;
use crate::prelude::*;
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
use lightning::ln::msgs::LightningError;
@ -137,13 +146,14 @@ use lightning::routing::scoring::{LockableScore, Score};
use lightning::routing::router::{Payee, Route, RouteParameters};
use lightning::util::events::{Event, EventHandler};
use lightning::util::logger::Logger;
use crate::sync::Mutex;
use secp256k1::key::PublicKey;
use std::collections::hash_map::{self, HashMap};
use std::ops::Deref;
use std::sync::Mutex;
use std::time::{Duration, SystemTime};
use core::ops::Deref;
use core::time::Duration;
#[cfg(feature = "std")]
use std::time::SystemTime;
/// A utility for paying [`Invoice`]s and sending spontaneous payments.
///
@ -336,9 +346,11 @@ where
fn pay_internal<F: FnOnce(&Route) -> Result<PaymentId, PaymentSendFailure> + Copy>(
&self, params: &RouteParameters, payment_hash: PaymentHash, send_payment: F,
) -> Result<PaymentId, PaymentError> {
if has_expired(params) {
log_trace!(self.logger, "Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0));
return Err(PaymentError::Invoice("Invoice expired prior to send"));
#[cfg(feature = "std")] {
if has_expired(params) {
log_trace!(self.logger, "Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0));
return Err(PaymentError::Invoice("Invoice expired prior to send"));
}
}
let payer = self.payer.node_id();
@ -360,7 +372,7 @@ where
Err(e)
} else {
*retry_count += 1;
std::mem::drop(payment_cache);
core::mem::drop(payment_cache);
Ok(self.pay_internal(params, payment_hash, send_payment)?)
}
},
@ -398,9 +410,11 @@ where
return Err(());
}
if has_expired(params) {
log_trace!(self.logger, "Invoice expired for payment {}; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
return Err(());
#[cfg(feature = "std")] {
if has_expired(params) {
log_trace!(self.logger, "Invoice expired for payment {}; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
return Err(());
}
}
let payer = self.payer.node_id();
@ -444,9 +458,10 @@ where
}
fn expiry_time_from_unix_epoch(invoice: &Invoice) -> Duration {
invoice.timestamp().duration_since(SystemTime::UNIX_EPOCH).unwrap() + invoice.expiry_time()
invoice.signed_invoice.raw_invoice.data.timestamp.0 + invoice.expiry_time()
}
#[cfg(feature = "std")]
fn has_expired(params: &RouteParameters) -> bool {
if let Some(expiry_time) = params.payee.expiry_time {
Invoice::is_expired_from_epoch(&SystemTime::UNIX_EPOCH, Duration::from_secs(expiry_time))
@ -510,8 +525,8 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::{DEFAULT_EXPIRY_TIME, InvoiceBuilder, Currency};
use utils::create_invoice_from_channelmanager;
use crate::{InvoiceBuilder, Currency};
use utils::create_invoice_from_channelmanager_and_duration_since_epoch;
use bitcoin_hashes::sha256::Hash as Sha256;
use lightning::ln::PaymentPreimage;
use lightning::ln::features::{ChannelFeatures, NodeFeatures, InitFeatures};
@ -526,15 +541,17 @@ mod tests {
use std::cell::RefCell;
use std::collections::VecDeque;
use std::time::{SystemTime, Duration};
use DEFAULT_EXPIRY_TIME;
fn invoice(payment_preimage: PaymentPreimage) -> Invoice {
let payment_hash = Sha256::hash(&payment_preimage.0);
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
InvoiceBuilder::new(Currency::Bitcoin)
.description("test".into())
.payment_hash(payment_hash)
.payment_secret(PaymentSecret([0; 32]))
.current_timestamp()
.duration_since_epoch(duration_since_epoch())
.min_final_cltv_expiry(144)
.amount_milli_satoshis(128)
.build_signed(|hash| {
@ -543,14 +560,24 @@ mod tests {
.unwrap()
}
fn duration_since_epoch() -> Duration {
#[cfg(feature = "std")]
let duration_since_epoch =
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
#[cfg(not(feature = "std"))]
let duration_since_epoch = Duration::from_secs(1234567);
duration_since_epoch
}
fn zero_value_invoice(payment_preimage: PaymentPreimage) -> Invoice {
let payment_hash = Sha256::hash(&payment_preimage.0);
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
InvoiceBuilder::new(Currency::Bitcoin)
.description("test".into())
.payment_hash(payment_hash)
.payment_secret(PaymentSecret([0; 32]))
.current_timestamp()
.duration_since_epoch(duration_since_epoch())
.min_final_cltv_expiry(144)
.build_signed(|hash| {
Secp256k1::new().sign_recoverable(hash, &private_key)
@ -558,17 +585,18 @@ mod tests {
.unwrap()
}
#[cfg(feature = "std")]
fn expired_invoice(payment_preimage: PaymentPreimage) -> Invoice {
let payment_hash = Sha256::hash(&payment_preimage.0);
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
let timestamp = SystemTime::now()
let duration = duration_since_epoch()
.checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME * 2))
.unwrap();
InvoiceBuilder::new(Currency::Bitcoin)
.description("test".into())
.payment_hash(payment_hash)
.payment_secret(PaymentSecret([0; 32]))
.timestamp(timestamp)
.duration_since_epoch(duration)
.min_final_cltv_expiry(144)
.amount_milli_satoshis(128)
.build_signed(|hash| {
@ -811,6 +839,8 @@ mod tests {
assert_eq!(*payer.attempts.borrow(), 1);
}
// Expiration is checked only in an std environment
#[cfg(feature = "std")]
#[test]
fn fails_paying_invoice_after_expiration() {
let event_handled = core::cell::RefCell::new(false);
@ -830,6 +860,8 @@ mod tests {
} else { panic!("Expected Invoice Error"); }
}
// Expiration is checked only in an std environment
#[cfg(feature = "std")]
#[test]
fn fails_retrying_invoice_after_expiration() {
let event_handled = core::cell::RefCell::new(false);
@ -1524,8 +1556,9 @@ mod tests {
let scorer = RefCell::new(TestScorer::new());
let invoice_payer = InvoicePayer::new(nodes[0].node, router, &scorer, nodes[0].logger, event_handler, RetryAttempts(1));
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string()).unwrap())
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
duration_since_epoch()).unwrap())
.is_ok());
let htlc_msgs = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(htlc_msgs.len(), 2);
@ -1569,8 +1602,9 @@ mod tests {
let scorer = RefCell::new(TestScorer::new());
let invoice_payer = InvoicePayer::new(nodes[0].node, router, &scorer, nodes[0].logger, event_handler, RetryAttempts(1));
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string()).unwrap())
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
duration_since_epoch()).unwrap())
.is_ok());
let htlc_msgs = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(htlc_msgs.len(), 2);
@ -1650,8 +1684,9 @@ mod tests {
let scorer = RefCell::new(TestScorer::new());
let invoice_payer = InvoicePayer::new(nodes[0].node, router, &scorer, nodes[0].logger, event_handler, RetryAttempts(1));
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string()).unwrap())
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
duration_since_epoch()).unwrap())
.is_ok());
let htlc_updates = SendEvent::from_node(&nodes[0]);
check_added_monitors!(nodes[0], 1);

View file

@ -1,6 +1,7 @@
use std::fmt;
use std::fmt::{Display, Formatter};
use core::fmt;
use core::fmt::{Display, Formatter};
use bech32::{ToBase32, u5, WriteBase32, Base32Len};
use crate::prelude::*;
use super::{Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiry, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp,
PrivateRoute, Description, RawTaggedField, Currency, RawHrp, SiPrefix, constants, SignedRawInvoice, RawDataPart};
@ -64,7 +65,7 @@ impl<'a, W: WriteBase32> BytesToBase32<'a, W> {
pub fn finalize(mut self) -> Result<(), W::Err> {
self.inner_finalize()?;
std::mem::forget(self);
core::mem::forget(self);
Ok(())
}

View file

@ -0,0 +1,37 @@
use core::cell::{RefCell, RefMut};
use core::ops::{Deref, DerefMut};
pub type LockResult<Guard> = Result<Guard, ()>;
pub struct Mutex<T: ?Sized> {
inner: RefCell<T>
}
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: RefMut<'a, T>,
}
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.lock.deref()
}
}
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
self.lock.deref_mut()
}
}
impl<T> Mutex<T> {
pub fn new(inner: T) -> Mutex<T> {
Mutex { inner: RefCell::new(inner) }
}
pub fn lock<'a>(&'a self) -> LockResult<MutexGuard<'a, T>> {
Ok(MutexGuard { lock: self.inner.borrow_mut() })
}
}

View file

@ -5,6 +5,7 @@ use payment::{Payer, Router};
use bech32::ToBase32;
use bitcoin_hashes::Hash;
use crate::prelude::*;
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use lightning::chain::keysinterface::{Sign, KeysInterface};
@ -16,9 +17,11 @@ use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route};
use lightning::util::logger::Logger;
use secp256k1::key::PublicKey;
use std::convert::TryInto;
use std::ops::Deref;
use core::convert::TryInto;
use core::ops::Deref;
use core::time::Duration;
#[cfg(feature = "std")]
/// Utility to construct an invoice. Generally, unless you want to do something like a custom
/// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
/// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
@ -28,6 +31,33 @@ pub fn create_invoice_from_channelmanager<Signer: Sign, M: Deref, T: Deref, K: D
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
amt_msat: Option<u64>, description: String
) -> Result<Invoice, SignOrCreationError<()>>
where
M::Target: chain::Watch<Signer>,
T::Target: BroadcasterInterface,
K::Target: KeysInterface<Signer = Signer>,
F::Target: FeeEstimator,
L::Target: Logger,
{
use std::time::SystemTime;
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("for the foreseeable future this shouldn't happen");
create_invoice_from_channelmanager_and_duration_since_epoch(
channelmanager,
keys_manager,
network,
amt_msat,
description,
duration
)
}
/// See [`create_invoice_from_channelmanager`]
/// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
/// available and the current time is supplied by the caller.
pub fn create_invoice_from_channelmanager_and_duration_since_epoch<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
) -> Result<Invoice, SignOrCreationError<()>>
where
M::Target: chain::Watch<Signer>,
T::Target: BroadcasterInterface,
@ -68,7 +98,7 @@ where
let our_node_pubkey = channelmanager.get_our_node_id();
let mut invoice = InvoiceBuilder::new(network)
.description(description)
.current_timestamp()
.duration_since_epoch(duration_since_epoch)
.payee_pub_key(our_node_pubkey)
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
.payment_secret(payment_secret)
@ -161,6 +191,7 @@ where
#[cfg(test)]
mod test {
use core::time::Duration;
use {Currency, Description, InvoiceDescription};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY;
@ -170,6 +201,8 @@ mod test {
use lightning::routing::router::{Payee, RouteParameters, find_route};
use lightning::util::events::MessageSendEventsProvider;
use lightning::util::test_utils;
use utils::create_invoice_from_channelmanager_and_duration_since_epoch;
#[test]
fn test_from_channelmanager() {
let chanmon_cfgs = create_chanmon_cfgs(2);
@ -177,7 +210,9 @@ mod test {
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let _chan = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
let invoice = ::utils::create_invoice_from_channelmanager(&nodes[1].node, nodes[1].keys_manager, Currency::BitcoinTestnet, Some(10_000), "test".to_string()).unwrap();
let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
&nodes[1].node, nodes[1].keys_manager, Currency::BitcoinTestnet, Some(10_000), "test".to_string(),
Duration::from_secs(1234567)).unwrap();
assert_eq!(invoice.amount_pico_btc(), Some(100_000));
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));

View file

@ -15,7 +15,7 @@ use lightning_invoice::*;
use secp256k1::PublicKey;
use secp256k1::recovery::{RecoverableSignature, RecoveryId};
use std::collections::HashSet;
use std::time::{Duration, UNIX_EPOCH};
use std::time::Duration;
use std::str::FromStr;
fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
@ -23,7 +23,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
(
"lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp9lfyql".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
@ -44,7 +44,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgquk0rl77nj30yxdy8j9vdx85fkpmdla2087ne0xh8nhedh8w27kyke0lp53ut353s06fv3qfegext0eh0ymjpf39tuven09sam30g4vgpfna3rh".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(250_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
@ -66,7 +66,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpu9qrsgqhtjpauu9ur7fw2thcl4y9vfvh4m9wlfyz2gem29g5ghe2aak2pm3ps8fdhtceqsaagty2vph7utlgj48u0ged6a337aewvraedendscp573dxr".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(250_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
@ -88,7 +88,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp7ynn44".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
@ -109,7 +109,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lntb20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un989qrsgqdj545axuxtnfemtpwkc45hx9d2ft7x04mt8q7y6t0k2dge9e7h8kpy9p34ytyslj3yu569aalz2xdk8xkd7ltxqld94u8h2esmsmacgpghe9k8".to_owned(),
InvoiceBuilder::new(Currency::BitcoinTestnet)
.amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
@ -131,7 +131,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq9qrsgqdfjcdk6w3ak5pca9hwfwfh63zrrz06wwfya0ydlzpgzxkn5xagsqz7x9j4jwe7yj7vaf2k9lqsdk45kts2fd0fkr28am0u4w95tt2nsq76cqw0".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
@ -170,7 +170,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z99qrsgqz6qsgww34xlatfj6e3sngrwfy3ytkt29d2qttr8qz2mnedfqysuqypgqex4haa2h8fx3wnypranf3pdwyluftwe680jjcfp438u82xqphf75ym".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
@ -192,7 +192,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7k9qrsgqt29a0wturnys2hhxpner2e3plp6jyj8qx7548zr2z7ptgjjc7hljm98xhjym0dg52sdrvqamxdezkmqg4gdrvwwnf0kv2jdfnl4xatsqmrnsse".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
@ -216,7 +216,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q9qrsgq9vlvyj8cqvq6ggvpwd53jncp9nwc47xlrsnenq2zp70fq83qlgesn4u3uyf4tesfkkwwfg3qs54qe426hp3tz7z6sweqdjg05axsrjqp9yrrwc".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
@ -240,7 +240,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q9qrsgqrvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqpu2v9wz".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(967878534)
.timestamp(UNIX_EPOCH + Duration::from_secs(1572468703))
.duration_since_epoch(Duration::from_secs(1572468703))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"462264ede7e14047e9b249da94fefc47f41f7d02ee9b091815a5506bc8abf75f"
@ -272,7 +272,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2a25dxl5hrntdtn6zvydt7d66hyzsyhqs4wdynavys42xgl6sgx9c4g7me86a27t07mdtfry458rtjr0v92cnmswpsjscgt2vcse3sgpz3uapa".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_500_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
@ -293,7 +293,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"LNBC25M1PVJLUEZPP5QQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQYPQDQ5VDHKVEN9V5SXYETPDEESSP5ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYGS9Q5SQQQQQQQQQQQQQQQQSGQ2A25DXL5HRNTDTN6ZVYDT7D66HYZSYHQS4WDYNAVYS42XGL6SGX9C4G7ME86A27T07MDTFRY458RTJR0V92CNMSWPSJSCGT2VCSE3SGPZ3UAPA".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_500_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
@ -314,7 +314,7 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
"lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz599y53s3ujmcfjp5xrdap68qxymkqphwsexhmhr8wdz5usdzkzrse33chw6dlp3jhuhge9ley7j2ayx36kawe7kmgg8sv5ugdyusdcqzn8z9x".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.amount_milli_satoshis(2_500_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.duration_since_epoch(Duration::from_secs(1496314658))
.payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"

View file

@ -22,7 +22,7 @@ use ln::msgs;
use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler};
use util::enforcing_trait_impls::EnforcingSigner;
use util::test_utils;
use util::test_utils::TestChainMonitor;
use util::test_utils::{panicking, TestChainMonitor};
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
use util::errors::APIError;
use util::config::UserConfig;
@ -40,7 +40,7 @@ use bitcoin::secp256k1::key::PublicKey;
use io;
use prelude::*;
use core::cell::RefCell;
use std::rc::Rc;
use alloc::rc::Rc;
use sync::{Arc, Mutex};
use core::mem;
@ -231,7 +231,7 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
fn drop(&mut self) {
if !::std::thread::panicking() {
if !panicking() {
// Check that we processed all pending events
assert!(self.node.get_and_clear_pending_msg_events().is_empty());
assert!(self.node.get_and_clear_pending_events().is_empty());

View file

@ -462,6 +462,7 @@ impl Logger for TestLogger {
fn log(&self, record: &Record) {
*self.lines.lock().unwrap().entry((record.module_path.to_string(), format!("{}", record.args))).or_insert(0) += 1;
if record.level >= self.level {
#[cfg(feature = "std")]
println!("{:<5} {} [{} : {}, {}] {}", record.level.to_string(), self.id, record.module_path, record.file, record.line, record.args);
}
}
@ -571,9 +572,17 @@ impl TestKeysInterface {
}
}
pub(crate) fn panicking() -> bool {
#[cfg(feature = "std")]
let panicking = ::std::thread::panicking();
#[cfg(not(feature = "std"))]
let panicking = false;
return panicking;
}
impl Drop for TestKeysInterface {
fn drop(&mut self) {
if std::thread::panicking() {
if panicking() {
return;
}
@ -665,7 +674,7 @@ impl chain::Filter for TestChainSource {
impl Drop for TestChainSource {
fn drop(&mut self) {
if std::thread::panicking() {
if panicking() {
return;
}