Merge pull request #3234 from TheBlueMatt/2024-08-lightning-dep-inv

Swap the dep order between `lightning` and `lightning-invoice`
This commit is contained in:
Matt Corallo 2024-08-14 14:27:39 +00:00 committed by GitHub
commit 398314b720
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 1874 additions and 1603 deletions

View file

@ -3,6 +3,7 @@ resolver = "2"
members = [ members = [
"lightning", "lightning",
"lightning-types",
"lightning-block-sync", "lightning-block-sync",
"lightning-invoice", "lightning-invoice",
"lightning-net-tokio", "lightning-net-tokio",

View file

@ -56,7 +56,7 @@ use lightning::ln::msgs::{
self, ChannelMessageHandler, CommitmentUpdate, DecodeError, Init, UpdateAddHTLC, self, ChannelMessageHandler, CommitmentUpdate, DecodeError, Init, UpdateAddHTLC,
}; };
use lightning::ln::script::ShutdownScript; use lightning::ln::script::ShutdownScript;
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::ln::types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice}; use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice_request::UnsignedInvoiceRequest; use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath}; use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
@ -71,6 +71,8 @@ use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
use lightning::util::test_channel_signer::{EnforcementState, TestChannelSigner}; use lightning::util::test_channel_signer::{EnforcementState, TestChannelSigner};
use lightning_invoice::RawBolt11Invoice;
use crate::utils::test_logger::{self, Output}; use crate::utils::test_logger::{self, Output};
use crate::utils::test_persister::TestPersister; use crate::utils::test_persister::TestPersister;
@ -79,7 +81,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::schnorr; use bitcoin::secp256k1::schnorr;
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey}; use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};
use bech32::u5;
use std::cmp::{self, Ordering}; use std::cmp::{self, Ordering};
use std::io::Cursor; use std::io::Cursor;
use std::mem; use std::mem;
@ -332,7 +333,7 @@ impl NodeSigner for KeyProvider {
} }
fn sign_invoice( fn sign_invoice(
&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient, &self, _invoice: &RawBolt11Invoice, _recipient: Recipient,
) -> Result<RecoverableSignature, ()> { ) -> Result<RecoverableSignature, ()> {
unreachable!() unreachable!()
} }

View file

@ -49,7 +49,7 @@ use lightning::ln::peer_handler::{
IgnoringMessageHandler, MessageHandler, PeerManager, SocketDescriptor, IgnoringMessageHandler, MessageHandler, PeerManager, SocketDescriptor,
}; };
use lightning::ln::script::ShutdownScript; use lightning::ln::script::ShutdownScript;
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::ln::types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice}; use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice_request::UnsignedInvoiceRequest; use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath}; use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
@ -68,6 +68,8 @@ use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, ReadableArgs, Writeable}; use lightning::util::ser::{Readable, ReadableArgs, Writeable};
use lightning::util::test_channel_signer::{EnforcementState, TestChannelSigner}; use lightning::util::test_channel_signer::{EnforcementState, TestChannelSigner};
use lightning_invoice::RawBolt11Invoice;
use crate::utils::test_logger; use crate::utils::test_logger;
use crate::utils::test_persister::TestPersister; use crate::utils::test_persister::TestPersister;
@ -76,7 +78,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::schnorr; use bitcoin::secp256k1::schnorr;
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey}; use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};
use bech32::u5;
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp; use std::cmp;
use std::convert::TryInto; use std::convert::TryInto;
@ -406,7 +407,7 @@ impl NodeSigner for KeyProvider {
} }
fn sign_invoice( fn sign_invoice(
&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient, &self, _invoice: &RawBolt11Invoice, _recipient: Recipient,
) -> Result<RecoverableSignature, ()> { ) -> Result<RecoverableSignature, ()> {
unreachable!() unreachable!()
} }

View file

@ -1,5 +1,4 @@
// Imports that need to be added manually // Imports that need to be added manually
use bech32::u5;
use bitcoin::script::ScriptBuf; use bitcoin::script::ScriptBuf;
use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature; use bitcoin::secp256k1::ecdsa::RecoverableSignature;
@ -27,6 +26,8 @@ use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, Writeable, Writer}; use lightning::util::ser::{Readable, Writeable, Writer};
use lightning::util::test_channel_signer::TestChannelSigner; use lightning::util::test_channel_signer::TestChannelSigner;
use lightning_invoice::RawBolt11Invoice;
use crate::utils::test_logger; use crate::utils::test_logger;
use std::io::{self, Cursor}; use std::io::{self, Cursor};
@ -225,7 +226,7 @@ impl NodeSigner for KeyProvider {
} }
fn sign_invoice( fn sign_invoice(
&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient, &self, _invoice: &RawBolt11Invoice, _recipient: Recipient,
) -> Result<RecoverableSignature, ()> { ) -> Result<RecoverableSignature, ()> {
unreachable!() unreachable!()
} }

View file

@ -18,7 +18,7 @@ use lightning::ln::channel_state::{ChannelCounterparty, ChannelDetails, ChannelS
use lightning::ln::channelmanager; use lightning::ln::channelmanager;
use lightning::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures}; use lightning::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
use lightning::ln::msgs; use lightning::ln::msgs;
use lightning::ln::ChannelId; use lightning::ln::types::ChannelId;
use lightning::offers::invoice::BlindedPayInfo; use lightning::offers::invoice::BlindedPayInfo;
use lightning::routing::gossip::{NetworkGraph, RoutingFees}; use lightning::routing::gossip::{NetworkGraph, RoutingFees};
use lightning::routing::router::{ use lightning::routing::router::{

View file

@ -16,18 +16,17 @@ rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
default = ["std"] default = ["std"]
no-std = ["lightning/no-std"] no-std = ["bitcoin/no-std"]
std = ["bitcoin/std", "lightning/std", "bech32/std"] std = ["bitcoin/std", "bech32/std"]
[dependencies] [dependencies]
bech32 = { version = "0.9.1", default-features = false } bech32 = { version = "0.9.1", default-features = false }
lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false } lightning-types = { version = "0.1", path = "../lightning-types", default-features = false }
secp256k1 = { version = "0.28.0", default-features = false, features = ["recovery", "alloc"] } secp256k1 = { version = "0.28.0", default-features = false, features = ["recovery", "alloc"] }
serde = { version = "1.0.118", optional = true } serde = { version = "1.0.118", optional = true }
bitcoin = { version = "0.31.2", default-features = false } bitcoin = { version = "0.31.2", default-features = false }
[dev-dependencies] [dev-dependencies]
lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false, features = ["_test_utils"] }
serde_json = { version = "1"} serde_json = { version = "1"}
hashbrown = { version = "0.13", default-features = false } hashbrown = { version = "0.13", default-features = false }

View file

@ -14,9 +14,8 @@ use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256; use bitcoin::hashes::sha256;
use crate::prelude::*; use crate::prelude::*;
use lightning::ln::types::PaymentSecret; use lightning_types::payment::PaymentSecret;
use lightning::routing::gossip::RoutingFees; use lightning_types::routing::{RoutingFees, RouteHint, RouteHintHop};
use lightning::routing::router::{RouteHint, RouteHintHop};
use secp256k1::ecdsa::{RecoveryId, RecoverableSignature}; use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
use secp256k1::PublicKey; use secp256k1::PublicKey;
@ -918,8 +917,7 @@ mod test {
#[test] #[test]
fn test_parse_route() { fn test_parse_route() {
use lightning::routing::gossip::RoutingFees; use lightning_types::routing::{RoutingFees, RouteHint, RouteHintHop};
use lightning::routing::router::{RouteHint, RouteHintHop};
use crate::PrivateRoute; use crate::PrivateRoute;
use bech32::FromBase32; use bech32::FromBase32;
@ -974,7 +972,7 @@ mod test {
#[test] #[test]
fn test_payment_secret_and_features_de_and_ser() { fn test_payment_secret_and_features_de_and_ser() {
use lightning::ln::features::Bolt11InvoiceFeatures; use lightning_types::features::Bolt11InvoiceFeatures;
use secp256k1::ecdsa::{RecoveryId, RecoverableSignature}; use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
use crate::TaggedField::*; use crate::TaggedField::*;
use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart,

View file

@ -25,11 +25,8 @@
#[cfg(not(any(feature = "std", feature = "no-std")))] #[cfg(not(any(feature = "std", feature = "no-std")))]
compile_error!("at least one of the `std` or `no-std` features must be enabled"); compile_error!("at least one of the `std` or `no-std` features must be enabled");
pub mod payment;
pub mod utils;
extern crate bech32; extern crate bech32;
#[macro_use] extern crate lightning; extern crate lightning_types;
extern crate secp256k1; extern crate secp256k1;
extern crate alloc; extern crate alloc;
#[cfg(any(test, feature = "std"))] #[cfg(any(test, feature = "std"))]
@ -40,12 +37,11 @@ extern crate serde;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::SystemTime; use std::time::SystemTime;
use bech32::u5; use bech32::{FromBase32, u5};
use bitcoin::{Address, Network, PubkeyHash, ScriptHash, WitnessProgram, WitnessVersion}; use bitcoin::{Address, Network, PubkeyHash, ScriptHash, WitnessProgram, WitnessVersion};
use bitcoin::address::Payload; use bitcoin::address::Payload;
use bitcoin::hashes::{Hash, sha256}; use bitcoin::hashes::{Hash, sha256};
use lightning::ln::features::Bolt11InvoiceFeatures; use lightning_types::features::Bolt11InvoiceFeatures;
use lightning::util::invoice::construct_invoice_preimage;
use secp256k1::PublicKey; use secp256k1::PublicKey;
use secp256k1::{Message, Secp256k1}; use secp256k1::{Message, Secp256k1};
@ -64,12 +60,10 @@ use core::str;
use serde::{Deserialize, Deserializer,Serialize, Serializer, de::Error}; use serde::{Deserialize, Deserializer,Serialize, Serializer, de::Error};
#[doc(no_inline)] #[doc(no_inline)]
pub use lightning::ln::types::PaymentSecret; pub use lightning_types::payment::PaymentSecret;
#[doc(no_inline)] #[doc(no_inline)]
pub use lightning::routing::router::{RouteHint, RouteHintHop}; pub use lightning_types::routing::{RoutingFees, RouteHint, RouteHintHop};
#[doc(no_inline)] use lightning_types::string::UntrustedString;
pub use lightning::routing::gossip::RoutingFees;
use lightning::util::string::UntrustedString;
mod de; mod de;
mod ser; mod ser;
@ -139,11 +133,9 @@ pub const DEFAULT_EXPIRY_TIME: u64 = 3600;
/// Default minimum final CLTV expiry as defined by [BOLT 11]. /// Default minimum final CLTV expiry as defined by [BOLT 11].
/// ///
/// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry, which is /// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry.
/// provided in [`MIN_FINAL_CLTV_EXPIRY_DELTA`].
/// ///
/// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
/// [`MIN_FINAL_CLTV_EXPIRY_DELTA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18; pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18;
/// Builder for [`Bolt11Invoice`]s. It's the most convenient and advised way to use this library. It /// Builder for [`Bolt11Invoice`]s. It's the most convenient and advised way to use this library. It
@ -151,7 +143,6 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18;
/// ///
/// ``` /// ```
/// extern crate secp256k1; /// extern crate secp256k1;
/// extern crate lightning;
/// extern crate lightning_invoice; /// extern crate lightning_invoice;
/// extern crate bitcoin; /// extern crate bitcoin;
/// ///
@ -161,7 +152,7 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18;
/// use secp256k1::Secp256k1; /// use secp256k1::Secp256k1;
/// use secp256k1::SecretKey; /// use secp256k1::SecretKey;
/// ///
/// use lightning::ln::types::PaymentSecret; /// use lightning_types::payment::PaymentSecret;
/// ///
/// use lightning_invoice::{Currency, InvoiceBuilder}; /// use lightning_invoice::{Currency, InvoiceBuilder};
/// ///
@ -970,7 +961,23 @@ macro_rules! find_all_extract {
impl RawBolt11Invoice { impl RawBolt11Invoice {
/// Hash the HRP as bytes and signatureless data part. /// Hash the HRP as bytes and signatureless data part.
fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] { fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
let preimage = construct_invoice_preimage(hrp_bytes, data_without_signature); let mut preimage = Vec::<u8>::from(hrp_bytes);
let mut data_part = Vec::from(data_without_signature);
let overhang = (data_part.len() * 5) % 8;
if overhang > 0 {
// add padding if data does not end at a byte boundary
data_part.push(u5::try_from_u8(0).unwrap());
// if overhang is in (1..3) we need to add u5(0) padding two times
if overhang < 3 {
data_part.push(u5::try_from_u8(0).unwrap());
}
}
preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
.expect("No padding error may occur due to appended zero above."));
let mut hash: [u8; 32] = Default::default(); let mut hash: [u8; 32] = Default::default();
hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]); hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]);
hash hash
@ -1636,15 +1643,12 @@ pub enum CreationError {
/// The supplied millisatoshi amount was greater than the total bitcoin supply. /// The supplied millisatoshi amount was greater than the total bitcoin supply.
InvalidAmount, InvalidAmount,
/// Route hints were required for this invoice and were missing. Applies to // TODO: These two errors are really errors with things in the `lightning` crate and thus
/// [phantom invoices]. // shouldn't live here.
/// /// Route hints were required for this invoice and were missing.
/// [phantom invoices]: crate::utils::create_phantom_invoice
MissingRouteHints, MissingRouteHints,
/// The provided `min_final_cltv_expiry_delta` was less than [`MIN_FINAL_CLTV_EXPIRY_DELTA`]. /// The provided `min_final_cltv_expiry_delta` was less than rust-lightning's minimum.
///
/// [`MIN_FINAL_CLTV_EXPIRY_DELTA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
MinFinalCltvExpiryDeltaTooShort, MinFinalCltvExpiryDeltaTooShort,
} }
@ -1877,14 +1881,14 @@ mod test {
#[test] #[test]
fn test_check_feature_bits() { fn test_check_feature_bits() {
use crate::TaggedField::*; use crate::TaggedField::*;
use lightning::ln::features::Bolt11InvoiceFeatures; use lightning_types::features::Bolt11InvoiceFeatures;
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use secp256k1::SecretKey; use secp256k1::SecretKey;
use crate::{Bolt11Invoice, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp, use crate::{Bolt11Invoice, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp,
Bolt11SemanticError}; Bolt11SemanticError};
let private_key = SecretKey::from_slice(&[42; 32]).unwrap(); let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
let payment_secret = lightning::ln::types::PaymentSecret([21; 32]); let payment_secret = lightning_types::payment::PaymentSecret([21; 32]);
let invoice_template = RawBolt11Invoice { let invoice_template = RawBolt11Invoice {
hrp: RawHrp { hrp: RawHrp {
currency: Currency::Bitcoin, currency: Currency::Bitcoin,
@ -1998,7 +2002,7 @@ mod test {
#[test] #[test]
fn test_builder_fail() { fn test_builder_fail() {
use crate::*; use crate::*;
use lightning::routing::router::RouteHintHop; use lightning_types::routing::RouteHintHop;
use std::iter::FromIterator; use std::iter::FromIterator;
use secp256k1::PublicKey; use secp256k1::PublicKey;
@ -2052,7 +2056,7 @@ mod test {
#[test] #[test]
fn test_builder_ok() { fn test_builder_ok() {
use crate::*; use crate::*;
use lightning::routing::router::RouteHintHop; use lightning_types::routing::RouteHintHop;
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use secp256k1::{SecretKey, PublicKey}; use secp256k1::{SecretKey, PublicKey};
use std::time::Duration; use std::time::Duration;

View file

@ -1,5 +1,4 @@
extern crate bech32; extern crate bech32;
extern crate lightning;
extern crate lightning_invoice; extern crate lightning_invoice;
extern crate secp256k1; extern crate secp256k1;

View file

@ -0,0 +1,25 @@
[package]
name = "lightning-types"
version = "0.1.0"
authors = ["Matt Corallo"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/lightningdevkit/rust-lightning/"
description = """
Basic types which are used in the lightning network
"""
edition = "2021"
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
[features]
_test_utils = []
[dependencies]
bitcoin = { version = "0.31", default-features = false }
# TODO: Once we switch to bitcoin 0.32 drop this explicit dep:
hex-conservative = { version = "0.2", default-features = false }
bech32 = { version = "0.9", default-features = false }
[lints]
workspace = true

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
#![crate_name = "lightning_types"]
//! Various types which are used in the lightning network.
//!
//! See the `lightning` crate for usage of these.
#![cfg_attr(not(test), no_std)]
#![deny(missing_docs)]
#![forbid(unsafe_code)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
extern crate alloc;
extern crate core;
pub mod features;
pub mod payment;
pub mod routing;
pub mod string;

View file

@ -0,0 +1,112 @@
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
//! Types which describe payments in lightning.
use alloc::vec::Vec;
use core::borrow::Borrow;
use bitcoin::hashes::{sha256::Hash as Sha256, Hash as _};
// TODO: Once we switch to rust-bitcoin 0.32, import this as bitcoin::hex
use hex_conservative::display::impl_fmt_traits;
/// The payment hash is the hash of the [`PaymentPreimage`] which is the value used to lock funds
/// in HTLCs while they transit the lightning network.
///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub struct PaymentHash(pub [u8; 32]);
impl Borrow<[u8]> for PaymentHash {
fn borrow(&self) -> &[u8] {
&self.0[..]
}
}
impl_fmt_traits! {
impl fmt_traits for PaymentHash {
const LENGTH: usize = 32;
}
}
/// The payment preimage is the "secret key" which is used to claim the funds of an HTLC on-chain
/// or in a lightning channel.
///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub struct PaymentPreimage(pub [u8; 32]);
impl Borrow<[u8]> for PaymentPreimage {
fn borrow(&self) -> &[u8] {
&self.0[..]
}
}
impl_fmt_traits! {
impl fmt_traits for PaymentPreimage {
const LENGTH: usize = 32;
}
}
/// Converts a `PaymentPreimage` into a `PaymentHash` by hashing the preimage with SHA256.
impl From<PaymentPreimage> for PaymentHash {
fn from(value: PaymentPreimage) -> Self {
PaymentHash(Sha256::hash(&value.0).to_byte_array())
}
}
/// The payment secret is used to authenticate the sender of an HTLC to the recipient and tie
/// multi-part HTLCs together into a single payment.
///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub struct PaymentSecret(pub [u8; 32]);
impl Borrow<[u8]> for PaymentSecret {
fn borrow(&self) -> &[u8] {
&self.0[..]
}
}
impl_fmt_traits! {
impl fmt_traits for PaymentSecret {
const LENGTH: usize = 32;
}
}
use bech32::{u5, Base32Len, FromBase32, ToBase32, WriteBase32};
impl FromBase32 for PaymentSecret {
type Err = bech32::Error;
fn from_base32(field_data: &[u5]) -> Result<PaymentSecret, bech32::Error> {
if field_data.len() != 52 {
return Err(bech32::Error::InvalidLength);
} else {
let data_bytes = Vec::<u8>::from_base32(field_data)?;
let mut payment_secret = [0; 32];
payment_secret.copy_from_slice(&data_bytes);
Ok(PaymentSecret(payment_secret))
}
}
}
impl ToBase32 for PaymentSecret {
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
(&self.0[..]).write_base32(writer)
}
}
impl Base32Len for PaymentSecret {
fn base32_len(&self) -> usize {
52
}
}

View file

@ -0,0 +1,50 @@
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
//! Various types which describe routes or information about partial routes within the lightning
//! network.
use alloc::vec::Vec;
use bitcoin::secp256k1::PublicKey;
/// Fees for routing via a given channel or a node
#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash, Ord, PartialOrd)]
pub struct RoutingFees {
/// Flat routing fee in millisatoshis.
pub base_msat: u32,
/// Liquidity-based routing fee in millionths of a routed amount.
/// In other words, 10000 is 1%.
pub proportional_millionths: u32,
}
/// A list of hops along a payment path terminating with a channel to the recipient.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct RouteHint(pub Vec<RouteHintHop>);
/// A channel descriptor for a hop along a payment path.
///
/// While this generally comes from BOLT 11's `r` field, this struct includes more fields than are
/// available in BOLT 11. Thus, encoding and decoding this via `lightning-invoice` is lossy, as
/// fields not supported in BOLT 11 will be stripped.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct RouteHintHop {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The fees which must be paid to use this channel
pub fees: RoutingFees,
/// The difference in CLTV values between this node and the next node.
pub cltv_expiry_delta: u16,
/// The minimum value, in msat, which must be relayed to the next hop.
pub htlc_minimum_msat: Option<u64>,
/// The maximum value in msat available for routing with a single HTLC.
pub htlc_maximum_msat: Option<u64>,
}

View file

@ -9,31 +9,13 @@
//! Utilities for strings. //! Utilities for strings.
use alloc::string::String;
use core::fmt; use core::fmt;
use crate::io::{self, Read};
use crate::ln::msgs;
use crate::util::ser::{Writeable, Writer, Readable};
#[allow(unused_imports)]
use crate::prelude::*;
/// Struct to `Display` fields in a safe way using `PrintableString` /// Struct to `Display` fields in a safe way using `PrintableString`
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct UntrustedString(pub String); pub struct UntrustedString(pub String);
impl Writeable for UntrustedString {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.write(w)
}
}
impl Readable for UntrustedString {
fn read<R: Read>(r: &mut R) -> Result<Self, msgs::DecodeError> {
let s: String = Readable::read(r)?;
Ok(Self(s))
}
}
impl fmt::Display for UntrustedString { impl fmt::Display for UntrustedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
PrintableString(&self.0).fmt(f) PrintableString(&self.0).fmt(f)

View file

@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
# Internal test utilities exposed to other repo crates # Internal test utilities exposed to other repo crates
_test_utils = ["regex", "bitcoin/bitcoinconsensus"] _test_utils = ["regex", "bitcoin/bitcoinconsensus", "lightning-types/_test_utils"]
# Unlog messages superior at targeted level. # Unlog messages superior at targeted level.
max_level_off = [] max_level_off = []
max_level_error = [] max_level_error = []
@ -31,8 +31,8 @@ unsafe_revoked_tx_signing = []
# Override signing to not include randomness when generating signatures for test vectors. # Override signing to not include randomness when generating signatures for test vectors.
_test_vectors = [] _test_vectors = []
no-std = ["hashbrown", "possiblyrandom", "bitcoin/no-std", "core2/alloc", "libm"] no-std = ["hashbrown", "possiblyrandom", "bitcoin/no-std", "lightning-invoice/no-std", "core2/alloc", "libm"]
std = ["bitcoin/std", "bech32/std"] std = ["bitcoin/std", "bech32/std", "lightning-invoice/std"]
# Generates low-r bitcoin signatures, which saves 1 byte in 50% of the cases # Generates low-r bitcoin signatures, which saves 1 byte in 50% of the cases
grind_signatures = [] grind_signatures = []
@ -40,6 +40,9 @@ grind_signatures = []
default = ["std", "grind_signatures"] default = ["std", "grind_signatures"]
[dependencies] [dependencies]
lightning-types = { version = "0.1", path = "../lightning-types", default-features = false }
lightning-invoice = { version = "0.31.0-beta", path = "../lightning-invoice", default-features = false }
bech32 = { version = "0.9.1", default-features = false } bech32 = { version = "0.9.1", default-features = false }
bitcoin = { version = "0.31.2", default-features = false, features = ["secp-recovery"] } bitcoin = { version = "0.31.2", default-features = false, features = ["secp-recovery"] }
@ -53,6 +56,7 @@ libm = { version = "0.2", optional = true, default-features = false }
[dev-dependencies] [dev-dependencies]
regex = "1.5.6" regex = "1.5.6"
lightning-types = { version = "0.1", path = "../lightning-types", features = ["_test_utils"] }
[dev-dependencies.bitcoin] [dev-dependencies.bitcoin]
version = "0.31.2" version = "0.31.2"

View file

@ -93,7 +93,7 @@ pub(crate) fn verify_channel_type_features(channel_type_features: &Option<Channe
supported_feature_set |= additional_permitted_features; supported_feature_set |= additional_permitted_features;
} }
if !features.is_subset(&supported_feature_set) { if features.requires_unknown_bits_from(&supported_feature_set) {
return Err(DecodeError::UnknownRequiredFeature); return Err(DecodeError::UnknownRequiredFeature);
} }
} }

View file

@ -61,6 +61,9 @@ compile_error!("Tests will always fail with cfg=fuzzing");
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;
extern crate lightning_types;
pub extern crate bitcoin; pub extern crate bitcoin;
#[cfg(any(test, feature = "std"))] #[cfg(any(test, feature = "std"))]
extern crate core; extern crate core;

View file

@ -9,12 +9,12 @@
//! Convenient utilities for paying Lightning invoices. //! Convenient utilities for paying Lightning invoices.
use crate::Bolt11Invoice;
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use lightning_invoice::Bolt11Invoice;
use lightning::ln::types::PaymentHash; use crate::ln::channelmanager::RecipientOnionFields;
use lightning::ln::channelmanager::RecipientOnionFields; use crate::ln::types::PaymentHash;
use lightning::routing::router::{PaymentParameters, RouteParameters}; use crate::routing::router::{PaymentParameters, RouteParameters};
/// Builds the necessary parameters to pay or pre-flight probe the given zero-amount /// Builds the necessary parameters to pay or pre-flight probe the given zero-amount
/// [`Bolt11Invoice`] using [`ChannelManager::send_payment`] or /// [`Bolt11Invoice`] using [`ChannelManager::send_payment`] or
@ -26,10 +26,11 @@ use lightning::routing::router::{PaymentParameters, RouteParameters};
/// Will always succeed unless the invoice has an amount specified, in which case /// Will always succeed unless the invoice has an amount specified, in which case
/// [`payment_parameters_from_invoice`] should be used. /// [`payment_parameters_from_invoice`] should be used.
/// ///
/// [`ChannelManager::send_payment`]: lightning::ln::channelmanager::ChannelManager::send_payment /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
/// [`ChannelManager::send_preflight_probes`]: lightning::ln::channelmanager::ChannelManager::send_preflight_probes /// [`ChannelManager::send_preflight_probes`]: crate::ln::channelmanager::ChannelManager::send_preflight_probes
pub fn payment_parameters_from_zero_amount_invoice(invoice: &Bolt11Invoice, amount_msat: u64) pub fn payment_parameters_from_zero_amount_invoice(
-> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { invoice: &Bolt11Invoice, amount_msat: u64,
) -> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> {
if invoice.amount_milli_satoshis().is_some() { if invoice.amount_milli_satoshis().is_some() {
Err(()) Err(())
} else { } else {
@ -46,10 +47,11 @@ pub fn payment_parameters_from_zero_amount_invoice(invoice: &Bolt11Invoice, amou
/// Will always succeed unless the invoice has no amount specified, in which case /// Will always succeed unless the invoice has no amount specified, in which case
/// [`payment_parameters_from_zero_amount_invoice`] should be used. /// [`payment_parameters_from_zero_amount_invoice`] should be used.
/// ///
/// [`ChannelManager::send_payment`]: lightning::ln::channelmanager::ChannelManager::send_payment /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
/// [`ChannelManager::send_preflight_probes`]: lightning::ln::channelmanager::ChannelManager::send_preflight_probes /// [`ChannelManager::send_preflight_probes`]: crate::ln::channelmanager::ChannelManager::send_preflight_probes
pub fn payment_parameters_from_invoice(invoice: &Bolt11Invoice) pub fn payment_parameters_from_invoice(
-> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { invoice: &Bolt11Invoice,
) -> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> {
if let Some(amount_msat) = invoice.amount_milli_satoshis() { if let Some(amount_msat) = invoice.amount_milli_satoshis() {
Ok(params_from_invoice(invoice, amount_msat)) Ok(params_from_invoice(invoice, amount_msat))
} else { } else {
@ -57,18 +59,20 @@ pub fn payment_parameters_from_invoice(invoice: &Bolt11Invoice)
} }
} }
fn params_from_invoice(invoice: &Bolt11Invoice, amount_msat: u64) fn params_from_invoice(
-> (PaymentHash, RecipientOnionFields, RouteParameters) { invoice: &Bolt11Invoice, amount_msat: u64,
) -> (PaymentHash, RecipientOnionFields, RouteParameters) {
let payment_hash = PaymentHash((*invoice.payment_hash()).to_byte_array()); let payment_hash = PaymentHash((*invoice.payment_hash()).to_byte_array());
let mut recipient_onion = RecipientOnionFields::secret_only(*invoice.payment_secret()); let mut recipient_onion = RecipientOnionFields::secret_only(*invoice.payment_secret());
recipient_onion.payment_metadata = invoice.payment_metadata().map(|v| v.clone()); recipient_onion.payment_metadata = invoice.payment_metadata().map(|v| v.clone());
let mut payment_params = PaymentParameters::from_node_id( let mut payment_params = PaymentParameters::from_node_id(
invoice.recover_payee_pub_key(), invoice.recover_payee_pub_key(),
invoice.min_final_cltv_expiry_delta() as u32 invoice.min_final_cltv_expiry_delta() as u32,
) )
.with_route_hints(invoice.route_hints()).unwrap(); .with_route_hints(invoice.route_hints())
.unwrap();
if let Some(expiry) = invoice.expires_at() { if let Some(expiry) = invoice.expires_at() {
payment_params = payment_params.with_expiry_time(expiry.as_secs()); payment_params = payment_params.with_expiry_time(expiry.as_secs());
} }
@ -83,19 +87,18 @@ fn params_from_invoice(invoice: &Bolt11Invoice, amount_msat: u64)
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{InvoiceBuilder, Currency}; use crate::ln::types::PaymentSecret;
use crate::routing::router::Payee;
use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::Hash as Sha256;
use lightning::ln::types::PaymentSecret; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use lightning::routing::router::Payee;
use secp256k1::{SecretKey, PublicKey, Secp256k1};
use core::time::Duration; use core::time::Duration;
use lightning_invoice::{Currency, InvoiceBuilder};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::SystemTime; use std::time::SystemTime;
fn duration_since_epoch() -> Duration { fn duration_since_epoch() -> Duration {
#[cfg(feature = "std")] #[cfg(feature = "std")]
let duration_since_epoch = let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
let duration_since_epoch = Duration::from_secs(1234567); let duration_since_epoch = Duration::from_secs(1234567);
duration_since_epoch duration_since_epoch
@ -115,9 +118,7 @@ mod tests {
.duration_since_epoch(duration_since_epoch()) .duration_since_epoch(duration_since_epoch())
.min_final_cltv_expiry_delta(144) .min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(128) .amount_milli_satoshis(128)
.build_signed(|hash| { .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &private_key))
secp_ctx.sign_ecdsa_recoverable(hash, &private_key)
})
.unwrap(); .unwrap();
assert!(payment_parameters_from_zero_amount_invoice(&invoice, 42).is_err()); assert!(payment_parameters_from_zero_amount_invoice(&invoice, 42).is_err());
@ -147,14 +148,13 @@ mod tests {
.payment_secret(PaymentSecret([0; 32])) .payment_secret(PaymentSecret([0; 32]))
.duration_since_epoch(duration_since_epoch()) .duration_since_epoch(duration_since_epoch())
.min_final_cltv_expiry_delta(144) .min_final_cltv_expiry_delta(144)
.build_signed(|hash| { .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &private_key))
secp_ctx.sign_ecdsa_recoverable(hash, &private_key) .unwrap();
})
.unwrap();
assert!(payment_parameters_from_invoice(&invoice).is_err()); assert!(payment_parameters_from_invoice(&invoice).is_err());
let (hash, onion, params) = payment_parameters_from_zero_amount_invoice(&invoice, 42).unwrap(); let (hash, onion, params) =
payment_parameters_from_zero_amount_invoice(&invoice, 42).unwrap();
assert_eq!(&hash.0[..], &payment_hash[..]); assert_eq!(&hash.0[..], &payment_hash[..]);
assert_eq!(onion.payment_secret, Some(PaymentSecret([0; 32]))); assert_eq!(onion.payment_secret, Some(PaymentSecret([0; 32])));
assert_eq!(params.final_value_msat, 42); assert_eq!(params.final_value_msat, 42);
@ -169,10 +169,10 @@ mod tests {
#[test] #[test]
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn payment_metadata_end_to_end() { fn payment_metadata_end_to_end() {
use lightning::events::Event; use crate::events::Event;
use lightning::ln::channelmanager::{Retry, PaymentId}; use crate::ln::channelmanager::{PaymentId, Retry};
use lightning::ln::msgs::ChannelMessageHandler; use crate::ln::functional_test_utils::*;
use lightning::ln::functional_test_utils::*; use crate::ln::msgs::ChannelMessageHandler;
// Test that a payment metadata read from an invoice passed to `pay_invoice` makes it all // Test that a payment metadata read from an invoice passed to `pay_invoice` makes it all
// the way out through the `PaymentClaimable` event. // the way out through the `PaymentClaimable` event.
let chanmon_cfgs = create_chanmon_cfgs(2); let chanmon_cfgs = create_chanmon_cfgs(2);
@ -186,6 +186,8 @@ mod tests {
let (payment_hash, payment_secret) = let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None).unwrap(); nodes[1].node.create_inbound_payment(None, 7200, None).unwrap();
let secp_ctx = Secp256k1::new();
let node_secret = nodes[1].keys_manager.backing.get_node_secret_key();
let invoice = InvoiceBuilder::new(Currency::Bitcoin) let invoice = InvoiceBuilder::new(Currency::Bitcoin)
.description("test".into()) .description("test".into())
.payment_hash(Sha256::from_slice(&payment_hash.0).unwrap()) .payment_hash(Sha256::from_slice(&payment_hash.0).unwrap())
@ -194,14 +196,14 @@ mod tests {
.min_final_cltv_expiry_delta(144) .min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000) .amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone()) .payment_metadata(payment_metadata.clone())
.build_signed(|hash| { .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &node_secret))
Secp256k1::new().sign_ecdsa_recoverable(hash,
&nodes[1].keys_manager.backing.get_node_secret_key())
})
.unwrap(); .unwrap();
let (hash, onion, params) = payment_parameters_from_invoice(&invoice).unwrap(); let (hash, onion, params) = payment_parameters_from_invoice(&invoice).unwrap();
nodes[0].node.send_payment(hash, onion, PaymentId(hash.0), params, Retry::Attempts(0)).unwrap(); nodes[0]
.node
.send_payment(hash, onion, PaymentId(hash.0), params, Retry::Attempts(0))
.unwrap();
check_added_monitors(&nodes[0], 1); check_added_monitors(&nodes[0], 1);
let send_event = SendEvent::from_node(&nodes[0]); let send_event = SendEvent::from_node(&nodes[0]);
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &send_event.msgs[0]); nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &send_event.msgs[0]);
@ -215,7 +217,7 @@ mod tests {
Event::PaymentClaimable { onion_fields, .. } => { Event::PaymentClaimable { onion_fields, .. } => {
assert_eq!(Some(payment_metadata), onion_fields.unwrap().payment_metadata); assert_eq!(Some(payment_metadata), onion_fields.unwrap().payment_metadata);
}, },
_ => panic!("Unexpected event") _ => panic!("Unexpected event"),
} }
} }
} }

View file

@ -772,10 +772,12 @@ pub(crate) fn legacy_deserialization_prevention_marker_for_channel_type_features
legacy_version_bit_set.set_scid_privacy_required(); legacy_version_bit_set.set_scid_privacy_required();
legacy_version_bit_set.set_zero_conf_required(); legacy_version_bit_set.set_zero_conf_required();
if features.is_subset(&legacy_version_bit_set) { debug_assert!(!legacy_version_bit_set.supports_any_optional_bits());
None debug_assert!(!features.supports_any_optional_bits());
} else { if features.requires_unknown_bits_from(&legacy_version_bit_set) {
Some(()) Some(())
} else {
None
} }
} }

View file

@ -1877,7 +1877,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
} }
let channel_type = get_initial_channel_type(&config, their_features); let channel_type = get_initial_channel_type(&config, their_features);
debug_assert!(channel_type.is_subset(&channelmanager::provided_channel_type_features(&config))); debug_assert!(!channel_type.supports_any_optional_bits());
debug_assert!(!channel_type.requires_unknown_bits_from(&channelmanager::provided_channel_type_features(&config)));
let (commitment_conf_target, anchor_outputs_value_msat) = if channel_type.supports_anchors_zero_fee_htlc_tx() { let (commitment_conf_target, anchor_outputs_value_msat) = if channel_type.supports_anchors_zero_fee_htlc_tx() {
(ConfirmationTarget::AnchorChannelFee, ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000) (ConfirmationTarget::AnchorChannelFee, ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000)
@ -7967,7 +7968,7 @@ pub(super) fn channel_type_from_open_channel(
return Err(ChannelError::close("Channel Type was not understood - we require static remote key".to_owned())); return Err(ChannelError::close("Channel Type was not understood - we require static remote key".to_owned()));
} }
// Make sure we support all of the features behind the channel type. // Make sure we support all of the features behind the channel type.
if !channel_type.is_subset(our_supported_features) { if channel_type.requires_unknown_bits_from(&our_supported_features) {
return Err(ChannelError::close("Channel Type contains unsupported features".to_owned())); return Err(ChannelError::close("Channel Type contains unsupported features".to_owned()));
} }
let announced_channel = if (common_fields.channel_flags & 1) == 1 { true } else { false }; let announced_channel = if (common_fields.channel_flags & 1) == 1 { true } else { false };
@ -9355,7 +9356,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
} }
let chan_features = channel_type.as_ref().unwrap(); let chan_features = channel_type.as_ref().unwrap();
if !chan_features.is_subset(our_supported_features) { if chan_features.supports_any_optional_bits() || chan_features.requires_unknown_bits_from(&our_supported_features) {
// If the channel was written by a new version and negotiated with features we don't // If the channel was written by a new version and negotiated with features we don't
// understand yet, refuse to read it. // understand yet, refuse to read it.
return Err(DecodeError::UnknownRequiredFeature); return Err(DecodeError::UnknownRequiredFeature);

File diff suppressed because it is too large Load diff

View file

@ -1,22 +1,23 @@
//! Convenient utilities to create an invoice. //! Convenient utilities to create an invoice.
use crate::{Bolt11Invoice, CreationError, Currency, InvoiceBuilder, SignOrCreationError}; use lightning_invoice::{Bolt11Invoice, CreationError, Currency, InvoiceBuilder, SignOrCreationError};
use lightning_invoice::{Description, Bolt11InvoiceDescription, Sha256};
use crate::prelude::*;
use crate::{prelude::*, Description, Bolt11InvoiceDescription, Sha256};
use bech32::ToBase32;
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use lightning::chain; use crate::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use lightning::sign::{Recipient, NodeSigner, SignerProvider, EntropySource}; use crate::sign::{Recipient, NodeSigner, SignerProvider, EntropySource};
use lightning::ln::types::{PaymentHash, PaymentSecret}; use crate::ln::types::{PaymentHash, PaymentSecret};
use lightning::ln::channel_state::ChannelDetails; use crate::ln::channel_state::ChannelDetails;
use lightning::ln::channelmanager::{ChannelManager, MIN_FINAL_CLTV_EXPIRY_DELTA}; use crate::ln::channelmanager::{ChannelManager, MIN_FINAL_CLTV_EXPIRY_DELTA};
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA}; use crate::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey}; use crate::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
use lightning::routing::gossip::RoutingFees; use crate::routing::gossip::RoutingFees;
use lightning::routing::router::{RouteHint, RouteHintHop, Router}; use crate::routing::router::{RouteHint, RouteHintHop, Router};
use lightning::util::logger::{Logger, Record}; use crate::util::logger::{Logger, Record};
use secp256k1::PublicKey; use bitcoin::secp256k1::PublicKey;
use alloc::collections::{btree_map, BTreeMap}; use alloc::collections::{btree_map, BTreeMap};
use core::ops::Deref; use core::ops::Deref;
use core::time::Duration; use core::time::Duration;
@ -54,12 +55,12 @@ use core::iter::Iterator;
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this /// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
/// requirement). /// requirement).
/// ///
/// [`PhantomKeysManager`]: lightning::sign::PhantomKeysManager /// [`PhantomKeysManager`]: crate::sign::PhantomKeysManager
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints /// [`ChannelManager::get_phantom_route_hints`]: crate::ln::channelmanager::ChannelManager::get_phantom_route_hints
/// [`ChannelManager::create_inbound_payment`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment /// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
/// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash /// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels /// [`PhantomRouteHints::channels`]: crate::ln::channelmanager::PhantomRouteHints::channels
/// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA /// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
/// ///
/// This can be used in a `no_std` environment, where [`std::time::SystemTime`] is not /// This can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
/// available and the current time is supplied by the caller. /// available and the current time is supplied by the caller.
@ -111,11 +112,11 @@ where
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this /// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
/// requirement). /// requirement).
/// ///
/// [`PhantomKeysManager`]: lightning::sign::PhantomKeysManager /// [`PhantomKeysManager`]: crate::sign::PhantomKeysManager
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints /// [`ChannelManager::get_phantom_route_hints`]: crate::ln::channelmanager::ChannelManager::get_phantom_route_hints
/// [`ChannelManager::create_inbound_payment`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment /// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
/// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash /// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels /// [`PhantomRouteHints::channels`]: crate::ln::channelmanager::PhantomRouteHints::channels
/// ///
/// This can be used in a `no_std` environment, where [`std::time::SystemTime`] is not /// This can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
/// available and the current time is supplied by the caller. /// available and the current time is supplied by the caller.
@ -161,7 +162,7 @@ where
let invoice = match description { let invoice = match description {
Bolt11InvoiceDescription::Direct(description) => { Bolt11InvoiceDescription::Direct(description) => {
InvoiceBuilder::new(network).description(description.0.0.clone()) InvoiceBuilder::new(network).description(description.as_inner().0.clone())
} }
Bolt11InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0), Bolt11InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
}; };
@ -217,10 +218,8 @@ where
Ok(inv) => inv, Ok(inv) => inv,
Err(e) => return Err(SignOrCreationError::CreationError(e)) Err(e) => return Err(SignOrCreationError::CreationError(e))
}; };
let hrp_str = raw_invoice.hrp.to_string(); let signature = node_signer.sign_invoice(&raw_invoice, Recipient::PhantomNode);
let hrp_bytes = hrp_str.as_bytes(); let signed_raw_invoice = raw_invoice.sign(|_| signature);
let data_without_signature = raw_invoice.data.to_base32();
let signed_raw_invoice = raw_invoice.sign(|_| node_signer.sign_invoice(hrp_bytes, &data_without_signature, Recipient::PhantomNode));
match signed_raw_invoice { match signed_raw_invoice {
Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()), Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()),
Err(e) => Err(SignOrCreationError::SignError(e)) Err(e) => Err(SignOrCreationError::SignError(e))
@ -234,7 +233,7 @@ where
/// * Select up to three channels per node. /// * Select up to three channels per node.
/// * Select one hint from each node, up to three hints or until we run out of hints. /// * Select one hint from each node, up to three hints or until we run out of hints.
/// ///
/// [`PhantomKeysManager`]: lightning::sign::PhantomKeysManager /// [`PhantomKeysManager`]: crate::sign::PhantomKeysManager
fn select_phantom_hints<L: Deref>(amt_msat: Option<u64>, phantom_route_hints: Vec<PhantomRouteHints>, fn select_phantom_hints<L: Deref>(amt_msat: Option<u64>, phantom_route_hints: Vec<PhantomRouteHints>,
logger: L) -> impl Iterator<Item = RouteHint> logger: L) -> impl Iterator<Item = RouteHint>
where where
@ -331,7 +330,7 @@ fn rotate_through_iterators<T, I: Iterator<Item = T>>(mut vecs: Vec<I>) -> impl
/// Note that LDK will add a buffer of 3 blocks to the delta to allow for up to a few new block /// Note that LDK will add a buffer of 3 blocks to the delta to allow for up to a few new block
/// confirmations during routing. /// confirmations during routing.
/// ///
/// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA /// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>( pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>(
channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L, channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
network: Currency, amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32, network: Currency, amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32,
@ -372,7 +371,7 @@ where
/// Note that LDK will add a buffer of 3 blocks to the delta to allow for up to a few new block /// Note that LDK will add a buffer of 3 blocks to the delta to allow for up to a few new block
/// confirmations during routing. /// confirmations during routing.
/// ///
/// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA /// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>( pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>(
channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L, channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
network: Currency, amt_msat: Option<u64>, description_hash: Sha256, network: Currency, amt_msat: Option<u64>, description_hash: Sha256,
@ -541,7 +540,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
let invoice = match description { let invoice = match description {
Bolt11InvoiceDescription::Direct(description) => { Bolt11InvoiceDescription::Direct(description) => {
InvoiceBuilder::new(network).description(description.0.0.clone()) InvoiceBuilder::new(network).description(description.as_inner().0.clone())
} }
Bolt11InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0), Bolt11InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
}; };
@ -569,10 +568,8 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
Ok(inv) => inv, Ok(inv) => inv,
Err(e) => return Err(SignOrCreationError::CreationError(e)) Err(e) => return Err(SignOrCreationError::CreationError(e))
}; };
let hrp_str = raw_invoice.hrp.to_string(); let signature = node_signer.sign_invoice(&raw_invoice, Recipient::Node);
let hrp_bytes = hrp_str.as_bytes(); let signed_raw_invoice = raw_invoice.sign(|_| signature);
let data_without_signature = raw_invoice.data.to_base32();
let signed_raw_invoice = raw_invoice.sign(|_| node_signer.sign_invoice(hrp_bytes, &data_without_signature, Recipient::Node));
match signed_raw_invoice { match signed_raw_invoice {
Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()), Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()),
Err(e) => Err(SignOrCreationError::SignError(e)) Err(e) => Err(SignOrCreationError::SignError(e))
@ -819,50 +816,49 @@ impl<'a, 'b, L: Deref> WithChannelDetails<'a, 'b, L> where L::Target: Logger {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*;
use core::time::Duration; use core::time::Duration;
use crate::{Currency, Description, Bolt11InvoiceDescription, SignOrCreationError, CreationError}; use lightning_invoice::{Currency, Description, Bolt11InvoiceDescription, SignOrCreationError, CreationError};
use bitcoin::hashes::{Hash, sha256}; use bitcoin::hashes::{Hash, sha256};
use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::Hash as Sha256;
use lightning::sign::PhantomKeysManager; use crate::sign::PhantomKeysManager;
use lightning::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::events::{MessageSendEvent, MessageSendEventsProvider};
use lightning::ln::types::PaymentHash; use crate::ln::types::PaymentHash;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use lightning::ln::types::PaymentPreimage; use crate::ln::types::PaymentPreimage;
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry}; use crate::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry};
use lightning::ln::functional_test_utils::*; use crate::ln::functional_test_utils::*;
use lightning::ln::msgs::ChannelMessageHandler; use crate::ln::msgs::ChannelMessageHandler;
use lightning::routing::router::{PaymentParameters, RouteParameters}; use crate::routing::router::{PaymentParameters, RouteParameters};
use lightning::util::test_utils; use crate::util::test_utils;
use lightning::util::config::UserConfig; use crate::util::config::UserConfig;
use crate::utils::{create_invoice_from_channelmanager_and_duration_since_epoch, rotate_through_iterators};
use std::collections::HashSet; use std::collections::HashSet;
use lightning::util::string::UntrustedString;
#[test] #[test]
fn test_prefer_current_channel() { fn test_prefer_current_channel() {
// No minimum, prefer larger candidate channel. // No minimum, prefer larger candidate channel.
assert_eq!(crate::utils::prefer_current_channel(None, 100, 200), false); assert_eq!(prefer_current_channel(None, 100, 200), false);
// No minimum, prefer larger current channel. // No minimum, prefer larger current channel.
assert_eq!(crate::utils::prefer_current_channel(None, 200, 100), true); assert_eq!(prefer_current_channel(None, 200, 100), true);
// Minimum set, prefer current channel over minimum + buffer. // Minimum set, prefer current channel over minimum + buffer.
assert_eq!(crate::utils::prefer_current_channel(Some(100), 115, 100), true); assert_eq!(prefer_current_channel(Some(100), 115, 100), true);
// Minimum set, prefer candidate channel over minimum + buffer. // Minimum set, prefer candidate channel over minimum + buffer.
assert_eq!(crate::utils::prefer_current_channel(Some(100), 105, 125), false); assert_eq!(prefer_current_channel(Some(100), 105, 125), false);
// Minimum set, both channels sufficient, prefer smaller current channel. // Minimum set, both channels sufficient, prefer smaller current channel.
assert_eq!(crate::utils::prefer_current_channel(Some(100), 115, 125), true); assert_eq!(prefer_current_channel(Some(100), 115, 125), true);
// Minimum set, both channels sufficient, prefer smaller candidate channel. // Minimum set, both channels sufficient, prefer smaller candidate channel.
assert_eq!(crate::utils::prefer_current_channel(Some(100), 200, 160), false); assert_eq!(prefer_current_channel(Some(100), 200, 160), false);
// Minimum set, neither sufficient, prefer larger current channel. // Minimum set, neither sufficient, prefer larger current channel.
assert_eq!(crate::utils::prefer_current_channel(Some(200), 100, 50), true); assert_eq!(prefer_current_channel(Some(200), 100, 50), true);
// Minimum set, neither sufficient, prefer larger candidate channel. // Minimum set, neither sufficient, prefer larger candidate channel.
assert_eq!(crate::utils::prefer_current_channel(Some(200), 100, 150), false); assert_eq!(prefer_current_channel(Some(200), 100, 150), false);
} }
@ -878,10 +874,10 @@ mod test {
nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet, nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
Some(10_000), "test".to_string(), Duration::from_secs(1234567), Some(10_000), "test".to_string(), Duration::from_secs(1234567),
non_default_invoice_expiry_secs, None).unwrap(); non_default_invoice_expiry_secs, None).unwrap();
assert_eq!(invoice.amount_pico_btc(), Some(100_000)); assert_eq!(invoice.amount_milli_satoshis(), Some(10_000));
// If no `min_final_cltv_expiry_delta` is specified, then it should be `MIN_FINAL_CLTV_EXPIRY_DELTA`. // If no `min_final_cltv_expiry_delta` is specified, then it should be `MIN_FINAL_CLTV_EXPIRY_DELTA`.
assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64); assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description(UntrustedString("test".to_string())))); assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description::new("test".to_string()).unwrap()));
assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into())); assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
// Invoice SCIDs should always use inbound SCID aliases over the real channel ID, if one is // Invoice SCIDs should always use inbound SCID aliases over the real channel ID, if one is
@ -905,20 +901,15 @@ mod test {
nodes[0].node.send_payment(payment_hash, nodes[0].node.send_payment(payment_hash,
RecipientOnionFields::secret_only(*invoice.payment_secret()), RecipientOnionFields::secret_only(*invoice.payment_secret()),
PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap(); check_added_monitors(&nodes[0], 1);
assert_eq!(added_monitors.len(), 1);
added_monitors.clear();
let mut events = nodes[0].node.get_and_clear_pending_msg_events(); let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1); assert_eq!(events.len(), 1);
SendEvent::from_event(events.remove(0)) SendEvent::from_event(events.remove(0))
}; };
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]); nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &payment_event.commitment_msg); nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &payment_event.commitment_msg);
let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap(); check_added_monitors(&nodes[1], 1);
assert_eq!(added_monitors.len(), 1);
added_monitors.clear();
let events = nodes[1].node.get_and_clear_pending_msg_events(); let events = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 2); assert_eq!(events.len(), 2);
} }
@ -930,7 +921,7 @@ mod test {
let nodes = create_network(2, &node_cfgs, &node_chanmgrs); let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let custom_min_final_cltv_expiry_delta = Some(50); let custom_min_final_cltv_expiry_delta = Some(50);
let invoice = crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch( let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet, nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
Some(10_000), "".into(), Duration::from_secs(1234567), 3600, Some(10_000), "".into(), Duration::from_secs(1234567), 3600,
if with_custom_delta { custom_min_final_cltv_expiry_delta } else { None }, if with_custom_delta { custom_min_final_cltv_expiry_delta } else { None },
@ -953,7 +944,7 @@ mod test {
let nodes = create_network(2, &node_cfgs, &node_chanmgrs); let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let custom_min_final_cltv_expiry_delta = Some(21); let custom_min_final_cltv_expiry_delta = Some(21);
let invoice = crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch( let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet, nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
Some(10_000), "".into(), Duration::from_secs(1234567), 3600, Some(10_000), "".into(), Duration::from_secs(1234567), 3600,
custom_min_final_cltv_expiry_delta, custom_min_final_cltv_expiry_delta,
@ -967,14 +958,14 @@ mod test {
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs); let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let description_hash = crate::Sha256(Hash::hash("Testing description_hash".as_bytes())); let description_hash = Sha256(Hash::hash("Testing description_hash".as_bytes()));
let invoice = crate::utils::create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch( let invoice = create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch(
nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet, nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
Some(10_000), description_hash, Duration::from_secs(1234567), 3600, None, Some(10_000), description_hash, Duration::from_secs(1234567), 3600, None,
).unwrap(); ).unwrap();
assert_eq!(invoice.amount_pico_btc(), Some(100_000)); assert_eq!(invoice.amount_milli_satoshis(), Some(10_000));
assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64); assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
assert_eq!(invoice.description(), Bolt11InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Testing description_hash".as_bytes())))); assert_eq!(invoice.description(), Bolt11InvoiceDescription::Hash(&Sha256(Sha256::hash("Testing description_hash".as_bytes()))));
} }
#[test] #[test]
@ -984,14 +975,14 @@ mod test {
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs); let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let payment_hash = PaymentHash([0; 32]); let payment_hash = PaymentHash([0; 32]);
let invoice = crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash( let invoice = create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet, nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
Some(10_000), "test".to_string(), Duration::from_secs(1234567), 3600, Some(10_000), "test".to_string(), Duration::from_secs(1234567), 3600,
payment_hash, None, payment_hash, None,
).unwrap(); ).unwrap();
assert_eq!(invoice.amount_pico_btc(), Some(100_000)); assert_eq!(invoice.amount_milli_satoshis(), Some(10_000));
assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64); assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description(UntrustedString("test".to_string())))); assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description::new("test".to_string()).unwrap()));
assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&payment_hash.0[..]).unwrap()); assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&payment_hash.0[..]).unwrap());
} }
@ -1283,7 +1274,7 @@ mod test {
let hints = invoice.private_routes(); let hints = invoice.private_routes();
for hint in hints { for hint in hints {
let hint_short_chan_id = (hint.0).0[0].short_channel_id; let hint_short_chan_id = hint.0[0].short_channel_id;
assert!(chan_ids_to_match.remove(&hint_short_chan_id)); assert!(chan_ids_to_match.remove(&hint_short_chan_id));
} }
assert!(chan_ids_to_match.is_empty(), "Unmatched short channel ids: {:?}", chan_ids_to_match); assert!(chan_ids_to_match.is_empty(), "Unmatched short channel ids: {:?}", chan_ids_to_match);
@ -1298,7 +1289,7 @@ mod test {
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn do_test_multi_node_receive(user_generated_pmt_hash: bool) { fn do_test_multi_node_receive(user_generated_pmt_hash: bool) {
use lightning::events::{Event, EventsProvider}; use crate::events::{Event, EventsProvider};
use core::cell::RefCell; use core::cell::RefCell;
let mut chanmon_cfgs = create_chanmon_cfgs(3); let mut chanmon_cfgs = create_chanmon_cfgs(3);
@ -1333,7 +1324,7 @@ mod test {
let non_default_invoice_expiry_secs = 4200; let non_default_invoice_expiry_secs = 4200;
let invoice = let invoice =
crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestLogger>( create_phantom_invoice::<&test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestLogger>(
Some(payment_amt), payment_hash, "test".to_string(), non_default_invoice_expiry_secs, Some(payment_amt), payment_hash, "test".to_string(), non_default_invoice_expiry_secs,
route_hints, nodes[1].keys_manager, nodes[1].keys_manager, nodes[1].logger, route_hints, nodes[1].keys_manager, nodes[1].keys_manager, nodes[1].logger,
Currency::BitcoinTestnet, None, Duration::from_secs(genesis_timestamp) Currency::BitcoinTestnet, None, Duration::from_secs(genesis_timestamp)
@ -1346,7 +1337,7 @@ mod test {
}; };
assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64); assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description(UntrustedString("test".to_string())))); assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description::new("test".to_string()).unwrap()));
assert_eq!(invoice.route_hints().len(), 2); assert_eq!(invoice.route_hints().len(), 2);
assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into())); assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
assert!(!invoice.features().unwrap().supports_basic_mpp()); assert!(!invoice.features().unwrap().supports_basic_mpp());
@ -1362,9 +1353,7 @@ mod test {
nodes[0].node.send_payment(payment_hash, nodes[0].node.send_payment(payment_hash,
RecipientOnionFields::secret_only(*invoice.payment_secret()), RecipientOnionFields::secret_only(*invoice.payment_secret()),
PaymentId(payment_hash.0), params, Retry::Attempts(0)).unwrap(); PaymentId(payment_hash.0), params, Retry::Attempts(0)).unwrap();
let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap(); check_added_monitors(&nodes[0], 1);
assert_eq!(added_monitors.len(), 1);
added_monitors.clear();
let mut events = nodes[0].node.get_and_clear_pending_msg_events(); let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1); assert_eq!(events.len(), 1);
@ -1428,7 +1417,7 @@ mod test {
nodes[2].node.get_phantom_route_hints(), nodes[2].node.get_phantom_route_hints(),
]; ];
let invoice = crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface, let invoice = create_phantom_invoice::<&test_utils::TestKeysInterface,
&test_utils::TestKeysInterface, &test_utils::TestLogger>(Some(payment_amt), Some(payment_hash), &test_utils::TestKeysInterface, &test_utils::TestLogger>(Some(payment_amt), Some(payment_hash),
"test".to_string(), 3600, route_hints, nodes[1].keys_manager, nodes[1].keys_manager, "test".to_string(), 3600, route_hints, nodes[1].keys_manager, nodes[1].keys_manager,
nodes[1].logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap(); nodes[1].logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap();
@ -1444,7 +1433,7 @@ mod test {
#[test] #[test]
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn create_phantom_invoice_with_description_hash() { fn test_create_phantom_invoice_with_description_hash() {
let chanmon_cfgs = create_chanmon_cfgs(3); let chanmon_cfgs = create_chanmon_cfgs(3);
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
@ -1456,9 +1445,9 @@ mod test {
nodes[2].node.get_phantom_route_hints(), nodes[2].node.get_phantom_route_hints(),
]; ];
let description_hash = crate::Sha256(Hash::hash("Description hash phantom invoice".as_bytes())); let description_hash = Sha256(Hash::hash("Description hash phantom invoice".as_bytes()));
let non_default_invoice_expiry_secs = 4200; let non_default_invoice_expiry_secs = 4200;
let invoice = crate::utils::create_phantom_invoice_with_description_hash::< let invoice = create_phantom_invoice_with_description_hash::<
&test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestLogger, &test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestLogger,
>( >(
Some(payment_amt), None, non_default_invoice_expiry_secs, description_hash, Some(payment_amt), None, non_default_invoice_expiry_secs, description_hash,
@ -1466,10 +1455,10 @@ mod test {
Currency::BitcoinTestnet, None, Duration::from_secs(1234567), Currency::BitcoinTestnet, None, Duration::from_secs(1234567),
) )
.unwrap(); .unwrap();
assert_eq!(invoice.amount_pico_btc(), Some(200_000)); assert_eq!(invoice.amount_milli_satoshis(), Some(20_000));
assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64); assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into())); assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
assert_eq!(invoice.description(), Bolt11InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Description hash phantom invoice".as_bytes())))); assert_eq!(invoice.description(), Bolt11InvoiceDescription::Hash(&Sha256(Sha256::hash("Description hash phantom invoice".as_bytes()))));
} }
#[test] #[test]
@ -1490,11 +1479,11 @@ mod test {
let non_default_invoice_expiry_secs = 4200; let non_default_invoice_expiry_secs = 4200;
let min_final_cltv_expiry_delta = Some(100); let min_final_cltv_expiry_delta = Some(100);
let duration_since_epoch = Duration::from_secs(1234567); let duration_since_epoch = Duration::from_secs(1234567);
let invoice = crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface, let invoice = create_phantom_invoice::<&test_utils::TestKeysInterface,
&test_utils::TestKeysInterface, &test_utils::TestLogger>(Some(payment_amt), payment_hash, &test_utils::TestKeysInterface, &test_utils::TestLogger>(Some(payment_amt), payment_hash,
"".to_string(), non_default_invoice_expiry_secs, route_hints, nodes[1].keys_manager, nodes[1].keys_manager, "".to_string(), non_default_invoice_expiry_secs, route_hints, nodes[1].keys_manager, nodes[1].keys_manager,
nodes[1].logger, Currency::BitcoinTestnet, min_final_cltv_expiry_delta, duration_since_epoch).unwrap(); nodes[1].logger, Currency::BitcoinTestnet, min_final_cltv_expiry_delta, duration_since_epoch).unwrap();
assert_eq!(invoice.amount_pico_btc(), Some(200_000)); assert_eq!(invoice.amount_milli_satoshis(), Some(20_000));
assert_eq!(invoice.min_final_cltv_expiry_delta(), (min_final_cltv_expiry_delta.unwrap() + 3) as u64); assert_eq!(invoice.min_final_cltv_expiry_delta(), (min_final_cltv_expiry_delta.unwrap() + 3) as u64);
assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into())); assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
} }
@ -1895,7 +1884,7 @@ mod test {
.map(|route_hint| route_hint.phantom_scid) .map(|route_hint| route_hint.phantom_scid)
.collect::<HashSet<u64>>(); .collect::<HashSet<u64>>();
let invoice = crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface, let invoice = create_phantom_invoice::<&test_utils::TestKeysInterface,
&test_utils::TestKeysInterface, &test_utils::TestLogger>(invoice_amt, None, "test".to_string(), &test_utils::TestKeysInterface, &test_utils::TestLogger>(invoice_amt, None, "test".to_string(),
3600, phantom_route_hints, invoice_node.keys_manager, invoice_node.keys_manager, 3600, phantom_route_hints, invoice_node.keys_manager, invoice_node.keys_manager,
invoice_node.logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap(); invoice_node.logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap();
@ -1903,7 +1892,7 @@ mod test {
let invoice_hints = invoice.private_routes(); let invoice_hints = invoice.private_routes();
for hint in invoice_hints { for hint in invoice_hints {
let hints = &(hint.0).0; let hints = &hint.0;
match hints.len() { match hints.len() {
1 => { 1 => {
assert!(nodes_contains_public_channels); assert!(nodes_contains_public_channels);
@ -1928,7 +1917,7 @@ mod test {
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs); let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let result = crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch( let result = create_invoice_from_channelmanager_and_duration_since_epoch(
nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet, nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
Some(10_000), "Some description".into(), Duration::from_secs(1234567), 3600, Some(MIN_FINAL_CLTV_EXPIRY_DELTA - 4), Some(10_000), "Some description".into(), Duration::from_secs(1234567), 3600, Some(MIN_FINAL_CLTV_EXPIRY_DELTA - 4),
); );

View file

@ -25,7 +25,12 @@ pub mod features;
pub mod script; pub mod script;
pub mod types; pub mod types;
pub use types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret}; // TODO: These modules were moved from lightning-invoice and need to be better integrated into this
// crate now:
pub mod invoice_utils;
pub mod bolt11_payment;
pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
#[cfg(fuzzing)] #[cfg(fuzzing)]
pub mod peer_channel_encryptor; pub mod peer_channel_encryptor;

View file

@ -2320,11 +2320,26 @@ impl_writeable_msg!(ChannelReady, {
(1, short_channel_id_alias, option), (1, short_channel_id_alias, option),
}); });
pub(crate) fn write_features_up_to_13<W: Writer>(w: &mut W, le_flags: &[u8]) -> Result<(), io::Error> {
let len = core::cmp::min(2, le_flags.len());
(len as u16).write(w)?;
for i in (0..len).rev() {
if i == 0 {
le_flags[i].write(w)?;
} else {
// On byte 1, we want up-to-and-including-bit-13, 0-indexed, which is
// up-to-and-including-bit-5, 0-indexed, on this byte:
(le_flags[i] & 0b00_11_11_11).write(w)?;
}
}
Ok(())
}
impl Writeable for Init { impl Writeable for Init {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// global_features gets the bottom 13 bits of our features, and local_features gets all of // global_features gets the bottom 13 bits of our features, and local_features gets all of
// our relevant feature bits. This keeps us compatible with old nodes. // our relevant feature bits. This keeps us compatible with old nodes.
self.features.write_up_to_13(w)?; write_features_up_to_13(w, self.features.le_flags())?;
self.features.write(w)?; self.features.write(w)?;
encode_tlv_stream!(w, { encode_tlv_stream!(w, {
(1, self.networks.as_ref().map(|n| WithoutLength(n)), option), (1, self.networks.as_ref().map(|n| WithoutLength(n)), option),

View file

@ -121,75 +121,7 @@ impl fmt::Display for ChannelId {
} }
} }
pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
/// The payment hash is the hash of the [`PaymentPreimage`] which is the value used to lock funds
/// in HTLCs while they transit the lightning network.
///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentHash(pub [u8; 32]);
impl core::fmt::Display for PaymentHash {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
crate::util::logger::DebugBytes(&self.0).fmt(f)
}
}
/// The payment preimage is the "secret key" which is used to claim the funds of an HTLC on-chain
/// or in a lightning channel.
///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentPreimage(pub [u8; 32]);
impl core::fmt::Display for PaymentPreimage {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
crate::util::logger::DebugBytes(&self.0).fmt(f)
}
}
/// Converts a `PaymentPreimage` into a `PaymentHash` by hashing the preimage with SHA256.
impl From<PaymentPreimage> for PaymentHash {
fn from(value: PaymentPreimage) -> Self {
PaymentHash(Sha256::hash(&value.0).to_byte_array())
}
}
/// The payment secret is used to authenticate the sender of an HTLC to the recipient and tie
/// multi-part HTLCs together into a single payment.
///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentSecret(pub [u8; 32]);
use bech32::{Base32Len, FromBase32, ToBase32, WriteBase32, u5};
impl FromBase32 for PaymentSecret {
type Err = bech32::Error;
fn from_base32(field_data: &[u5]) -> Result<PaymentSecret, bech32::Error> {
if field_data.len() != 52 {
return Err(bech32::Error::InvalidLength)
} else {
let data_bytes = Vec::<u8>::from_base32(field_data)?;
let mut payment_secret = [0; 32];
payment_secret.copy_from_slice(&data_bytes);
Ok(PaymentSecret(payment_secret))
}
}
}
impl ToBase32 for PaymentSecret {
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
(&self.0[..]).write_base32(writer)
}
}
impl Base32Len for PaymentSecret {
fn base32_len(&self) -> usize {
52
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -45,6 +45,8 @@ use core::str::FromStr;
use core::sync::atomic::{AtomicUsize, Ordering}; use core::sync::atomic::{AtomicUsize, Ordering};
use core::{cmp, fmt}; use core::{cmp, fmt};
pub use lightning_types::routing::RoutingFees;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@ -1212,16 +1214,6 @@ impl EffectiveCapacity {
} }
} }
/// Fees for routing via a given channel or a node
#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash, Ord, PartialOrd)]
pub struct RoutingFees {
/// Flat routing fee in millisatoshis.
pub base_msat: u32,
/// Liquidity-based routing fee in millionths of a routed amount.
/// In other words, 10000 is 1%.
pub proportional_millionths: u32,
}
impl_writeable_tlv_based!(RoutingFees, { impl_writeable_tlv_based!(RoutingFees, {
(0, base_msat, required), (0, base_msat, required),
(2, proportional_millionths, required) (2, proportional_millionths, required)

View file

@ -22,7 +22,7 @@ use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use crate::ln::onion_utils; use crate::ln::onion_utils;
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice}; use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath}; use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees}; use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId};
use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp}; use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp};
use crate::sign::EntropySource; use crate::sign::EntropySource;
use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer}; use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer};
@ -35,6 +35,10 @@ use alloc::collections::BinaryHeap;
use core::{cmp, fmt}; use core::{cmp, fmt};
use core::ops::Deref; use core::ops::Deref;
use lightning_types::routing::RoutingFees;
pub use lightning_types::routing::{RouteHint, RouteHintHop};
/// A [`Router`] implemented using [`find_route`]. /// A [`Router`] implemented using [`find_route`].
/// ///
/// # Privacy /// # Privacy
@ -1099,10 +1103,6 @@ impl ReadableArgs<bool> for Features {
} }
} }
/// A list of hops along a payment path terminating with a channel to the recipient.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct RouteHint(pub Vec<RouteHintHop>);
impl Writeable for RouteHint { impl Writeable for RouteHint {
fn write<W: crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> { fn write<W: crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
(self.0.len() as u64).write(writer)?; (self.0.len() as u64).write(writer)?;
@ -1124,27 +1124,6 @@ impl Readable for RouteHint {
} }
} }
/// A channel descriptor for a hop along a payment path.
///
/// While this generally comes from BOLT 11's `r` field, this struct includes more fields than are
/// available in BOLT 11. Thus, encoding and decoding this via `lightning-invoice` is lossy, as
/// fields not supported in BOLT 11 will be stripped.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct RouteHintHop {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The fees which must be paid to use this channel
pub fees: RoutingFees,
/// The difference in CLTV values between this node and the next node.
pub cltv_expiry_delta: u16,
/// The minimum value, in msat, which must be relayed to the next hop.
pub htlc_minimum_msat: Option<u64>,
/// The maximum value in msat available for routing with a single HTLC.
pub htlc_maximum_msat: Option<u64>,
}
impl_writeable_tlv_based!(RouteHintHop, { impl_writeable_tlv_based!(RouteHintHop, {
(0, src_node_id, required), (0, src_node_id, required),
(1, htlc_minimum_msat, option), (1, htlc_minimum_msat, option),

View file

@ -24,7 +24,6 @@ use bitcoin::sighash::EcdsaSighashType;
use bitcoin::transaction::Version; use bitcoin::transaction::Version;
use bitcoin::transaction::{Transaction, TxIn, TxOut}; use bitcoin::transaction::{Transaction, TxIn, TxOut};
use bech32::u5;
use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::{Hash, HashEngine};
@ -37,6 +36,8 @@ use bitcoin::secp256k1::All;
use bitcoin::secp256k1::{Keypair, PublicKey, Scalar, Secp256k1, SecretKey, Signing}; use bitcoin::secp256k1::{Keypair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
use bitcoin::{secp256k1, Psbt, Sequence, Txid, WPubkeyHash, Witness}; use bitcoin::{secp256k1, Psbt, Sequence, Txid, WPubkeyHash, Witness};
use lightning_invoice::RawBolt11Invoice;
use crate::chain::transaction::OutPoint; use crate::chain::transaction::OutPoint;
use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
use crate::ln::chan_utils; use crate::ln::chan_utils;
@ -69,7 +70,6 @@ use crate::sign::ecdsa::EcdsaChannelSigner;
#[cfg(taproot)] #[cfg(taproot)]
use crate::sign::taproot::TaprootChannelSigner; use crate::sign::taproot::TaprootChannelSigner;
use crate::util::atomic_counter::AtomicCounter; use crate::util::atomic_counter::AtomicCounter;
use crate::util::invoice::construct_invoice_preimage;
use core::convert::TryInto; use core::convert::TryInto;
use core::ops::Deref; use core::ops::Deref;
use core::sync::atomic::{AtomicUsize, Ordering}; use core::sync::atomic::{AtomicUsize, Ordering};
@ -867,7 +867,7 @@ pub trait NodeSigner {
/// ///
/// Errors if the [`Recipient`] variant is not supported by the implementation. /// Errors if the [`Recipient`] variant is not supported by the implementation.
fn sign_invoice( fn sign_invoice(
&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, &self, invoice: &RawBolt11Invoice, recipient: Recipient,
) -> Result<RecoverableSignature, ()>; ) -> Result<RecoverableSignature, ()>;
/// Signs the [`TaggedHash`] of a BOLT 12 invoice request. /// Signs the [`TaggedHash`] of a BOLT 12 invoice request.
@ -2174,17 +2174,14 @@ impl NodeSigner for KeysManager {
} }
fn sign_invoice( fn sign_invoice(
&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, &self, invoice: &RawBolt11Invoice, recipient: Recipient,
) -> Result<RecoverableSignature, ()> { ) -> Result<RecoverableSignature, ()> {
let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data); let hash = invoice.signable_hash();
let secret = match recipient { let secret = match recipient {
Recipient::Node => Ok(&self.node_secret), Recipient::Node => Ok(&self.node_secret),
Recipient::PhantomNode => Err(()), Recipient::PhantomNode => Err(()),
}?; }?;
Ok(self.secp_ctx.sign_ecdsa_recoverable( Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&hash), secret))
&hash_to_message!(&Sha256::hash(&preimage).to_byte_array()),
secret,
))
} }
fn sign_bolt12_invoice_request( fn sign_bolt12_invoice_request(
@ -2352,17 +2349,14 @@ impl NodeSigner for PhantomKeysManager {
} }
fn sign_invoice( fn sign_invoice(
&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, &self, invoice: &RawBolt11Invoice, recipient: Recipient,
) -> Result<RecoverableSignature, ()> { ) -> Result<RecoverableSignature, ()> {
let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data); let hash = invoice.signable_hash();
let secret = match recipient { let secret = match recipient {
Recipient::Node => &self.inner.node_secret, Recipient::Node => &self.inner.node_secret,
Recipient::PhantomNode => &self.phantom_secret, Recipient::PhantomNode => &self.phantom_secret,
}; };
Ok(self.inner.secp_ctx.sign_ecdsa_recoverable( Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&hash), secret))
&hash_to_message!(&Sha256::hash(&preimage).to_byte_array()),
secret,
))
} }
fn sign_bolt12_invoice_request( fn sign_bolt12_invoice_request(

View file

@ -1,28 +0,0 @@
//! Low level invoice utilities.
use bech32::{u5, FromBase32};
#[allow(unused)]
use crate::prelude::*;
/// Construct the invoice's HRP and signatureless data into a preimage to be hashed.
pub fn construct_invoice_preimage(hrp_bytes: &[u8], data_without_signature: &[u5]) -> Vec<u8> {
let mut preimage = Vec::<u8>::from(hrp_bytes);
let mut data_part = Vec::from(data_without_signature);
let overhang = (data_part.len() * 5) % 8;
if overhang > 0 {
// add padding if data does not end at a byte boundary
data_part.push(u5::try_from_u8(0).unwrap());
// if overhang is in (1..3) we need to add u5(0) padding two times
if overhang < 3 {
data_part.push(u5::try_from_u8(0).unwrap());
}
}
preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
.expect("No padding error may occur due to appended zero above."));
preimage
}

View file

@ -18,10 +18,8 @@ pub mod ser_macros;
pub mod errors; pub mod errors;
pub mod ser; pub mod ser;
pub mod message_signing; pub mod message_signing;
pub mod invoice;
pub mod persist; pub mod persist;
pub mod scid_utils; pub mod scid_utils;
pub mod string;
pub mod sweep; pub mod sweep;
pub mod wakers; pub mod wakers;
#[cfg(fuzzing)] #[cfg(fuzzing)]
@ -54,3 +52,7 @@ pub mod test_utils;
#[cfg(any(test, feature = "_test_utils"))] #[cfg(any(test, feature = "_test_utils"))]
pub mod test_channel_signer; pub mod test_channel_signer;
pub mod string {
//! Utilities to wrap untrusted strings and handle them (more) safely
pub use lightning_types::string::{PrintableString, UntrustedString};
}

View file

@ -627,6 +627,18 @@ impl<'a> From<&'a String> for WithoutLength<&'a String> {
fn from(s: &'a String) -> Self { Self(s) } fn from(s: &'a String) -> Self { Self(s) }
} }
impl Writeable for UntrustedString {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.write(w)
}
}
impl Readable for UntrustedString {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let s: String = Readable::read(r)?;
Ok(Self(s))
}
}
impl Writeable for WithoutLength<&UntrustedString> { impl Writeable for WithoutLength<&UntrustedString> {
#[inline] #[inline]

View file

@ -65,6 +65,8 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::schnorr; use bitcoin::secp256k1::schnorr;
use lightning_invoice::RawBolt11Invoice;
use crate::io; use crate::io;
use crate::prelude::*; use crate::prelude::*;
use core::cell::RefCell; use core::cell::RefCell;
@ -72,7 +74,6 @@ use core::time::Duration;
use crate::sync::{Mutex, Arc}; use crate::sync::{Mutex, Arc};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use core::mem; use core::mem;
use bech32::u5;
use crate::sign::{InMemorySigner, RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider}; use crate::sign::{InMemorySigner, RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider};
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -1217,7 +1218,7 @@ impl NodeSigner for TestNodeSigner {
Ok(SharedSecret::new(other_key, &node_secret)) Ok(SharedSecret::new(other_key, &node_secret))
} }
fn sign_invoice(&self, _: &[u8], _: &[bech32::u5], _: Recipient) -> Result<bitcoin::secp256k1::ecdsa::RecoverableSignature, ()> { fn sign_invoice(&self, _: &RawBolt11Invoice, _: Recipient) -> Result<RecoverableSignature, ()> {
unreachable!() unreachable!()
} }
@ -1270,8 +1271,8 @@ impl NodeSigner for TestKeysInterface {
self.backing.get_inbound_payment_key_material() self.backing.get_inbound_payment_key_material()
} }
fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> { fn sign_invoice(&self, invoice: &RawBolt11Invoice, recipient: Recipient) -> Result<RecoverableSignature, ()> {
self.backing.sign_invoice(hrp_bytes, invoice_data, recipient) self.backing.sign_invoice(invoice, recipient)
} }
fn sign_bolt12_invoice_request( fn sign_bolt12_invoice_request(

View file

@ -41,6 +41,7 @@
./lightning/src/ln/functional_test_utils.rs ./lightning/src/ln/functional_test_utils.rs
./lightning/src/ln/functional_tests.rs ./lightning/src/ln/functional_tests.rs
./lightning/src/ln/inbound_payment.rs ./lightning/src/ln/inbound_payment.rs
./lightning/src/ln/invoice_utils.rs
./lightning/src/ln/max_payment_path_len_tests.rs ./lightning/src/ln/max_payment_path_len_tests.rs
./lightning/src/ln/mod.rs ./lightning/src/ln/mod.rs
./lightning/src/ln/monitor_tests.rs ./lightning/src/ln/monitor_tests.rs