diff --git a/Cargo.toml b/Cargo.toml index 0aa7f7624d8..f7f4ba021fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "lightning", + "lightning-types", "lightning-block-sync", "lightning-invoice", "lightning-net-tokio", diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 7e95dc07f33..4322fccb16f 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -56,7 +56,7 @@ use lightning::ln::msgs::{ self, ChannelMessageHandler, CommitmentUpdate, DecodeError, Init, UpdateAddHTLC, }; 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_request::UnsignedInvoiceRequest; 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::test_channel_signer::{EnforcementState, TestChannelSigner}; +use lightning_invoice::RawBolt11Invoice; + use crate::utils::test_logger::{self, Output}; use crate::utils::test_persister::TestPersister; @@ -79,7 +81,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::schnorr; use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey}; -use bech32::u5; use std::cmp::{self, Ordering}; use std::io::Cursor; use std::mem; @@ -332,7 +333,7 @@ impl NodeSigner for KeyProvider { } fn sign_invoice( - &self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient, + &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 5499dc5e902..fa0c2906294 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -49,7 +49,7 @@ use lightning::ln::peer_handler::{ IgnoringMessageHandler, MessageHandler, PeerManager, SocketDescriptor, }; 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_request::UnsignedInvoiceRequest; 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::test_channel_signer::{EnforcementState, TestChannelSigner}; +use lightning_invoice::RawBolt11Invoice; + use crate::utils::test_logger; use crate::utils::test_persister::TestPersister; @@ -76,7 +78,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::schnorr; use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey}; -use bech32::u5; use std::cell::RefCell; use std::cmp; use std::convert::TryInto; @@ -406,7 +407,7 @@ impl NodeSigner for KeyProvider { } fn sign_invoice( - &self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient, + &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index a05551410f3..48f184af709 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -1,5 +1,4 @@ // Imports that need to be added manually -use bech32::u5; use bitcoin::script::ScriptBuf; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::RecoverableSignature; @@ -27,6 +26,8 @@ use lightning::util::logger::Logger; use lightning::util::ser::{Readable, Writeable, Writer}; use lightning::util::test_channel_signer::TestChannelSigner; +use lightning_invoice::RawBolt11Invoice; + use crate::utils::test_logger; use std::io::{self, Cursor}; @@ -225,7 +226,7 @@ impl NodeSigner for KeyProvider { } fn sign_invoice( - &self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient, + &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index 034672fb275..96046f25842 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -18,7 +18,7 @@ use lightning::ln::channel_state::{ChannelCounterparty, ChannelDetails, ChannelS use lightning::ln::channelmanager; use lightning::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures}; use lightning::ln::msgs; -use lightning::ln::ChannelId; +use lightning::ln::types::ChannelId; use lightning::offers::invoice::BlindedPayInfo; use lightning::routing::gossip::{NetworkGraph, RoutingFees}; use lightning::routing::router::{ diff --git a/lightning-invoice/Cargo.toml b/lightning-invoice/Cargo.toml index 94b3071676a..f73321296d1 100644 --- a/lightning-invoice/Cargo.toml +++ b/lightning-invoice/Cargo.toml @@ -16,18 +16,17 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["std"] -no-std = ["lightning/no-std"] -std = ["bitcoin/std", "lightning/std", "bech32/std"] +no-std = ["bitcoin/no-std"] +std = ["bitcoin/std", "bech32/std"] [dependencies] 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"] } serde = { version = "1.0.118", optional = true } bitcoin = { version = "0.31.2", default-features = false } [dev-dependencies] -lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false, features = ["_test_utils"] } serde_json = { version = "1"} hashbrown = { version = "0.13", default-features = false } diff --git a/lightning-invoice/src/de.rs b/lightning-invoice/src/de.rs index bd9f4a5f6de..be1a21aa25f 100644 --- a/lightning-invoice/src/de.rs +++ b/lightning-invoice/src/de.rs @@ -14,9 +14,8 @@ use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion}; use bitcoin::hashes::Hash; use bitcoin::hashes::sha256; use crate::prelude::*; -use lightning::ln::types::PaymentSecret; -use lightning::routing::gossip::RoutingFees; -use lightning::routing::router::{RouteHint, RouteHintHop}; +use lightning_types::payment::PaymentSecret; +use lightning_types::routing::{RoutingFees, RouteHint, RouteHintHop}; use secp256k1::ecdsa::{RecoveryId, RecoverableSignature}; use secp256k1::PublicKey; @@ -918,8 +917,7 @@ mod test { #[test] fn test_parse_route() { - use lightning::routing::gossip::RoutingFees; - use lightning::routing::router::{RouteHint, RouteHintHop}; + use lightning_types::routing::{RoutingFees, RouteHint, RouteHintHop}; use crate::PrivateRoute; use bech32::FromBase32; @@ -974,7 +972,7 @@ mod test { #[test] 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 crate::TaggedField::*; use crate::{SiPrefix, SignedRawBolt11Invoice, Bolt11InvoiceSignature, RawBolt11Invoice, RawHrp, RawDataPart, diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs index 2df015d1a08..56ca21a9b45 100644 --- a/lightning-invoice/src/lib.rs +++ b/lightning-invoice/src/lib.rs @@ -25,11 +25,8 @@ #[cfg(not(any(feature = "std", feature = "no-std")))] compile_error!("at least one of the `std` or `no-std` features must be enabled"); -pub mod payment; -pub mod utils; - extern crate bech32; -#[macro_use] extern crate lightning; +extern crate lightning_types; extern crate secp256k1; extern crate alloc; #[cfg(any(test, feature = "std"))] @@ -40,12 +37,11 @@ extern crate serde; #[cfg(feature = "std")] use std::time::SystemTime; -use bech32::u5; +use bech32::{FromBase32, u5}; use bitcoin::{Address, Network, PubkeyHash, ScriptHash, WitnessProgram, WitnessVersion}; use bitcoin::address::Payload; use bitcoin::hashes::{Hash, sha256}; -use lightning::ln::features::Bolt11InvoiceFeatures; -use lightning::util::invoice::construct_invoice_preimage; +use lightning_types::features::Bolt11InvoiceFeatures; use secp256k1::PublicKey; use secp256k1::{Message, Secp256k1}; @@ -64,12 +60,10 @@ use core::str; use serde::{Deserialize, Deserializer,Serialize, Serializer, de::Error}; #[doc(no_inline)] -pub use lightning::ln::types::PaymentSecret; -#[doc(no_inline)] -pub use lightning::routing::router::{RouteHint, RouteHintHop}; +pub use lightning_types::payment::PaymentSecret; #[doc(no_inline)] -pub use lightning::routing::gossip::RoutingFees; -use lightning::util::string::UntrustedString; +pub use lightning_types::routing::{RoutingFees, RouteHint, RouteHintHop}; +use lightning_types::string::UntrustedString; mod de; mod ser; @@ -139,11 +133,9 @@ pub const DEFAULT_EXPIRY_TIME: u64 = 3600; /// 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 -/// provided in [`MIN_FINAL_CLTV_EXPIRY_DELTA`]. +/// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry. /// /// [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; /// 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 lightning; /// extern crate lightning_invoice; /// extern crate bitcoin; /// @@ -161,7 +152,7 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18; /// use secp256k1::Secp256k1; /// use secp256k1::SecretKey; /// -/// use lightning::ln::types::PaymentSecret; +/// use lightning_types::payment::PaymentSecret; /// /// use lightning_invoice::{Currency, InvoiceBuilder}; /// @@ -970,7 +961,23 @@ macro_rules! find_all_extract { impl RawBolt11Invoice { /// Hash the HRP as bytes and signatureless data part. 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::::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::::from_base32(&data_part) + .expect("No padding error may occur due to appended zero above.")); + let mut hash: [u8; 32] = Default::default(); hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]); hash @@ -1636,15 +1643,12 @@ pub enum CreationError { /// The supplied millisatoshi amount was greater than the total bitcoin supply. InvalidAmount, - /// Route hints were required for this invoice and were missing. Applies to - /// [phantom invoices]. - /// - /// [phantom invoices]: crate::utils::create_phantom_invoice + // TODO: These two errors are really errors with things in the `lightning` crate and thus + // shouldn't live here. + /// Route hints were required for this invoice and were missing. MissingRouteHints, - /// The provided `min_final_cltv_expiry_delta` was less than [`MIN_FINAL_CLTV_EXPIRY_DELTA`]. - /// - /// [`MIN_FINAL_CLTV_EXPIRY_DELTA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA + /// The provided `min_final_cltv_expiry_delta` was less than rust-lightning's minimum. MinFinalCltvExpiryDeltaTooShort, } @@ -1877,14 +1881,14 @@ mod test { #[test] fn test_check_feature_bits() { use crate::TaggedField::*; - use lightning::ln::features::Bolt11InvoiceFeatures; + use lightning_types::features::Bolt11InvoiceFeatures; use secp256k1::Secp256k1; use secp256k1::SecretKey; use crate::{Bolt11Invoice, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp, Bolt11SemanticError}; 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 { hrp: RawHrp { currency: Currency::Bitcoin, @@ -1998,7 +2002,7 @@ mod test { #[test] fn test_builder_fail() { use crate::*; - use lightning::routing::router::RouteHintHop; + use lightning_types::routing::RouteHintHop; use std::iter::FromIterator; use secp256k1::PublicKey; @@ -2052,7 +2056,7 @@ mod test { #[test] fn test_builder_ok() { use crate::*; - use lightning::routing::router::RouteHintHop; + use lightning_types::routing::RouteHintHop; use secp256k1::Secp256k1; use secp256k1::{SecretKey, PublicKey}; use std::time::Duration; diff --git a/lightning-invoice/tests/ser_de.rs b/lightning-invoice/tests/ser_de.rs index d0058c9a1e3..d9d6ad4c195 100644 --- a/lightning-invoice/tests/ser_de.rs +++ b/lightning-invoice/tests/ser_de.rs @@ -1,5 +1,4 @@ extern crate bech32; -extern crate lightning; extern crate lightning_invoice; extern crate secp256k1; diff --git a/lightning-types/Cargo.toml b/lightning-types/Cargo.toml new file mode 100644 index 00000000000..a063176ae57 --- /dev/null +++ b/lightning-types/Cargo.toml @@ -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 diff --git a/lightning-types/src/features.rs b/lightning-types/src/features.rs new file mode 100644 index 00000000000..3aec5358d2b --- /dev/null +++ b/lightning-types/src/features.rs @@ -0,0 +1,1359 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! Feature flag definitions for the Lightning protocol according to [BOLT #9]. +//! +//! Lightning nodes advertise a supported set of operation through feature flags. Features are +//! applicable for a specific context. [`Features`] encapsulates behavior for specifying and +//! checking feature flags for a particular context. Each feature is defined internally by a trait +//! specifying the corresponding flags (i.e., even and odd bits). +//! +//! Whether a feature is considered "known" or "unknown" is relative to the implementation, whereas +//! the term "supports" is used in reference to a particular set of [`Features`]. That is, a node +//! supports a feature if it advertises the feature (as either required or optional) to its peers. +//! And the implementation can interpret a feature if the feature is known to it. +//! +//! The following features are currently required in the LDK: +//! - `VariableLengthOnion` - requires/supports variable-length routing onion payloads +//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md) for more information). +//! - `StaticRemoteKey` - requires/supports static key for remote output +//! (see [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more information). +//! +//! The following features are currently supported in the LDK: +//! - `DataLossProtect` - requires/supports that a node which has somehow fallen behind, e.g., has been restored from an old backup, +//! can detect that it has fallen behind +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). +//! - `InitialRoutingSync` - requires/supports that the sending node needs a complete routing information dump +//! (see [BOLT-7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#initial-sync) for more information). +//! - `UpfrontShutdownScript` - commits to a shutdown scriptpubkey when opening a channel +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message) for more information). +//! - `GossipQueries` - requires/supports more sophisticated gossip control +//! (see [BOLT-7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md) for more information). +//! - `PaymentSecret` - requires/supports that a node supports payment_secret field +//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md) for more information). +//! - `BasicMPP` - requires/supports that a node can receive basic multi-part payments +//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#basic-multi-part-payments) for more information). +//! - `Wumbo` - requires/supports that a node create large channels. Called `option_support_large_channel` in the spec. +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message) for more information). +//! - `AnchorsZeroFeeHtlcTx` - requires/supports that commitment transactions include anchor outputs +//! and HTLC transactions are pre-signed with zero fee (see +//! [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more +//! information). +//! - `RouteBlinding` - requires/supports that a node can relay payments over blinded paths +//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#route-blinding) for more information). +//! - `ShutdownAnySegwit` - requires/supports that future segwit versions are allowed in `shutdown` +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). +//! - `OnionMessages` - requires/supports forwarding onion messages +//! (see [BOLT-7](https://github.com/lightning/bolts/pull/759/files) for more information). +// TODO: update link +//! - `ChannelType` - node supports the channel_type field in open/accept +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). +//! - `SCIDPrivacy` - supply channel aliases for routing +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). +//! - `PaymentMetadata` - include additional data in invoices which is passed to recipients in the +//! onion. +//! (see [BOLT-11](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md) for +//! more). +//! - `ZeroConf` - supports accepting HTLCs and using channels prior to funding confirmation +//! (see +//! [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-channel_ready-message) +//! for more info). +//! - `Keysend` - send funds to a node without an invoice +//! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information). +//! - `Trampoline` - supports receiving and forwarding Trampoline payments +//! (see the [`Trampoline` feature proposal](https://github.com/lightning/bolts/pull/836) for more information). +//! +//! LDK knows about the following features, but does not support them: +//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be +//! vulnerable (see this +//! [mailing list post](https://lists.linuxfoundation.org/pipermail/lightning-dev/2020-September/002796.html) +//! for more information). +//! +//! [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md + +use core::borrow::Borrow; +use core::hash::{Hash, Hasher}; +use core::marker::PhantomData; +use core::{cmp, fmt}; + +use alloc::vec; +use alloc::vec::Vec; + +use bech32::{u5, Base32Len, FromBase32, ToBase32, WriteBase32}; + +mod sealed { + use super::*; + + /// The context in which [`Features`] are applicable. Defines which features are known to the + /// implementation, though specification of them as required or optional is up to the code + /// constructing a features object. + pub trait Context { + /// Bitmask for selecting features that are known to the implementation. + const KNOWN_FEATURE_MASK: &'static [u8]; + } + + /// Defines a [`Context`] by stating which features it requires and which are optional. Features + /// are specified as a comma-separated list of bytes where each byte is a pipe-delimited list of + /// feature identifiers. + macro_rules! define_context { + ($context: ident, [$( $( $known_feature: ident )|*, )*]) => { + #[derive(Eq, PartialEq)] + pub struct $context {} + + impl Context for $context { + const KNOWN_FEATURE_MASK: &'static [u8] = &[ + $( + 0b00_00_00_00 $(| + ::REQUIRED_MASK | + ::OPTIONAL_MASK)*, + )* + ]; + } + + impl alloc::fmt::Display for Features<$context> { + fn fmt(&self, fmt: &mut alloc::fmt::Formatter) -> Result<(), alloc::fmt::Error> { + $( + $( + fmt.write_fmt(format_args!("{}: {}, ", stringify!($known_feature), + if <$context as $known_feature>::requires_feature(&self.flags) { "required" } + else if <$context as $known_feature>::supports_feature(&self.flags) { "supported" } + else { "not supported" }))?; + )* + {} // Rust gets mad if we only have a $()* block here, so add a dummy {} + )* + fmt.write_fmt(format_args!("unknown flags: {}", + if self.requires_unknown_bits() { "required" } + else if self.supports_unknown_bits() { "supported" } else { "none" })) + } + } + }; + } + + define_context!( + InitContext, + [ + // Byte 0 + DataLossProtect | InitialRoutingSync | UpfrontShutdownScript | GossipQueries, + // Byte 1 + VariableLengthOnion | StaticRemoteKey | PaymentSecret, + // Byte 2 + BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, + // Byte 3 + RouteBlinding | ShutdownAnySegwit | Taproot, + // Byte 4 + OnionMessages, + // Byte 5 + ChannelType | SCIDPrivacy, + // Byte 6 + ZeroConf, + // Byte 7 + Trampoline, + ] + ); + define_context!( + NodeContext, + [ + // Byte 0 + DataLossProtect | UpfrontShutdownScript | GossipQueries, + // Byte 1 + VariableLengthOnion | StaticRemoteKey | PaymentSecret, + // Byte 2 + BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, + // Byte 3 + RouteBlinding | ShutdownAnySegwit | Taproot, + // Byte 4 + OnionMessages, + // Byte 5 + ChannelType | SCIDPrivacy, + // Byte 6 + ZeroConf | Keysend, + // Byte 7 + Trampoline, + ] + ); + define_context!(ChannelContext, []); + define_context!(Bolt11InvoiceContext, [ + // Byte 0 + , + // Byte 1 + VariableLengthOnion | PaymentSecret, + // Byte 2 + BasicMPP, + // Byte 3 + , + // Byte 4 + , + // Byte 5 + , + // Byte 6 + PaymentMetadata, + // Byte 7 + Trampoline, + ]); + define_context!(OfferContext, []); + define_context!(InvoiceRequestContext, []); + define_context!(Bolt12InvoiceContext, [ + // Byte 0 + , + // Byte 1 + , + // Byte 2 + BasicMPP, + ]); + define_context!(BlindedHopContext, []); + // This isn't a "real" feature context, and is only used in the channel_type field in an + // `OpenChannel` message. + define_context!(ChannelTypeContext, [ + // Byte 0 + , + // Byte 1 + StaticRemoteKey, + // Byte 2 + AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, + // Byte 3 + Taproot, + // Byte 4 + , + // Byte 5 + SCIDPrivacy, + // Byte 6 + ZeroConf, + ]); + + /// Defines a feature with the given bits for the specified [`Context`]s. The generated trait is + /// useful for manipulating feature flags. + macro_rules! define_feature { + ($odd_bit: expr, $feature: ident, [$($context: ty),+], $doc: expr, $optional_setter: ident, + $required_setter: ident, $supported_getter: ident) => { + #[doc = $doc] + /// + /// See [BOLT #9] for details. + /// + /// [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md + pub trait $feature: Context { + /// The bit used to signify that the feature is required. + const EVEN_BIT: usize = $odd_bit - 1; + + /// The bit used to signify that the feature is optional. + const ODD_BIT: usize = $odd_bit; + + /// Assertion that [`EVEN_BIT`] is actually even. + /// + /// [`EVEN_BIT`]: #associatedconstant.EVEN_BIT + const ASSERT_EVEN_BIT_PARITY: usize; + + /// Assertion that [`ODD_BIT`] is actually odd. + /// + /// [`ODD_BIT`]: #associatedconstant.ODD_BIT + const ASSERT_ODD_BIT_PARITY: usize; + + /// Assertion that the bits are set in the context's [`KNOWN_FEATURE_MASK`]. + /// + /// [`KNOWN_FEATURE_MASK`]: Context::KNOWN_FEATURE_MASK + #[cfg(not(any(test, feature = "_test_utils")))] // We violate this constraint with `UnknownFeature` + const ASSERT_BITS_IN_MASK: u8; + + /// The byte where the feature is set. + const BYTE_OFFSET: usize = Self::EVEN_BIT / 8; + + /// The bitmask for the feature's required flag relative to the [`BYTE_OFFSET`]. + /// + /// [`BYTE_OFFSET`]: #associatedconstant.BYTE_OFFSET + const REQUIRED_MASK: u8 = 1 << (Self::EVEN_BIT - 8 * Self::BYTE_OFFSET); + + /// The bitmask for the feature's optional flag relative to the [`BYTE_OFFSET`]. + /// + /// [`BYTE_OFFSET`]: #associatedconstant.BYTE_OFFSET + const OPTIONAL_MASK: u8 = 1 << (Self::ODD_BIT - 8 * Self::BYTE_OFFSET); + + /// Returns whether the feature is required by the given flags. + #[inline] + fn requires_feature(flags: &Vec) -> bool { + flags.len() > Self::BYTE_OFFSET && + (flags[Self::BYTE_OFFSET] & Self::REQUIRED_MASK) != 0 + } + + /// Returns whether the feature is supported by the given flags. + #[inline] + fn supports_feature(flags: &Vec) -> bool { + flags.len() > Self::BYTE_OFFSET && + (flags[Self::BYTE_OFFSET] & (Self::REQUIRED_MASK | Self::OPTIONAL_MASK)) != 0 + } + + /// Sets the feature's required (even) bit in the given flags. + #[inline] + fn set_required_bit(flags: &mut Vec) { + if flags.len() <= Self::BYTE_OFFSET { + flags.resize(Self::BYTE_OFFSET + 1, 0u8); + } + + flags[Self::BYTE_OFFSET] |= Self::REQUIRED_MASK; + flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK; + } + + /// Sets the feature's optional (odd) bit in the given flags. + #[inline] + fn set_optional_bit(flags: &mut Vec) { + if flags.len() <= Self::BYTE_OFFSET { + flags.resize(Self::BYTE_OFFSET + 1, 0u8); + } + + flags[Self::BYTE_OFFSET] |= Self::OPTIONAL_MASK; + } + + /// Clears the feature's required (even) and optional (odd) bits from the given + /// flags. + #[inline] + fn clear_bits(flags: &mut Vec) { + if flags.len() > Self::BYTE_OFFSET { + flags[Self::BYTE_OFFSET] &= !Self::REQUIRED_MASK; + flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK; + } + + let last_non_zero_byte = flags.iter().rposition(|&byte| byte != 0); + let size = if let Some(offset) = last_non_zero_byte { offset + 1 } else { 0 }; + flags.resize(size, 0u8); + } + } + + impl Features { + /// Set this feature as optional. + pub fn $optional_setter(&mut self) { + ::set_optional_bit(&mut self.flags); + } + + /// Set this feature as required. + pub fn $required_setter(&mut self) { + ::set_required_bit(&mut self.flags); + } + + /// Checks if this feature is supported. + pub fn $supported_getter(&self) -> bool { + ::supports_feature(&self.flags) + } + } + + $( + impl $feature for $context { + // EVEN_BIT % 2 == 0 + const ASSERT_EVEN_BIT_PARITY: usize = 0 - (::EVEN_BIT % 2); + + // ODD_BIT % 2 == 1 + const ASSERT_ODD_BIT_PARITY: usize = (::ODD_BIT % 2) - 1; + + // (byte & (REQUIRED_MASK | OPTIONAL_MASK)) >> (EVEN_BIT % 8) == 3 + #[cfg(not(any(test, feature = "_test_utils")))] // We violate this constraint with `UnknownFeature` + const ASSERT_BITS_IN_MASK: u8 = + ((<$context>::KNOWN_FEATURE_MASK[::BYTE_OFFSET] & (::REQUIRED_MASK | ::OPTIONAL_MASK)) + >> (::EVEN_BIT % 8)) - 3; + } + )* + }; + ($odd_bit: expr, $feature: ident, [$($context: ty),+], $doc: expr, $optional_setter: ident, + $required_setter: ident, $supported_getter: ident, $required_getter: ident) => { + define_feature!($odd_bit, $feature, [$($context),+], $doc, $optional_setter, $required_setter, $supported_getter); + impl Features { + /// Checks if this feature is required. + pub fn $required_getter(&self) -> bool { + ::requires_feature(&self.flags) + } + } + } + } + + define_feature!( + 1, + DataLossProtect, + [InitContext, NodeContext], + "Feature flags for `option_data_loss_protect`.", + set_data_loss_protect_optional, + set_data_loss_protect_required, + supports_data_loss_protect, + requires_data_loss_protect + ); + // NOTE: Per Bolt #9, initial_routing_sync has no even bit. + define_feature!( + 3, + InitialRoutingSync, + [InitContext], + "Feature flags for `initial_routing_sync`.", + set_initial_routing_sync_optional, + set_initial_routing_sync_required, + initial_routing_sync + ); + define_feature!( + 5, + UpfrontShutdownScript, + [InitContext, NodeContext], + "Feature flags for `option_upfront_shutdown_script`.", + set_upfront_shutdown_script_optional, + set_upfront_shutdown_script_required, + supports_upfront_shutdown_script, + requires_upfront_shutdown_script + ); + define_feature!( + 7, + GossipQueries, + [InitContext, NodeContext], + "Feature flags for `gossip_queries`.", + set_gossip_queries_optional, + set_gossip_queries_required, + supports_gossip_queries, + requires_gossip_queries + ); + define_feature!( + 9, + VariableLengthOnion, + [InitContext, NodeContext, Bolt11InvoiceContext], + "Feature flags for `var_onion_optin`.", + set_variable_length_onion_optional, + set_variable_length_onion_required, + supports_variable_length_onion, + requires_variable_length_onion + ); + define_feature!( + 13, + StaticRemoteKey, + [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for `option_static_remotekey`.", + set_static_remote_key_optional, + set_static_remote_key_required, + supports_static_remote_key, + requires_static_remote_key + ); + define_feature!( + 15, + PaymentSecret, + [InitContext, NodeContext, Bolt11InvoiceContext], + "Feature flags for `payment_secret`.", + set_payment_secret_optional, + set_payment_secret_required, + supports_payment_secret, + requires_payment_secret + ); + define_feature!( + 17, + BasicMPP, + [InitContext, NodeContext, Bolt11InvoiceContext, Bolt12InvoiceContext], + "Feature flags for `basic_mpp`.", + set_basic_mpp_optional, + set_basic_mpp_required, + supports_basic_mpp, + requires_basic_mpp + ); + define_feature!( + 19, + Wumbo, + [InitContext, NodeContext], + "Feature flags for `option_support_large_channel` (aka wumbo channels).", + set_wumbo_optional, + set_wumbo_required, + supports_wumbo, + requires_wumbo + ); + define_feature!( + 21, + AnchorsNonzeroFeeHtlcTx, + [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for `option_anchors_nonzero_fee_htlc_tx`.", + set_anchors_nonzero_fee_htlc_tx_optional, + set_anchors_nonzero_fee_htlc_tx_required, + supports_anchors_nonzero_fee_htlc_tx, + requires_anchors_nonzero_fee_htlc_tx + ); + define_feature!( + 23, + AnchorsZeroFeeHtlcTx, + [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for `option_anchors_zero_fee_htlc_tx`.", + set_anchors_zero_fee_htlc_tx_optional, + set_anchors_zero_fee_htlc_tx_required, + supports_anchors_zero_fee_htlc_tx, + requires_anchors_zero_fee_htlc_tx + ); + define_feature!( + 25, + RouteBlinding, + [InitContext, NodeContext], + "Feature flags for `option_route_blinding`.", + set_route_blinding_optional, + set_route_blinding_required, + supports_route_blinding, + requires_route_blinding + ); + define_feature!( + 27, + ShutdownAnySegwit, + [InitContext, NodeContext], + "Feature flags for `opt_shutdown_anysegwit`.", + set_shutdown_any_segwit_optional, + set_shutdown_any_segwit_required, + supports_shutdown_anysegwit, + requires_shutdown_anysegwit + ); + define_feature!( + 31, + Taproot, + [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for `option_taproot`.", + set_taproot_optional, + set_taproot_required, + supports_taproot, + requires_taproot + ); + define_feature!( + 39, + OnionMessages, + [InitContext, NodeContext], + "Feature flags for `option_onion_messages`.", + set_onion_messages_optional, + set_onion_messages_required, + supports_onion_messages, + requires_onion_messages + ); + define_feature!( + 45, + ChannelType, + [InitContext, NodeContext], + "Feature flags for `option_channel_type`.", + set_channel_type_optional, + set_channel_type_required, + supports_channel_type, + requires_channel_type + ); + define_feature!(47, SCIDPrivacy, [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for only forwarding with SCID aliasing. Called `option_scid_alias` in the BOLTs", + set_scid_privacy_optional, set_scid_privacy_required, supports_scid_privacy, requires_scid_privacy); + define_feature!( + 49, + PaymentMetadata, + [Bolt11InvoiceContext], + "Feature flags for payment metadata in invoices.", + set_payment_metadata_optional, + set_payment_metadata_required, + supports_payment_metadata, + requires_payment_metadata + ); + define_feature!(51, ZeroConf, [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for accepting channels with zero confirmations. Called `option_zeroconf` in the BOLTs", + set_zero_conf_optional, set_zero_conf_required, supports_zero_conf, requires_zero_conf); + define_feature!( + 55, + Keysend, + [NodeContext], + "Feature flags for keysend payments.", + set_keysend_optional, + set_keysend_required, + supports_keysend, + requires_keysend + ); + define_feature!( + 57, + Trampoline, + [InitContext, NodeContext, Bolt11InvoiceContext], + "Feature flags for Trampoline routing.", + set_trampoline_routing_optional, + set_trampoline_routing_required, + supports_trampoline_routing, + requires_trampoline_routing + ); + // Note: update the module-level docs when a new feature bit is added! + + #[cfg(any(test, feature = "_test_utils"))] + define_feature!( + 123456789, + UnknownFeature, + [ + NodeContext, + ChannelContext, + Bolt11InvoiceContext, + OfferContext, + InvoiceRequestContext, + Bolt12InvoiceContext, + BlindedHopContext + ], + "Feature flags for an unknown feature used in testing.", + set_unknown_feature_optional, + set_unknown_feature_required, + supports_unknown_test_feature, + requires_unknown_test_feature + ); +} + +const ANY_REQUIRED_FEATURES_MASK: u8 = 0b01_01_01_01; +const ANY_OPTIONAL_FEATURES_MASK: u8 = 0b10_10_10_10; + +/// Tracks the set of features which a node implements, templated by the context in which it +/// appears. +/// +/// This is not exported to bindings users as we map the concrete feature types below directly instead +#[derive(Eq)] +pub struct Features { + /// Note that, for convenience, flags is LITTLE endian (despite being big-endian on the wire) + flags: Vec, + mark: PhantomData, +} + +impl> core::ops::BitOrAssign for Features { + fn bitor_assign(&mut self, rhs: Rhs) { + let total_feature_len = cmp::max(self.flags.len(), rhs.borrow().flags.len()); + self.flags.resize(total_feature_len, 0u8); + for (byte, rhs_byte) in self.flags.iter_mut().zip(rhs.borrow().flags.iter()) { + *byte |= *rhs_byte; + } + } +} + +impl core::ops::BitOr for Features { + type Output = Self; + + fn bitor(mut self, o: Self) -> Self { + self |= o; + self + } +} + +impl Clone for Features { + fn clone(&self) -> Self { + Self { flags: self.flags.clone(), mark: PhantomData } + } +} +impl Hash for Features { + fn hash(&self, hasher: &mut H) { + let mut nonzero_flags = &self.flags[..]; + while nonzero_flags.last() == Some(&0) { + nonzero_flags = &nonzero_flags[..nonzero_flags.len() - 1]; + } + nonzero_flags.hash(hasher); + } +} +impl PartialEq for Features { + fn eq(&self, o: &Self) -> bool { + let mut o_iter = o.flags.iter(); + let mut self_iter = self.flags.iter(); + loop { + match (o_iter.next(), self_iter.next()) { + (Some(o), Some(us)) => { + if o != us { + return false; + } + }, + (Some(b), None) | (None, Some(b)) => { + if *b != 0 { + return false; + } + }, + (None, None) => return true, + } + } + } +} +impl PartialOrd for Features { + fn partial_cmp(&self, other: &Self) -> Option { + self.flags.partial_cmp(&other.flags) + } +} +impl Ord for Features { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.flags.cmp(&other.flags) + } +} +impl fmt::Debug for Features { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.flags.fmt(fmt) + } +} + +/// Features used within an `init` message. +pub type InitFeatures = Features; +/// Features used within a `node_announcement` message. +pub type NodeFeatures = Features; +/// Features used within a `channel_announcement` message. +pub type ChannelFeatures = Features; +/// Features used within an invoice. +pub type Bolt11InvoiceFeatures = Features; +/// Features used within an `offer`. +pub type OfferFeatures = Features; +/// Features used within an `invoice_request`. +pub type InvoiceRequestFeatures = Features; +/// Features used within an `invoice`. +pub type Bolt12InvoiceFeatures = Features; +/// Features used within BOLT 4 encrypted_data_tlv and BOLT 12 blinded_payinfo +pub type BlindedHopFeatures = Features; + +/// Features used within the channel_type field in an OpenChannel message. +/// +/// A channel is always of some known "type", describing the transaction formats used and the exact +/// semantics of our interaction with our peer. +/// +/// Note that because a channel is a specific type which is proposed by the opener and accepted by +/// the counterparty, only required features are allowed here. +/// +/// This is serialized differently from other feature types - it is not prefixed by a length, and +/// thus must only appear inside a TLV where its length is known in advance. +pub type ChannelTypeFeatures = Features; + +impl InitFeatures { + #[doc(hidden)] + /// Converts `InitFeatures` to `Features`. Only known `InitFeatures` relevant to context `C` + /// are included in the result. + pub fn to_context(&self) -> Features { + self.to_context_internal() + } +} + +impl Bolt11InvoiceFeatures { + #[doc(hidden)] + /// Converts `Bolt11InvoiceFeatures` to `Features`. Only known `Bolt11InvoiceFeatures` relevant to + /// context `C` are included in the result. + pub fn to_context(&self) -> Features { + self.to_context_internal() + } + + /// Getting a route for a keysend payment to a private node requires providing the payee's + /// features (since they were not announced in a node announcement). However, keysend payments + /// don't have an invoice to pull the payee's features from, so this method is provided for use + /// when a [`Bolt11InvoiceFeatures`] is required in a route. + /// + /// MPP keysend is not widely supported yet, so we parameterize support to allow the user to + /// choose whether their router should find multi-part routes. + pub fn for_keysend(allow_mpp: bool) -> Bolt11InvoiceFeatures { + let mut res = Bolt11InvoiceFeatures::empty(); + res.set_variable_length_onion_optional(); + if allow_mpp { + res.set_basic_mpp_optional(); + } + res + } +} + +impl Bolt12InvoiceFeatures { + #[doc(hidden)] + /// Converts [`Bolt12InvoiceFeatures`] to [`Features`]. Only known [`Bolt12InvoiceFeatures`] + /// relevant to context `C` are included in the result. + pub fn to_context(&self) -> Features { + self.to_context_internal() + } +} + +impl ChannelTypeFeatures { + #[doc(hidden)] + // Maps the relevant `InitFeatures` to `ChannelTypeFeatures`. Any unknown features to + // `ChannelTypeFeatures` are not included in the result. + pub fn from_init(init: &InitFeatures) -> Self { + let mut ret = init.to_context_internal(); + // ChannelTypeFeatures must only contain required bits, so we OR the required forms of all + // optional bits and then AND out the optional ones. + for byte in ret.flags.iter_mut() { + *byte |= (*byte & ANY_OPTIONAL_FEATURES_MASK) >> 1; + *byte &= ANY_REQUIRED_FEATURES_MASK; + } + ret + } + + /// Constructs a ChannelTypeFeatures with only static_remotekey set + pub fn only_static_remote_key() -> Self { + let mut ret = Self::empty(); + ::set_required_bit(&mut ret.flags); + ret + } + + /// Constructs a ChannelTypeFeatures with anchors support + pub fn anchors_zero_htlc_fee_and_dependencies() -> Self { + let mut ret = Self::empty(); + ::set_required_bit(&mut ret.flags); + ::set_required_bit( + &mut ret.flags, + ); + ret + } +} + +impl ToBase32 for Bolt11InvoiceFeatures { + fn write_base32(&self, writer: &mut W) -> Result<(), ::Err> { + // Explanation for the "4": the normal way to round up when dividing is to add the divisor + // minus one before dividing + let length_u5s = (self.flags.len() * 8 + 4) / 5 as usize; + let mut res_u5s: Vec = vec![u5::try_from_u8(0).unwrap(); length_u5s]; + for (byte_idx, byte) in self.flags.iter().enumerate() { + let bit_pos_from_left_0_indexed = byte_idx * 8; + let new_u5_idx = length_u5s - (bit_pos_from_left_0_indexed / 5) as usize - 1; + let new_bit_pos = bit_pos_from_left_0_indexed % 5; + let shifted_chunk_u16 = (*byte as u16) << new_bit_pos; + let curr_u5_as_u8 = res_u5s[new_u5_idx].to_u8(); + res_u5s[new_u5_idx] = + u5::try_from_u8(curr_u5_as_u8 | ((shifted_chunk_u16 & 0x001f) as u8)).unwrap(); + if new_u5_idx > 0 { + let curr_u5_as_u8 = res_u5s[new_u5_idx - 1].to_u8(); + res_u5s[new_u5_idx - 1] = + u5::try_from_u8(curr_u5_as_u8 | (((shifted_chunk_u16 >> 5) & 0x001f) as u8)) + .unwrap(); + } + if new_u5_idx > 1 { + let curr_u5_as_u8 = res_u5s[new_u5_idx - 2].to_u8(); + res_u5s[new_u5_idx - 2] = + u5::try_from_u8(curr_u5_as_u8 | (((shifted_chunk_u16 >> 10) & 0x001f) as u8)) + .unwrap(); + } + } + // Trim the highest feature bits. + while !res_u5s.is_empty() && res_u5s[0] == u5::try_from_u8(0).unwrap() { + res_u5s.remove(0); + } + writer.write(&res_u5s) + } +} + +impl Base32Len for Bolt11InvoiceFeatures { + fn base32_len(&self) -> usize { + self.to_base32().len() + } +} + +impl FromBase32 for Bolt11InvoiceFeatures { + type Err = bech32::Error; + + fn from_base32(field_data: &[u5]) -> Result { + // Explanation for the "7": the normal way to round up when dividing is to add the divisor + // minus one before dividing + let length_bytes = (field_data.len() * 5 + 7) / 8 as usize; + let mut res_bytes: Vec = vec![0; length_bytes]; + for (u5_idx, chunk) in field_data.iter().enumerate() { + let bit_pos_from_right_0_indexed = (field_data.len() - u5_idx - 1) * 5; + let new_byte_idx = (bit_pos_from_right_0_indexed / 8) as usize; + let new_bit_pos = bit_pos_from_right_0_indexed % 8; + let chunk_u16 = chunk.to_u8() as u16; + res_bytes[new_byte_idx] |= ((chunk_u16 << new_bit_pos) & 0xff) as u8; + if new_byte_idx != length_bytes - 1 { + res_bytes[new_byte_idx + 1] |= ((chunk_u16 >> (8 - new_bit_pos)) & 0xff) as u8; + } + } + // Trim the highest feature bits. + while !res_bytes.is_empty() && res_bytes[res_bytes.len() - 1] == 0 { + res_bytes.pop(); + } + Ok(Bolt11InvoiceFeatures::from_le_bytes(res_bytes)) + } +} + +impl Features { + /// Create a blank Features with no features set + pub fn empty() -> Self { + Features { flags: Vec::new(), mark: PhantomData } + } + + /// Converts `Features` to `Features`. Only known `T` features relevant to context `C` are + /// included in the result. + fn to_context_internal(&self) -> Features { + let from_byte_count = T::KNOWN_FEATURE_MASK.len(); + let to_byte_count = C::KNOWN_FEATURE_MASK.len(); + let mut flags = Vec::new(); + for (i, byte) in self.flags.iter().enumerate() { + if i < from_byte_count && i < to_byte_count { + let from_known_features = T::KNOWN_FEATURE_MASK[i]; + let to_known_features = C::KNOWN_FEATURE_MASK[i]; + flags.push(byte & from_known_features & to_known_features); + } + } + Features:: { flags, mark: PhantomData } + } + + /// Create a Features given a set of flags, in little-endian. This is in reverse byte order from + /// most on-the-wire encodings. + /// + /// This is not exported to bindings users as we don't support export across multiple T + pub fn from_le_bytes(flags: Vec) -> Features { + Features { flags, mark: PhantomData } + } + + /// Returns the feature set as a list of bytes, in little-endian. This is in reverse byte order + /// from most on-the-wire encodings. + pub fn le_flags(&self) -> &[u8] { + &self.flags + } + + /// Create a [`Features`] given a set of flags, in big-endian. This is in byte order from + /// most on-the-wire encodings. + /// + /// This is not exported to bindings users as we don't support export across multiple T + pub fn from_be_bytes(mut flags: Vec) -> Features { + flags.reverse(); // Swap to little-endian + Self { flags, mark: PhantomData } + } + + /// Returns true if this `Features` has any optional flags set + pub fn supports_any_optional_bits(&self) -> bool { + self.flags.iter().any(|&byte| (byte & ANY_OPTIONAL_FEATURES_MASK) != 0) + } + + /// Returns true if this `Features` object contains required features unknown by `other`. + pub fn requires_unknown_bits_from(&self, other: &Self) -> bool { + // Bitwise AND-ing with all even bits set except for known features will select required + // unknown features. + self.flags.iter().enumerate().any(|(i, &byte)| { + let unknown_features = unset_features_mask_at_position(other, i); + (byte & (ANY_REQUIRED_FEATURES_MASK & unknown_features)) != 0 + }) + } + + /// Returns the set of required features unknown by `other`, as their bit position. + pub fn required_unknown_bits_from(&self, other: &Self) -> Vec { + let mut unknown_bits = Vec::new(); + + // Bitwise AND-ing with all even bits set except for known features will select required + // unknown features. + self.flags.iter().enumerate().for_each(|(i, &byte)| { + let unknown_features = unset_features_mask_at_position(other, i); + if byte & unknown_features != 0 { + for bit in (0..8).step_by(2) { + if ((byte & unknown_features) >> bit) & 1 == 1 { + unknown_bits.push(i * 8 + bit); + } + } + } + }); + + unknown_bits + } + + /// Returns true if this `Features` object contains unknown feature flags which are set as + /// "required". + pub fn requires_unknown_bits(&self) -> bool { + // Bitwise AND-ing with all even bits set except for known features will select required + // unknown features. + let mut known_chunks = T::KNOWN_FEATURE_MASK.chunks(8); + for chunk in self.flags.chunks(8) { + let mut flag_bytes = [0; 8]; + flag_bytes[..chunk.len()].copy_from_slice(&chunk); + let flag_int = u64::from_le_bytes(flag_bytes); + + let known_chunk = known_chunks.next().unwrap_or(&[0; 0]); + let mut known_bytes = [0; 8]; + known_bytes[..known_chunk.len()].copy_from_slice(&known_chunk); + let known_int = u64::from_le_bytes(known_bytes); + + const REQ_MASK: u64 = u64::from_le_bytes([ANY_REQUIRED_FEATURES_MASK; 8]); + if flag_int & (REQ_MASK & !known_int) != 0 { + return true; + } + } + false + } + + /// Returns true if this `Features` supports any bits which we do not know of + pub fn supports_unknown_bits(&self) -> bool { + // Bitwise AND-ing with all even and odd bits set except for known features will select + // both required and optional unknown features. + let byte_count = T::KNOWN_FEATURE_MASK.len(); + self.flags.iter().enumerate().any(|(i, &byte)| { + let unknown_features = + if i < byte_count { !T::KNOWN_FEATURE_MASK[i] } else { 0b11_11_11_11 }; + (byte & unknown_features) != 0 + }) + } + + /// Sets a required feature bit. Errors if `bit` is outside the feature range as defined + /// by [BOLT 9]. + /// + /// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will + /// be set instead (i.e., `bit - 1`). + /// + /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md + pub fn set_required_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_feature_bit(bit - (bit % 2)) + } + + /// Sets an optional feature bit. Errors if `bit` is outside the feature range as defined + /// by [BOLT 9]. + /// + /// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be + /// set instead (i.e., `bit + 1`). + /// + /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md + pub fn set_optional_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_feature_bit(bit + (1 - (bit % 2))) + } + + fn set_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + if bit > 255 { + return Err(()); + } + self.set_bit(bit, false) + } + + /// Sets a required custom feature bit. Errors if `bit` is outside the custom range as defined + /// by [bLIP 2] or if it is a known `T` feature. + /// + /// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will + /// be set instead (i.e., `bit - 1`). + /// + /// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits + pub fn set_required_custom_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_custom_bit(bit - (bit % 2)) + } + + /// Sets an optional custom feature bit. Errors if `bit` is outside the custom range as defined + /// by [bLIP 2] or if it is a known `T` feature. + /// + /// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be + /// set instead (i.e., `bit + 1`). + /// + /// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits + pub fn set_optional_custom_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_custom_bit(bit + (1 - (bit % 2))) + } + + fn set_custom_bit(&mut self, bit: usize) -> Result<(), ()> { + if bit < 256 { + return Err(()); + } + self.set_bit(bit, true) + } + + fn set_bit(&mut self, bit: usize, custom: bool) -> Result<(), ()> { + let byte_offset = bit / 8; + let mask = 1 << (bit - 8 * byte_offset); + if byte_offset < T::KNOWN_FEATURE_MASK.len() && custom { + if (T::KNOWN_FEATURE_MASK[byte_offset] & mask) != 0 { + return Err(()); + } + } + + if self.flags.len() <= byte_offset { + self.flags.resize(byte_offset + 1, 0u8); + } + + self.flags[byte_offset] |= mask; + + Ok(()) + } +} + +impl Features { + /// Unsets the `upfront_shutdown_script` feature + pub fn clear_upfront_shutdown_script(mut self) -> Self { + ::clear_bits(&mut self.flags); + self + } +} + +impl Features { + /// Unsets the `shutdown_anysegwit` feature + pub fn clear_shutdown_anysegwit(mut self) -> Self { + ::clear_bits(&mut self.flags); + self + } +} + +impl Features { + /// Unsets the `wumbo` feature + pub fn clear_wumbo(mut self) -> Self { + ::clear_bits(&mut self.flags); + self + } +} + +impl Features { + /// Unsets the `scid_privacy` feature + pub fn clear_scid_privacy(&mut self) { + ::clear_bits(&mut self.flags); + } +} + +impl Features { + /// Unsets the `anchors_zero_fee_htlc_tx` feature + pub fn clear_anchors_zero_fee_htlc_tx(&mut self) { + ::clear_bits(&mut self.flags); + } +} + +impl Features { + /// Unsets the `route_blinding` feature + pub fn clear_route_blinding(&mut self) { + ::clear_bits(&mut self.flags); + } +} + +#[cfg(any(test, feature = "_test_utils"))] +impl Features { + /// Sets an unknown feature for testing + pub fn unknown() -> Self { + let mut features = Self::empty(); + features.set_unknown_feature_required(); + features + } +} + +pub(crate) fn unset_features_mask_at_position( + other: &Features, index: usize, +) -> u8 { + if index < other.flags.len() { + // Form a mask similar to !T::KNOWN_FEATURE_MASK only for `other` + !(other.flags[index] + | ((other.flags[index] >> 1) & ANY_REQUIRED_FEATURES_MASK) + | ((other.flags[index] << 1) & ANY_OPTIONAL_FEATURES_MASK)) + } else { + 0b11_11_11_11 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sanity_test_unknown_bits() { + let features = ChannelFeatures::empty(); + assert!(!features.requires_unknown_bits()); + assert!(!features.supports_unknown_bits()); + + let mut features = ChannelFeatures::empty(); + features.set_unknown_feature_required(); + assert!(features.requires_unknown_bits()); + assert!(features.supports_unknown_bits()); + assert_eq!(features.required_unknown_bits_from(&ChannelFeatures::empty()), vec![123456788]); + + let mut features = ChannelFeatures::empty(); + features.set_unknown_feature_optional(); + assert!(!features.requires_unknown_bits()); + assert!(features.supports_unknown_bits()); + assert_eq!(features.required_unknown_bits_from(&ChannelFeatures::empty()), vec![]); + + let mut features = ChannelFeatures::empty(); + features.set_unknown_feature_required(); + features.set_custom_bit(123456786).unwrap(); + assert!(features.requires_unknown_bits()); + assert!(features.supports_unknown_bits()); + assert_eq!( + features.required_unknown_bits_from(&ChannelFeatures::empty()), + vec![123456786, 123456788] + ); + + let mut limiter = ChannelFeatures::empty(); + limiter.set_unknown_feature_optional(); + assert_eq!(features.required_unknown_bits_from(&limiter), vec![123456786]); + } + + #[test] + fn requires_unknown_bits_from() { + let mut features1 = InitFeatures::empty(); + let mut features2 = InitFeatures::empty(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features1.set_data_loss_protect_required(); + assert!(features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features2.set_data_loss_protect_optional(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features2.set_gossip_queries_required(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(features2.requires_unknown_bits_from(&features1)); + + features1.set_gossip_queries_optional(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features1.set_variable_length_onion_required(); + assert!(features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features2.set_variable_length_onion_optional(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features1.set_basic_mpp_required(); + features2.set_wumbo_required(); + assert!(features1.requires_unknown_bits_from(&features2)); + assert!(features2.requires_unknown_bits_from(&features1)); + } + + #[test] + fn convert_to_context_with_relevant_flags() { + let mut init_features = InitFeatures::empty(); + // Set a bunch of features we use, plus initial_routing_sync_required (which shouldn't get + // converted as it's only relevant in an init context). + init_features.set_initial_routing_sync_required(); + init_features.set_data_loss_protect_required(); + init_features.set_variable_length_onion_required(); + init_features.set_static_remote_key_required(); + init_features.set_payment_secret_required(); + init_features.set_basic_mpp_optional(); + init_features.set_wumbo_optional(); + init_features.set_anchors_zero_fee_htlc_tx_optional(); + init_features.set_route_blinding_optional(); + init_features.set_shutdown_any_segwit_optional(); + init_features.set_onion_messages_optional(); + init_features.set_channel_type_optional(); + init_features.set_scid_privacy_optional(); + init_features.set_zero_conf_optional(); + + assert!(init_features.initial_routing_sync()); + assert!(!init_features.supports_upfront_shutdown_script()); + assert!(!init_features.supports_gossip_queries()); + + let node_features: NodeFeatures = init_features.to_context(); + { + // Check that the flags are as expected: + // - option_data_loss_protect (req) + // - var_onion_optin (req) | static_remote_key (req) | payment_secret(req) + // - basic_mpp | wumbo | option_anchors_zero_fee_htlc_tx + // - option_route_blinding | opt_shutdown_anysegwit + // - onion_messages + // - option_channel_type | option_scid_alias + // - option_zeroconf + assert_eq!(node_features.flags.len(), 7); + assert_eq!(node_features.flags[0], 0b00000001); + assert_eq!(node_features.flags[1], 0b01010001); + assert_eq!(node_features.flags[2], 0b10001010); + assert_eq!(node_features.flags[3], 0b00001010); + assert_eq!(node_features.flags[4], 0b10000000); + assert_eq!(node_features.flags[5], 0b10100000); + assert_eq!(node_features.flags[6], 0b00001000); + } + + // Check that cleared flags are kept blank when converting back: + // - initial_routing_sync was not applicable to NodeContext + // - upfront_shutdown_script was cleared before converting + // - gossip_queries was cleared before converting + let features: InitFeatures = node_features.to_context_internal(); + assert!(!features.initial_routing_sync()); + assert!(!features.supports_upfront_shutdown_script()); + assert!(!init_features.supports_gossip_queries()); + } + + #[test] + fn convert_to_context_with_unknown_flags() { + // Ensure the `from` context has fewer known feature bytes than the `to` context. + assert!( + ::KNOWN_FEATURE_MASK.len() + < ::KNOWN_FEATURE_MASK.len() + ); + let mut channel_features = ChannelFeatures::empty(); + channel_features.set_unknown_feature_optional(); + assert!(channel_features.supports_unknown_bits()); + let invoice_features: Bolt11InvoiceFeatures = channel_features.to_context_internal(); + assert!(!invoice_features.supports_unknown_bits()); + } + + #[test] + fn set_feature_bits() { + let mut features = Bolt11InvoiceFeatures::empty(); + features.set_basic_mpp_optional(); + features.set_payment_secret_required(); + assert!(features.supports_basic_mpp()); + assert!(!features.requires_basic_mpp()); + assert!(features.requires_payment_secret()); + assert!(features.supports_payment_secret()); + + // Set flags manually + let mut features = NodeFeatures::empty(); + assert!(features.set_optional_feature_bit(55).is_ok()); + assert!(features.supports_keysend()); + assert!(features.set_optional_feature_bit(255).is_ok()); + assert!(features.set_required_feature_bit(256).is_err()); + } + + #[test] + fn set_custom_bits() { + let mut features = Bolt11InvoiceFeatures::empty(); + features.set_variable_length_onion_optional(); + assert_eq!(features.flags[1], 0b00000010); + + assert!(features.set_optional_custom_bit(255).is_err()); + assert!(features.set_required_custom_bit(256).is_ok()); + assert!(features.set_required_custom_bit(258).is_ok()); + assert_eq!(features.flags[31], 0b00000000); + assert_eq!(features.flags[32], 0b00000101); + + let known_bit = ::EVEN_BIT; + let byte_offset = ::BYTE_OFFSET; + assert_eq!(byte_offset, 1); + assert_eq!(features.flags[byte_offset], 0b00000010); + assert!(features.set_required_custom_bit(known_bit).is_err()); + assert_eq!(features.flags[byte_offset], 0b00000010); + + let mut features = Bolt11InvoiceFeatures::empty(); + assert!(features.set_optional_custom_bit(256).is_ok()); + assert!(features.set_optional_custom_bit(259).is_ok()); + assert_eq!(features.flags[32], 0b00001010); + + let mut features = Bolt11InvoiceFeatures::empty(); + assert!(features.set_required_custom_bit(257).is_ok()); + assert!(features.set_required_custom_bit(258).is_ok()); + assert_eq!(features.flags[32], 0b00000101); + } + + #[test] + fn invoice_features_encoding() { + let features_as_u5s = vec![ + u5::try_from_u8(6).unwrap(), + u5::try_from_u8(10).unwrap(), + u5::try_from_u8(25).unwrap(), + u5::try_from_u8(1).unwrap(), + u5::try_from_u8(10).unwrap(), + u5::try_from_u8(0).unwrap(), + u5::try_from_u8(20).unwrap(), + u5::try_from_u8(2).unwrap(), + u5::try_from_u8(0).unwrap(), + u5::try_from_u8(6).unwrap(), + u5::try_from_u8(0).unwrap(), + u5::try_from_u8(16).unwrap(), + u5::try_from_u8(1).unwrap(), + ]; + let features = Bolt11InvoiceFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]); + + // Test length calculation. + assert_eq!(features.base32_len(), 13); + + // Test serialization. + let features_serialized = features.to_base32(); + assert_eq!(features_as_u5s, features_serialized); + + // Test deserialization. + let features_deserialized = Bolt11InvoiceFeatures::from_base32(&features_as_u5s).unwrap(); + assert_eq!(features, features_deserialized); + } + + #[test] + fn test_channel_type_mapping() { + // If we map an Bolt11InvoiceFeatures with StaticRemoteKey optional, it should map into a + // required-StaticRemoteKey ChannelTypeFeatures. + let mut init_features = InitFeatures::empty(); + init_features.set_static_remote_key_optional(); + let converted_features = ChannelTypeFeatures::from_init(&init_features); + assert_eq!(converted_features, ChannelTypeFeatures::only_static_remote_key()); + assert!(!converted_features.supports_any_optional_bits()); + assert!(converted_features.requires_static_remote_key()); + } + + #[test] + fn test_excess_zero_bytes_ignored() { + // Checks that `Hash` and `PartialEq` ignore excess zero bytes, which may appear due to + // feature conversion or because a peer serialized their feature poorly. + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut zerod_features = InitFeatures::empty(); + zerod_features.flags = vec![0]; + let empty_features = InitFeatures::empty(); + assert!(empty_features.flags.is_empty()); + + assert_eq!(zerod_features, empty_features); + + let mut zerod_hash = DefaultHasher::new(); + zerod_features.hash(&mut zerod_hash); + let mut empty_hash = DefaultHasher::new(); + empty_features.hash(&mut empty_hash); + assert_eq!(zerod_hash.finish(), empty_hash.finish()); + } +} diff --git a/lightning-types/src/lib.rs b/lightning-types/src/lib.rs new file mode 100644 index 00000000000..1539401d383 --- /dev/null +++ b/lightning-types/src/lib.rs @@ -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 or the MIT license +// , 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; diff --git a/lightning-types/src/payment.rs b/lightning-types/src/payment.rs new file mode 100644 index 00000000000..6b8854ac5f8 --- /dev/null +++ b/lightning-types/src/payment.rs @@ -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 or the MIT license +// , 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 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 { + if field_data.len() != 52 { + return Err(bech32::Error::InvalidLength); + } else { + let data_bytes = Vec::::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(&self, writer: &mut W) -> Result<(), ::Err> { + (&self.0[..]).write_base32(writer) + } +} + +impl Base32Len for PaymentSecret { + fn base32_len(&self) -> usize { + 52 + } +} diff --git a/lightning-types/src/routing.rs b/lightning-types/src/routing.rs new file mode 100644 index 00000000000..2335af2f1c3 --- /dev/null +++ b/lightning-types/src/routing.rs @@ -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 or the MIT license +// , 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); + +/// 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, + /// The maximum value in msat available for routing with a single HTLC. + pub htlc_maximum_msat: Option, +} diff --git a/lightning/src/util/string.rs b/lightning-types/src/string.rs similarity index 77% rename from lightning/src/util/string.rs rename to lightning-types/src/string.rs index ab12486a0d8..ae5395a5289 100644 --- a/lightning/src/util/string.rs +++ b/lightning-types/src/string.rs @@ -9,31 +9,13 @@ //! Utilities for strings. +use alloc::string::String; 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` #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] pub struct UntrustedString(pub String); -impl Writeable for UntrustedString { - fn write(&self, w: &mut W) -> Result<(), io::Error> { - self.0.write(w) - } -} - -impl Readable for UntrustedString { - fn read(r: &mut R) -> Result { - let s: String = Readable::read(r)?; - Ok(Self(s)) - } -} - impl fmt::Display for UntrustedString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { PrintableString(&self.0).fmt(f) diff --git a/lightning/Cargo.toml b/lightning/Cargo.toml index a0f1e4027f6..c6d82113053 100644 --- a/lightning/Cargo.toml +++ b/lightning/Cargo.toml @@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] # 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. max_level_off = [] max_level_error = [] @@ -31,8 +31,8 @@ unsafe_revoked_tx_signing = [] # Override signing to not include randomness when generating signatures for test vectors. _test_vectors = [] -no-std = ["hashbrown", "possiblyrandom", "bitcoin/no-std", "core2/alloc", "libm"] -std = ["bitcoin/std", "bech32/std"] +no-std = ["hashbrown", "possiblyrandom", "bitcoin/no-std", "lightning-invoice/no-std", "core2/alloc", "libm"] +std = ["bitcoin/std", "bech32/std", "lightning-invoice/std"] # Generates low-r bitcoin signatures, which saves 1 byte in 50% of the cases grind_signatures = [] @@ -40,6 +40,9 @@ grind_signatures = [] default = ["std", "grind_signatures"] [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 } 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] regex = "1.5.6" +lightning-types = { version = "0.1", path = "../lightning-types", features = ["_test_utils"] } [dev-dependencies.bitcoin] version = "0.31.2" diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index b0dfca4c207..361e12fa563 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -93,7 +93,7 @@ pub(crate) fn verify_channel_type_features(channel_type_features: &Option Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { +/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment +/// [`ChannelManager::send_preflight_probes`]: crate::ln::channelmanager::ChannelManager::send_preflight_probes +pub fn payment_parameters_from_zero_amount_invoice( + invoice: &Bolt11Invoice, amount_msat: u64, +) -> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { if invoice.amount_milli_satoshis().is_some() { Err(()) } 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 /// [`payment_parameters_from_zero_amount_invoice`] should be used. /// -/// [`ChannelManager::send_payment`]: lightning::ln::channelmanager::ChannelManager::send_payment -/// [`ChannelManager::send_preflight_probes`]: lightning::ln::channelmanager::ChannelManager::send_preflight_probes -pub fn payment_parameters_from_invoice(invoice: &Bolt11Invoice) --> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { +/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment +/// [`ChannelManager::send_preflight_probes`]: crate::ln::channelmanager::ChannelManager::send_preflight_probes +pub fn payment_parameters_from_invoice( + invoice: &Bolt11Invoice, +) -> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { if let Some(amount_msat) = invoice.amount_milli_satoshis() { Ok(params_from_invoice(invoice, amount_msat)) } else { @@ -57,18 +59,20 @@ pub fn payment_parameters_from_invoice(invoice: &Bolt11Invoice) } } -fn params_from_invoice(invoice: &Bolt11Invoice, amount_msat: u64) --> (PaymentHash, RecipientOnionFields, RouteParameters) { +fn params_from_invoice( + invoice: &Bolt11Invoice, amount_msat: u64, +) -> (PaymentHash, RecipientOnionFields, RouteParameters) { let payment_hash = PaymentHash((*invoice.payment_hash()).to_byte_array()); let mut recipient_onion = RecipientOnionFields::secret_only(*invoice.payment_secret()); recipient_onion.payment_metadata = invoice.payment_metadata().map(|v| v.clone()); let mut payment_params = PaymentParameters::from_node_id( - invoice.recover_payee_pub_key(), - invoice.min_final_cltv_expiry_delta() as u32 - ) - .with_route_hints(invoice.route_hints()).unwrap(); + invoice.recover_payee_pub_key(), + invoice.min_final_cltv_expiry_delta() as u32, + ) + .with_route_hints(invoice.route_hints()) + .unwrap(); if let Some(expiry) = invoice.expires_at() { 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)] mod tests { use super::*; - use crate::{InvoiceBuilder, Currency}; + use crate::ln::types::PaymentSecret; + use crate::routing::router::Payee; use bitcoin::hashes::sha256::Hash as Sha256; - use lightning::ln::types::PaymentSecret; - use lightning::routing::router::Payee; - use secp256k1::{SecretKey, PublicKey, Secp256k1}; + use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use core::time::Duration; + use lightning_invoice::{Currency, InvoiceBuilder}; #[cfg(feature = "std")] use std::time::SystemTime; fn duration_since_epoch() -> Duration { #[cfg(feature = "std")] - let duration_since_epoch = - SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); #[cfg(not(feature = "std"))] let duration_since_epoch = Duration::from_secs(1234567); duration_since_epoch @@ -115,9 +118,7 @@ mod tests { .duration_since_epoch(duration_since_epoch()) .min_final_cltv_expiry_delta(144) .amount_milli_satoshis(128) - .build_signed(|hash| { - secp_ctx.sign_ecdsa_recoverable(hash, &private_key) - }) + .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &private_key)) .unwrap(); assert!(payment_parameters_from_zero_amount_invoice(&invoice, 42).is_err()); @@ -147,14 +148,13 @@ mod tests { .payment_secret(PaymentSecret([0; 32])) .duration_since_epoch(duration_since_epoch()) .min_final_cltv_expiry_delta(144) - .build_signed(|hash| { - secp_ctx.sign_ecdsa_recoverable(hash, &private_key) - }) - .unwrap(); + .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &private_key)) + .unwrap(); 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!(onion.payment_secret, Some(PaymentSecret([0; 32]))); assert_eq!(params.final_value_msat, 42); @@ -169,10 +169,10 @@ mod tests { #[test] #[cfg(feature = "std")] fn payment_metadata_end_to_end() { - use lightning::events::Event; - use lightning::ln::channelmanager::{Retry, PaymentId}; - use lightning::ln::msgs::ChannelMessageHandler; - use lightning::ln::functional_test_utils::*; + use crate::events::Event; + use crate::ln::channelmanager::{PaymentId, Retry}; + use crate::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 // the way out through the `PaymentClaimable` event. let chanmon_cfgs = create_chanmon_cfgs(2); @@ -186,6 +186,8 @@ mod tests { let (payment_hash, payment_secret) = 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) .description("test".into()) .payment_hash(Sha256::from_slice(&payment_hash.0).unwrap()) @@ -194,14 +196,14 @@ mod tests { .min_final_cltv_expiry_delta(144) .amount_milli_satoshis(50_000) .payment_metadata(payment_metadata.clone()) - .build_signed(|hash| { - Secp256k1::new().sign_ecdsa_recoverable(hash, - &nodes[1].keys_manager.backing.get_node_secret_key()) - }) + .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &node_secret)) .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); 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]); @@ -215,7 +217,7 @@ mod tests { Event::PaymentClaimable { onion_fields, .. } => { assert_eq!(Some(payment_metadata), onion_fields.unwrap().payment_metadata); }, - _ => panic!("Unexpected event") + _ => panic!("Unexpected event"), } } } diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 4d2572e895f..8c811c920c0 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -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_zero_conf_required(); - if features.is_subset(&legacy_version_bit_set) { - None - } else { + debug_assert!(!legacy_version_bit_set.supports_any_optional_bits()); + debug_assert!(!features.supports_any_optional_bits()); + if features.requires_unknown_bits_from(&legacy_version_bit_set) { Some(()) + } else { + None } } diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index afd4e73578f..7ba94998a0a 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1877,7 +1877,8 @@ impl ChannelContext where SP::Target: SignerProvider { } 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() { (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())); } // 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())); } 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(); - 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 // understand yet, refuse to read it. return Err(DecodeError::UnknownRequiredFeature); diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index 79bf871de72..11e2b12dcdb 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -9,984 +9,37 @@ //! Feature flag definitions for the Lightning protocol according to [BOLT #9]. //! -//! Lightning nodes advertise a supported set of operation through feature flags. Features are -//! applicable for a specific context as indicated in some [messages]. [`Features`] encapsulates -//! behavior for specifying and checking feature flags for a particular context. Each feature is -//! defined internally by a trait specifying the corresponding flags (i.e., even and odd bits). -//! -//! Whether a feature is considered "known" or "unknown" is relative to the implementation, whereas -//! the term "supports" is used in reference to a particular set of [`Features`]. That is, a node -//! supports a feature if it advertises the feature (as either required or optional) to its peers. -//! And the implementation can interpret a feature if the feature is known to it. -//! -//! The following features are currently required in the LDK: -//! - `VariableLengthOnion` - requires/supports variable-length routing onion payloads -//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md) for more information). -//! - `StaticRemoteKey` - requires/supports static key for remote output -//! (see [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more information). -//! -//! The following features are currently supported in the LDK: -//! - `DataLossProtect` - requires/supports that a node which has somehow fallen behind, e.g., has been restored from an old backup, -//! can detect that it has fallen behind -//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). -//! - `InitialRoutingSync` - requires/supports that the sending node needs a complete routing information dump -//! (see [BOLT-7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#initial-sync) for more information). -//! - `UpfrontShutdownScript` - commits to a shutdown scriptpubkey when opening a channel -//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message) for more information). -//! - `GossipQueries` - requires/supports more sophisticated gossip control -//! (see [BOLT-7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md) for more information). -//! - `PaymentSecret` - requires/supports that a node supports payment_secret field -//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md) for more information). -//! - `BasicMPP` - requires/supports that a node can receive basic multi-part payments -//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#basic-multi-part-payments) for more information). -//! - `Wumbo` - requires/supports that a node create large channels. Called `option_support_large_channel` in the spec. -//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message) for more information). -//! - `AnchorsZeroFeeHtlcTx` - requires/supports that commitment transactions include anchor outputs -//! and HTLC transactions are pre-signed with zero fee (see -//! [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more -//! information). -//! - `RouteBlinding` - requires/supports that a node can relay payments over blinded paths -//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#route-blinding) for more information). -//! - `ShutdownAnySegwit` - requires/supports that future segwit versions are allowed in `shutdown` -//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). -//! - `OnionMessages` - requires/supports forwarding onion messages -//! (see [BOLT-7](https://github.com/lightning/bolts/pull/759/files) for more information). -// TODO: update link -//! - `ChannelType` - node supports the channel_type field in open/accept -//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). -//! - `SCIDPrivacy` - supply channel aliases for routing -//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). -//! - `PaymentMetadata` - include additional data in invoices which is passed to recipients in the -//! onion. -//! (see [BOLT-11](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md) for -//! more). -//! - `ZeroConf` - supports accepting HTLCs and using channels prior to funding confirmation -//! (see -//! [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-channel_ready-message) -//! for more info). -//! - `Keysend` - send funds to a node without an invoice -//! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information). -//! - `Trampoline` - supports receiving and forwarding Trampoline payments -//! (see the [`Trampoline` feature proposal](https://github.com/lightning/bolts/pull/836) for more information). -//! -//! LDK knows about the following features, but does not support them: -//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be -//! vulnerable (see this -//! [mailing list post](https://lists.linuxfoundation.org/pipermail/lightning-dev/2020-September/002796.html) -//! for more information). +//! See [`lightning_types::features`] for the list of features currently supported. //! //! [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md -//! [messages]: crate::ln::msgs + +pub use lightning_types::features::Features; +pub use lightning_types::features::{InitFeatures, NodeFeatures, ChannelFeatures}; +pub use lightning_types::features::{Bolt11InvoiceFeatures, OfferFeatures, InvoiceRequestFeatures}; +pub use lightning_types::features::{Bolt12InvoiceFeatures, BlindedHopFeatures}; +pub use lightning_types::features::ChannelTypeFeatures; #[allow(unused_imports)] use crate::prelude::*; use crate::{io, io_extras}; -use core::{cmp, fmt}; -use core::borrow::Borrow; -use core::hash::{Hash, Hasher}; -use core::marker::PhantomData; - -use bech32::{Base32Len, FromBase32, ToBase32, u5, WriteBase32}; use crate::ln::msgs::DecodeError; -use crate::util::ser::{Readable, WithoutLength, Writeable, Writer}; - -mod sealed { - #[allow(unused_imports)] - use crate::prelude::*; - use crate::ln::features::Features; - - /// The context in which [`Features`] are applicable. Defines which features are known to the - /// implementation, though specification of them as required or optional is up to the code - /// constructing a features object. - pub trait Context { - /// Bitmask for selecting features that are known to the implementation. - const KNOWN_FEATURE_MASK: &'static [u8]; - } - - /// Defines a [`Context`] by stating which features it requires and which are optional. Features - /// are specified as a comma-separated list of bytes where each byte is a pipe-delimited list of - /// feature identifiers. - macro_rules! define_context { - ($context: ident, [$( $( $known_feature: ident )|*, )*]) => { - #[derive(Eq, PartialEq)] - pub struct $context {} - - impl Context for $context { - const KNOWN_FEATURE_MASK: &'static [u8] = &[ - $( - 0b00_00_00_00 $(| - ::REQUIRED_MASK | - ::OPTIONAL_MASK)*, - )* - ]; - } - - impl alloc::fmt::Display for Features<$context> { - fn fmt(&self, fmt: &mut alloc::fmt::Formatter) -> Result<(), alloc::fmt::Error> { - $( - $( - fmt.write_fmt(format_args!("{}: {}, ", stringify!($known_feature), - if <$context as $known_feature>::requires_feature(&self.flags) { "required" } - else if <$context as $known_feature>::supports_feature(&self.flags) { "supported" } - else { "not supported" }))?; - )* - {} // Rust gets mad if we only have a $()* block here, so add a dummy {} - )* - fmt.write_fmt(format_args!("unknown flags: {}", - if self.requires_unknown_bits() { "required" } - else if self.supports_unknown_bits() { "supported" } else { "none" })) - } - } - }; - } - - define_context!(InitContext, [ - // Byte 0 - DataLossProtect | InitialRoutingSync | UpfrontShutdownScript | GossipQueries, - // Byte 1 - VariableLengthOnion | StaticRemoteKey | PaymentSecret, - // Byte 2 - BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, - // Byte 3 - RouteBlinding | ShutdownAnySegwit | Taproot, - // Byte 4 - OnionMessages, - // Byte 5 - ChannelType | SCIDPrivacy, - // Byte 6 - ZeroConf, - // Byte 7 - Trampoline, - ]); - define_context!(NodeContext, [ - // Byte 0 - DataLossProtect | UpfrontShutdownScript | GossipQueries, - // Byte 1 - VariableLengthOnion | StaticRemoteKey | PaymentSecret, - // Byte 2 - BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, - // Byte 3 - RouteBlinding | ShutdownAnySegwit | Taproot, - // Byte 4 - OnionMessages, - // Byte 5 - ChannelType | SCIDPrivacy, - // Byte 6 - ZeroConf | Keysend, - // Byte 7 - Trampoline, - ]); - define_context!(ChannelContext, []); - define_context!(Bolt11InvoiceContext, [ - // Byte 0 - , - // Byte 1 - VariableLengthOnion | PaymentSecret, - // Byte 2 - BasicMPP, - // Byte 3 - , - // Byte 4 - , - // Byte 5 - , - // Byte 6 - PaymentMetadata, - // Byte 7 - Trampoline, - ]); - define_context!(OfferContext, []); - define_context!(InvoiceRequestContext, []); - define_context!(Bolt12InvoiceContext, [ - // Byte 0 - , - // Byte 1 - , - // Byte 2 - BasicMPP, - ]); - define_context!(BlindedHopContext, []); - // This isn't a "real" feature context, and is only used in the channel_type field in an - // `OpenChannel` message. - define_context!(ChannelTypeContext, [ - // Byte 0 - , - // Byte 1 - StaticRemoteKey, - // Byte 2 - AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, - // Byte 3 - Taproot, - // Byte 4 - , - // Byte 5 - SCIDPrivacy, - // Byte 6 - ZeroConf, - ]); - - /// Defines a feature with the given bits for the specified [`Context`]s. The generated trait is - /// useful for manipulating feature flags. - macro_rules! define_feature { - ($odd_bit: expr, $feature: ident, [$($context: ty),+], $doc: expr, $optional_setter: ident, - $required_setter: ident, $supported_getter: ident) => { - #[doc = $doc] - /// - /// See [BOLT #9] for details. - /// - /// [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md - pub trait $feature: Context { - /// The bit used to signify that the feature is required. - const EVEN_BIT: usize = $odd_bit - 1; - - /// The bit used to signify that the feature is optional. - const ODD_BIT: usize = $odd_bit; - - /// Assertion that [`EVEN_BIT`] is actually even. - /// - /// [`EVEN_BIT`]: #associatedconstant.EVEN_BIT - const ASSERT_EVEN_BIT_PARITY: usize; - - /// Assertion that [`ODD_BIT`] is actually odd. - /// - /// [`ODD_BIT`]: #associatedconstant.ODD_BIT - const ASSERT_ODD_BIT_PARITY: usize; - - /// Assertion that the bits are set in the context's [`KNOWN_FEATURE_MASK`]. - /// - /// [`KNOWN_FEATURE_MASK`]: Context::KNOWN_FEATURE_MASK - #[cfg(not(test))] // We violate this constraint with `UnknownFeature` - const ASSERT_BITS_IN_MASK: u8; - - /// The byte where the feature is set. - const BYTE_OFFSET: usize = Self::EVEN_BIT / 8; - - /// The bitmask for the feature's required flag relative to the [`BYTE_OFFSET`]. - /// - /// [`BYTE_OFFSET`]: #associatedconstant.BYTE_OFFSET - const REQUIRED_MASK: u8 = 1 << (Self::EVEN_BIT - 8 * Self::BYTE_OFFSET); - - /// The bitmask for the feature's optional flag relative to the [`BYTE_OFFSET`]. - /// - /// [`BYTE_OFFSET`]: #associatedconstant.BYTE_OFFSET - const OPTIONAL_MASK: u8 = 1 << (Self::ODD_BIT - 8 * Self::BYTE_OFFSET); - - /// Returns whether the feature is required by the given flags. - #[inline] - fn requires_feature(flags: &Vec) -> bool { - flags.len() > Self::BYTE_OFFSET && - (flags[Self::BYTE_OFFSET] & Self::REQUIRED_MASK) != 0 - } - - /// Returns whether the feature is supported by the given flags. - #[inline] - fn supports_feature(flags: &Vec) -> bool { - flags.len() > Self::BYTE_OFFSET && - (flags[Self::BYTE_OFFSET] & (Self::REQUIRED_MASK | Self::OPTIONAL_MASK)) != 0 - } - - /// Sets the feature's required (even) bit in the given flags. - #[inline] - fn set_required_bit(flags: &mut Vec) { - if flags.len() <= Self::BYTE_OFFSET { - flags.resize(Self::BYTE_OFFSET + 1, 0u8); - } - - flags[Self::BYTE_OFFSET] |= Self::REQUIRED_MASK; - flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK; - } - - /// Sets the feature's optional (odd) bit in the given flags. - #[inline] - fn set_optional_bit(flags: &mut Vec) { - if flags.len() <= Self::BYTE_OFFSET { - flags.resize(Self::BYTE_OFFSET + 1, 0u8); - } - - flags[Self::BYTE_OFFSET] |= Self::OPTIONAL_MASK; - } - - /// Clears the feature's required (even) and optional (odd) bits from the given - /// flags. - #[inline] - fn clear_bits(flags: &mut Vec) { - if flags.len() > Self::BYTE_OFFSET { - flags[Self::BYTE_OFFSET] &= !Self::REQUIRED_MASK; - flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK; - } - - let last_non_zero_byte = flags.iter().rposition(|&byte| byte != 0); - let size = if let Some(offset) = last_non_zero_byte { offset + 1 } else { 0 }; - flags.resize(size, 0u8); - } - } - - impl Features { - /// Set this feature as optional. - pub fn $optional_setter(&mut self) { - ::set_optional_bit(&mut self.flags); - } - - /// Set this feature as required. - pub fn $required_setter(&mut self) { - ::set_required_bit(&mut self.flags); - } - - /// Checks if this feature is supported. - pub fn $supported_getter(&self) -> bool { - ::supports_feature(&self.flags) - } - } - - $( - impl $feature for $context { - // EVEN_BIT % 2 == 0 - const ASSERT_EVEN_BIT_PARITY: usize = 0 - (::EVEN_BIT % 2); - - // ODD_BIT % 2 == 1 - const ASSERT_ODD_BIT_PARITY: usize = (::ODD_BIT % 2) - 1; - - // (byte & (REQUIRED_MASK | OPTIONAL_MASK)) >> (EVEN_BIT % 8) == 3 - #[cfg(not(test))] // We violate this constraint with `UnknownFeature` - const ASSERT_BITS_IN_MASK: u8 = - ((<$context>::KNOWN_FEATURE_MASK[::BYTE_OFFSET] & (::REQUIRED_MASK | ::OPTIONAL_MASK)) - >> (::EVEN_BIT % 8)) - 3; - } - )* - }; - ($odd_bit: expr, $feature: ident, [$($context: ty),+], $doc: expr, $optional_setter: ident, - $required_setter: ident, $supported_getter: ident, $required_getter: ident) => { - define_feature!($odd_bit, $feature, [$($context),+], $doc, $optional_setter, $required_setter, $supported_getter); - impl Features { - /// Checks if this feature is required. - pub fn $required_getter(&self) -> bool { - ::requires_feature(&self.flags) - } - } - } - } - - define_feature!(1, DataLossProtect, [InitContext, NodeContext], - "Feature flags for `option_data_loss_protect`.", set_data_loss_protect_optional, - set_data_loss_protect_required, supports_data_loss_protect, requires_data_loss_protect); - // NOTE: Per Bolt #9, initial_routing_sync has no even bit. - define_feature!(3, InitialRoutingSync, [InitContext], "Feature flags for `initial_routing_sync`.", - set_initial_routing_sync_optional, set_initial_routing_sync_required, - initial_routing_sync); - define_feature!(5, UpfrontShutdownScript, [InitContext, NodeContext], - "Feature flags for `option_upfront_shutdown_script`.", set_upfront_shutdown_script_optional, - set_upfront_shutdown_script_required, supports_upfront_shutdown_script, - requires_upfront_shutdown_script); - define_feature!(7, GossipQueries, [InitContext, NodeContext], - "Feature flags for `gossip_queries`.", set_gossip_queries_optional, set_gossip_queries_required, - supports_gossip_queries, requires_gossip_queries); - define_feature!(9, VariableLengthOnion, [InitContext, NodeContext, Bolt11InvoiceContext], - "Feature flags for `var_onion_optin`.", set_variable_length_onion_optional, - set_variable_length_onion_required, supports_variable_length_onion, - requires_variable_length_onion); - define_feature!(13, StaticRemoteKey, [InitContext, NodeContext, ChannelTypeContext], - "Feature flags for `option_static_remotekey`.", set_static_remote_key_optional, - set_static_remote_key_required, supports_static_remote_key, requires_static_remote_key); - define_feature!(15, PaymentSecret, [InitContext, NodeContext, Bolt11InvoiceContext], - "Feature flags for `payment_secret`.", set_payment_secret_optional, set_payment_secret_required, - supports_payment_secret, requires_payment_secret); - define_feature!(17, BasicMPP, [InitContext, NodeContext, Bolt11InvoiceContext, Bolt12InvoiceContext], - "Feature flags for `basic_mpp`.", set_basic_mpp_optional, set_basic_mpp_required, - supports_basic_mpp, requires_basic_mpp); - define_feature!(19, Wumbo, [InitContext, NodeContext], - "Feature flags for `option_support_large_channel` (aka wumbo channels).", set_wumbo_optional, set_wumbo_required, - supports_wumbo, requires_wumbo); - define_feature!(21, AnchorsNonzeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext], - "Feature flags for `option_anchors_nonzero_fee_htlc_tx`.", set_anchors_nonzero_fee_htlc_tx_optional, - set_anchors_nonzero_fee_htlc_tx_required, supports_anchors_nonzero_fee_htlc_tx, requires_anchors_nonzero_fee_htlc_tx); - define_feature!(23, AnchorsZeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext], - "Feature flags for `option_anchors_zero_fee_htlc_tx`.", set_anchors_zero_fee_htlc_tx_optional, - set_anchors_zero_fee_htlc_tx_required, supports_anchors_zero_fee_htlc_tx, requires_anchors_zero_fee_htlc_tx); - define_feature!(25, RouteBlinding, [InitContext, NodeContext], - "Feature flags for `option_route_blinding`.", set_route_blinding_optional, - set_route_blinding_required, supports_route_blinding, requires_route_blinding); - define_feature!(27, ShutdownAnySegwit, [InitContext, NodeContext], - "Feature flags for `opt_shutdown_anysegwit`.", set_shutdown_any_segwit_optional, - set_shutdown_any_segwit_required, supports_shutdown_anysegwit, requires_shutdown_anysegwit); - define_feature!(31, Taproot, [InitContext, NodeContext, ChannelTypeContext], - "Feature flags for `option_taproot`.", set_taproot_optional, - set_taproot_required, supports_taproot, requires_taproot); - define_feature!(39, OnionMessages, [InitContext, NodeContext], - "Feature flags for `option_onion_messages`.", set_onion_messages_optional, - set_onion_messages_required, supports_onion_messages, requires_onion_messages); - define_feature!(45, ChannelType, [InitContext, NodeContext], - "Feature flags for `option_channel_type`.", set_channel_type_optional, - set_channel_type_required, supports_channel_type, requires_channel_type); - define_feature!(47, SCIDPrivacy, [InitContext, NodeContext, ChannelTypeContext], - "Feature flags for only forwarding with SCID aliasing. Called `option_scid_alias` in the BOLTs", - set_scid_privacy_optional, set_scid_privacy_required, supports_scid_privacy, requires_scid_privacy); - define_feature!(49, PaymentMetadata, [Bolt11InvoiceContext], - "Feature flags for payment metadata in invoices.", set_payment_metadata_optional, - set_payment_metadata_required, supports_payment_metadata, requires_payment_metadata); - define_feature!(51, ZeroConf, [InitContext, NodeContext, ChannelTypeContext], - "Feature flags for accepting channels with zero confirmations. Called `option_zeroconf` in the BOLTs", - set_zero_conf_optional, set_zero_conf_required, supports_zero_conf, requires_zero_conf); - define_feature!(55, Keysend, [NodeContext], - "Feature flags for keysend payments.", set_keysend_optional, set_keysend_required, - supports_keysend, requires_keysend); - define_feature!(57, Trampoline, [InitContext, NodeContext, Bolt11InvoiceContext], - "Feature flags for Trampoline routing.", set_trampoline_routing_optional, set_trampoline_routing_required, - supports_trampoline_routing, requires_trampoline_routing); - // Note: update the module-level docs when a new feature bit is added! - - #[cfg(test)] - define_feature!(123456789, UnknownFeature, - [NodeContext, ChannelContext, Bolt11InvoiceContext, OfferContext, InvoiceRequestContext, Bolt12InvoiceContext, BlindedHopContext], - "Feature flags for an unknown feature used in testing.", set_unknown_feature_optional, - set_unknown_feature_required, supports_unknown_test_feature, requires_unknown_test_feature); -} - -const ANY_REQUIRED_FEATURES_MASK: u8 = 0b01_01_01_01; -const ANY_OPTIONAL_FEATURES_MASK: u8 = 0b10_10_10_10; - -/// Tracks the set of features which a node implements, templated by the context in which it -/// appears. -/// -/// This is not exported to bindings users as we map the concrete feature types below directly instead -#[derive(Eq)] -pub struct Features { - /// Note that, for convenience, flags is LITTLE endian (despite being big-endian on the wire) - flags: Vec, - mark: PhantomData, -} - -impl> core::ops::BitOrAssign for Features { - fn bitor_assign(&mut self, rhs: Rhs) { - let total_feature_len = cmp::max(self.flags.len(), rhs.borrow().flags.len()); - self.flags.resize(total_feature_len, 0u8); - for (byte, rhs_byte) in self.flags.iter_mut().zip(rhs.borrow().flags.iter()) { - *byte |= *rhs_byte; - } - } -} - -impl core::ops::BitOr for Features { - type Output = Self; - - fn bitor(mut self, o: Self) -> Self { - self |= o; - self - } -} - -impl Clone for Features { - fn clone(&self) -> Self { - Self { - flags: self.flags.clone(), - mark: PhantomData, - } - } -} -impl Hash for Features { - fn hash(&self, hasher: &mut H) { - let mut nonzero_flags = &self.flags[..]; - while nonzero_flags.last() == Some(&0) { - nonzero_flags = &nonzero_flags[..nonzero_flags.len() - 1]; - } - nonzero_flags.hash(hasher); - } -} -impl PartialEq for Features { - fn eq(&self, o: &Self) -> bool { - let mut o_iter = o.flags.iter(); - let mut self_iter = self.flags.iter(); - loop { - match (o_iter.next(), self_iter.next()) { - (Some(o), Some(us)) => if o != us { return false }, - (Some(b), None) | (None, Some(b)) => if *b != 0 { return false }, - (None, None) => return true, - } - } - } -} -impl PartialOrd for Features { - fn partial_cmp(&self, other: &Self) -> Option { - self.flags.partial_cmp(&other.flags) - } -} -impl Ord for Features { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.flags.cmp(&other.flags) - } -} -impl fmt::Debug for Features { - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.flags.fmt(fmt) - } -} - -/// Features used within an `init` message. -pub type InitFeatures = Features; -/// Features used within a `node_announcement` message. -pub type NodeFeatures = Features; -/// Features used within a `channel_announcement` message. -pub type ChannelFeatures = Features; -/// Features used within an invoice. -pub type Bolt11InvoiceFeatures = Features; -/// Features used within an `offer`. -pub type OfferFeatures = Features; -/// Features used within an `invoice_request`. -pub type InvoiceRequestFeatures = Features; -/// Features used within an `invoice`. -pub type Bolt12InvoiceFeatures = Features; -/// Features used within BOLT 4 encrypted_data_tlv and BOLT 12 blinded_payinfo -pub type BlindedHopFeatures = Features; - -/// Features used within the channel_type field in an OpenChannel message. -/// -/// A channel is always of some known "type", describing the transaction formats used and the exact -/// semantics of our interaction with our peer. -/// -/// Note that because a channel is a specific type which is proposed by the opener and accepted by -/// the counterparty, only required features are allowed here. -/// -/// This is serialized differently from other feature types - it is not prefixed by a length, and -/// thus must only appear inside a TLV where its length is known in advance. -pub type ChannelTypeFeatures = Features; - -impl InitFeatures { - /// Writes all features present up to, and including, 13. - pub(crate) fn write_up_to_13(&self, w: &mut W) -> Result<(), io::Error> { - let len = cmp::min(2, self.flags.len()); - (len as u16).write(w)?; - for i in (0..len).rev() { - if i == 0 { - self.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: - (self.flags[i] & 0b00_11_11_11).write(w)?; - } - } - Ok(()) - } - - /// Converts `InitFeatures` to `Features`. Only known `InitFeatures` relevant to context `C` - /// are included in the result. - pub(crate) fn to_context(&self) -> Features { - self.to_context_internal() - } -} - -impl Bolt11InvoiceFeatures { - /// Converts `Bolt11InvoiceFeatures` to `Features`. Only known `Bolt11InvoiceFeatures` relevant to - /// context `C` are included in the result. - pub(crate) fn to_context(&self) -> Features { - self.to_context_internal() - } - - /// Getting a route for a keysend payment to a private node requires providing the payee's - /// features (since they were not announced in a node announcement). However, keysend payments - /// don't have an invoice to pull the payee's features from, so this method is provided for use in - /// [`PaymentParameters::for_keysend`], thus omitting the need for payers to manually construct an - /// `Bolt11InvoiceFeatures` for [`find_route`]. - /// - /// MPP keysend is not widely supported yet, so we parameterize support to allow the user to - /// choose whether their router should find multi-part routes. - /// - /// [`PaymentParameters::for_keysend`]: crate::routing::router::PaymentParameters::for_keysend - /// [`find_route`]: crate::routing::router::find_route - pub(crate) fn for_keysend(allow_mpp: bool) -> Bolt11InvoiceFeatures { - let mut res = Bolt11InvoiceFeatures::empty(); - res.set_variable_length_onion_optional(); - if allow_mpp { - res.set_basic_mpp_optional(); - } - res - } -} - -impl Bolt12InvoiceFeatures { - /// Converts [`Bolt12InvoiceFeatures`] to [`Features`]. Only known [`Bolt12InvoiceFeatures`] - /// relevant to context `C` are included in the result. - pub(crate) fn to_context(&self) -> Features { - self.to_context_internal() - } -} - -impl ChannelTypeFeatures { - // Maps the relevant `InitFeatures` to `ChannelTypeFeatures`. Any unknown features to - // `ChannelTypeFeatures` are not included in the result. - pub(crate) fn from_init(init: &InitFeatures) -> Self { - let mut ret = init.to_context_internal(); - // ChannelTypeFeatures must only contain required bits, so we OR the required forms of all - // optional bits and then AND out the optional ones. - for byte in ret.flags.iter_mut() { - *byte |= (*byte & ANY_OPTIONAL_FEATURES_MASK) >> 1; - *byte &= ANY_REQUIRED_FEATURES_MASK; - } - ret - } - - /// Constructs a ChannelTypeFeatures with only static_remotekey set - pub(crate) fn only_static_remote_key() -> Self { - let mut ret = Self::empty(); - ::set_required_bit(&mut ret.flags); - ret - } - - /// Constructs a ChannelTypeFeatures with anchors support - pub(crate) fn anchors_zero_htlc_fee_and_dependencies() -> Self { - let mut ret = Self::empty(); - ::set_required_bit(&mut ret.flags); - ::set_required_bit(&mut ret.flags); - ret - } -} - -impl ToBase32 for Bolt11InvoiceFeatures { - fn write_base32(&self, writer: &mut W) -> Result<(), ::Err> { - // Explanation for the "4": the normal way to round up when dividing is to add the divisor - // minus one before dividing - let length_u5s = (self.flags.len() * 8 + 4) / 5 as usize; - let mut res_u5s: Vec = vec![u5::try_from_u8(0).unwrap(); length_u5s]; - for (byte_idx, byte) in self.flags.iter().enumerate() { - let bit_pos_from_left_0_indexed = byte_idx * 8; - let new_u5_idx = length_u5s - (bit_pos_from_left_0_indexed / 5) as usize - 1; - let new_bit_pos = bit_pos_from_left_0_indexed % 5; - let shifted_chunk_u16 = (*byte as u16) << new_bit_pos; - let curr_u5_as_u8 = res_u5s[new_u5_idx].to_u8(); - res_u5s[new_u5_idx] = u5::try_from_u8(curr_u5_as_u8 | ((shifted_chunk_u16 & 0x001f) as u8)).unwrap(); - if new_u5_idx > 0 { - let curr_u5_as_u8 = res_u5s[new_u5_idx - 1].to_u8(); - res_u5s[new_u5_idx - 1] = u5::try_from_u8(curr_u5_as_u8 | (((shifted_chunk_u16 >> 5) & 0x001f) as u8)).unwrap(); - } - if new_u5_idx > 1 { - let curr_u5_as_u8 = res_u5s[new_u5_idx - 2].to_u8(); - res_u5s[new_u5_idx - 2] = u5::try_from_u8(curr_u5_as_u8 | (((shifted_chunk_u16 >> 10) & 0x001f) as u8)).unwrap(); - } - } - // Trim the highest feature bits. - while !res_u5s.is_empty() && res_u5s[0] == u5::try_from_u8(0).unwrap() { - res_u5s.remove(0); - } - writer.write(&res_u5s) - } -} - -impl Base32Len for Bolt11InvoiceFeatures { - fn base32_len(&self) -> usize { - self.to_base32().len() - } -} - -impl FromBase32 for Bolt11InvoiceFeatures { - type Err = bech32::Error; - - fn from_base32(field_data: &[u5]) -> Result { - // Explanation for the "7": the normal way to round up when dividing is to add the divisor - // minus one before dividing - let length_bytes = (field_data.len() * 5 + 7) / 8 as usize; - let mut res_bytes: Vec = vec![0; length_bytes]; - for (u5_idx, chunk) in field_data.iter().enumerate() { - let bit_pos_from_right_0_indexed = (field_data.len() - u5_idx - 1) * 5; - let new_byte_idx = (bit_pos_from_right_0_indexed / 8) as usize; - let new_bit_pos = bit_pos_from_right_0_indexed % 8; - let chunk_u16 = chunk.to_u8() as u16; - res_bytes[new_byte_idx] |= ((chunk_u16 << new_bit_pos) & 0xff) as u8; - if new_byte_idx != length_bytes - 1 { - res_bytes[new_byte_idx + 1] |= ((chunk_u16 >> (8-new_bit_pos)) & 0xff) as u8; - } - } - // Trim the highest feature bits. - while !res_bytes.is_empty() && res_bytes[res_bytes.len() - 1] == 0 { - res_bytes.pop(); - } - Ok(Bolt11InvoiceFeatures::from_le_bytes(res_bytes)) - } -} - -impl Features { - /// Create a blank Features with no features set - pub fn empty() -> Self { - Features { - flags: Vec::new(), - mark: PhantomData, - } - } - - /// Converts `Features` to `Features`. Only known `T` features relevant to context `C` are - /// included in the result. - fn to_context_internal(&self) -> Features { - let from_byte_count = T::KNOWN_FEATURE_MASK.len(); - let to_byte_count = C::KNOWN_FEATURE_MASK.len(); - let mut flags = Vec::new(); - for (i, byte) in self.flags.iter().enumerate() { - if i < from_byte_count && i < to_byte_count { - let from_known_features = T::KNOWN_FEATURE_MASK[i]; - let to_known_features = C::KNOWN_FEATURE_MASK[i]; - flags.push(byte & from_known_features & to_known_features); - } - } - Features:: { flags, mark: PhantomData, } - } - - /// Create a Features given a set of flags, in little-endian. This is in reverse byte order from - /// most on-the-wire encodings. - /// - /// This is not exported to bindings users as we don't support export across multiple T - pub fn from_le_bytes(flags: Vec) -> Features { - Features { - flags, - mark: PhantomData, - } - } - - #[cfg(test)] - /// Gets the underlying flags set, in LE. - pub fn le_flags(&self) -> &Vec { - &self.flags - } - - fn write_be(&self, w: &mut W) -> Result<(), io::Error> { - for f in self.flags.iter().rev() { // Swap back to big-endian - f.write(w)?; - } - Ok(()) - } - - /// Create a [`Features`] given a set of flags, in big-endian. This is in byte order from - /// most on-the-wire encodings. - /// - /// This is not exported to bindings users as we don't support export across multiple T - pub fn from_be_bytes(mut flags: Vec) -> Features { - flags.reverse(); // Swap to little-endian - Self { - flags, - mark: PhantomData, - } - } - - pub(crate) fn supports_any_optional_bits(&self) -> bool { - self.flags.iter().any(|&byte| (byte & ANY_OPTIONAL_FEATURES_MASK) != 0) - } - - /// Returns true if this `Features` object contains required features unknown by `other`. - pub fn requires_unknown_bits_from(&self, other: &Self) -> bool { - // Bitwise AND-ing with all even bits set except for known features will select required - // unknown features. - self.flags.iter().enumerate().any(|(i, &byte)| { - let unknown_features = unset_features_mask_at_position(other, i); - (byte & (ANY_REQUIRED_FEATURES_MASK & unknown_features)) != 0 - }) - } - - pub(crate) fn required_unknown_bits_from(&self, other: &Self) -> Vec { - let mut unknown_bits = Vec::new(); - - // Bitwise AND-ing with all even bits set except for known features will select required - // unknown features. - self.flags.iter().enumerate().for_each(|(i, &byte)| { - let unknown_features = unset_features_mask_at_position(other, i); - if byte & unknown_features != 0 { - for bit in (0..8).step_by(2) { - if ((byte & unknown_features) >> bit) & 1 == 1 { - unknown_bits.push(i * 8 + bit); - } - } - } - }); - - unknown_bits - } - - /// Returns true if this `Features` object contains unknown feature flags which are set as - /// "required". - pub fn requires_unknown_bits(&self) -> bool { - // Bitwise AND-ing with all even bits set except for known features will select required - // unknown features. - let mut known_chunks = T::KNOWN_FEATURE_MASK.chunks(8); - for chunk in self.flags.chunks(8) { - let mut flag_bytes = [0; 8]; - flag_bytes[..chunk.len()].copy_from_slice(&chunk); - let flag_int = u64::from_le_bytes(flag_bytes); - - let known_chunk = known_chunks.next().unwrap_or(&[0; 0]); - let mut known_bytes = [0; 8]; - known_bytes[..known_chunk.len()].copy_from_slice(&known_chunk); - let known_int = u64::from_le_bytes(known_bytes); - - const REQ_MASK: u64 = u64::from_le_bytes([ANY_REQUIRED_FEATURES_MASK; 8]); - if flag_int & (REQ_MASK & !known_int) != 0 { - return true; - } - } - false - } - - pub(crate) fn supports_unknown_bits(&self) -> bool { - // Bitwise AND-ing with all even and odd bits set except for known features will select - // both required and optional unknown features. - let byte_count = T::KNOWN_FEATURE_MASK.len(); - self.flags.iter().enumerate().any(|(i, &byte)| { - let unknown_features = if i < byte_count { - !T::KNOWN_FEATURE_MASK[i] - } else { - 0b11_11_11_11 - }; - (byte & unknown_features) != 0 - }) - } - - // Returns true if the features within `self` are a subset of the features within `other`. - pub(crate) fn is_subset(&self, other: &Self) -> bool { - for (idx, byte) in self.flags.iter().enumerate() { - if let Some(other_byte) = other.flags.get(idx) { - if byte & other_byte != *byte { - // `self` has bits set that `other` doesn't. - return false; - } - } else { - if *byte > 0 { - // `self` has a non-zero byte that `other` doesn't. - return false; - } - } - } - true - } - - /// Sets a required feature bit. Errors if `bit` is outside the feature range as defined - /// by [BOLT 9]. - /// - /// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will - /// be set instead (i.e., `bit - 1`). - /// - /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md - pub fn set_required_feature_bit(&mut self, bit: usize) -> Result<(), ()> { - self.set_feature_bit(bit - (bit % 2)) - } - - /// Sets an optional feature bit. Errors if `bit` is outside the feature range as defined - /// by [BOLT 9]. - /// - /// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be - /// set instead (i.e., `bit + 1`). - /// - /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md - pub fn set_optional_feature_bit(&mut self, bit: usize) -> Result<(), ()> { - self.set_feature_bit(bit + (1 - (bit % 2))) - } - - fn set_feature_bit(&mut self, bit: usize) -> Result<(), ()> { - if bit > 255 { - return Err(()); - } - self.set_bit(bit, false) - } - - /// Sets a required custom feature bit. Errors if `bit` is outside the custom range as defined - /// by [bLIP 2] or if it is a known `T` feature. - /// - /// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will - /// be set instead (i.e., `bit - 1`). - /// - /// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits - pub fn set_required_custom_bit(&mut self, bit: usize) -> Result<(), ()> { - self.set_custom_bit(bit - (bit % 2)) - } +use crate::util::ser::{Writer, Readable, Writeable, WithoutLength}; - /// Sets an optional custom feature bit. Errors if `bit` is outside the custom range as defined - /// by [bLIP 2] or if it is a known `T` feature. - /// - /// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be - /// set instead (i.e., `bit + 1`). - /// - /// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits - pub fn set_optional_custom_bit(&mut self, bit: usize) -> Result<(), ()> { - self.set_custom_bit(bit + (1 - (bit % 2))) - } - - fn set_custom_bit(&mut self, bit: usize) -> Result<(), ()> { - if bit < 256 { - return Err(()); - } - self.set_bit(bit, true) - } - - fn set_bit(&mut self, bit: usize, custom: bool) -> Result<(), ()> { - let byte_offset = bit / 8; - let mask = 1 << (bit - 8 * byte_offset); - if byte_offset < T::KNOWN_FEATURE_MASK.len() && custom { - if (T::KNOWN_FEATURE_MASK[byte_offset] & mask) != 0 { - return Err(()); - } - } - - if self.flags.len() <= byte_offset { - self.flags.resize(byte_offset + 1, 0u8); - } - - self.flags[byte_offset] |= mask; - - Ok(()) - } -} - -impl Features { - #[cfg(test)] - pub(crate) fn clear_upfront_shutdown_script(mut self) -> Self { - ::clear_bits(&mut self.flags); - self - } -} - -impl Features { - #[cfg(test)] - pub(crate) fn clear_shutdown_anysegwit(mut self) -> Self { - ::clear_bits(&mut self.flags); - self - } -} - -impl Features { - #[cfg(test)] - pub(crate) fn clear_wumbo(mut self) -> Self { - ::clear_bits(&mut self.flags); - self - } -} - -impl Features { - pub(crate) fn clear_scid_privacy(&mut self) { - ::clear_bits(&mut self.flags); - } -} - -impl Features { - pub(crate) fn clear_anchors_zero_fee_htlc_tx(&mut self) { - ::clear_bits(&mut self.flags); - } -} - -impl Features { - #[cfg(test)] - pub(crate) fn clear_route_blinding(&mut self) { - ::clear_bits(&mut self.flags); - } -} - -#[cfg(test)] -impl Features { - pub(crate) fn unknown() -> Self { - let mut features = Self::empty(); - features.set_unknown_feature_required(); - features +fn write_be(w: &mut W, le_flags: &[u8]) -> Result<(), io::Error> { + for f in le_flags.iter().rev() { // Swap back to big-endian + f.write(w)?; } + Ok(()) } macro_rules! impl_feature_len_prefixed_write { ($features: ident) => { impl Writeable for $features { fn write(&self, w: &mut W) -> Result<(), io::Error> { - (self.flags.len() as u16).write(w)?; - self.write_be(w) + let bytes = self.le_flags(); + (bytes.len() as u16).write(w)?; + write_be(w, bytes) } } impl Readable for $features { @@ -1024,221 +77,37 @@ impl_feature_tlv_write!(ChannelTypeFeatures); // Some features may appear both in a TLV record and as part of a TLV subtype sequence. The latter // requires a length but the former does not. -impl Writeable for WithoutLength<&Features> { - fn write(&self, w: &mut W) -> Result<(), io::Error> { - self.0.write_be(w) - } -} +macro_rules! impl_feature_write_without_length { + ($features: ident) => { + impl Writeable for WithoutLength<&$features> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + write_be(w, self.0.le_flags()) + } + } -impl Readable for WithoutLength> { - fn read(r: &mut R) -> Result { - let v = io_extras::read_to_end(r)?; - Ok(WithoutLength(Features::::from_be_bytes(v))) + impl Readable for WithoutLength<$features> { + fn read(r: &mut R) -> Result { + let v = io_extras::read_to_end(r)?; + Ok(WithoutLength($features::from_be_bytes(v))) + } + } } } -pub(crate) fn unset_features_mask_at_position(other: &Features, index: usize) -> u8 { - if index < other.flags.len() { - // Form a mask similar to !T::KNOWN_FEATURE_MASK only for `other` - !(other.flags[index] - | ((other.flags[index] >> 1) & ANY_REQUIRED_FEATURES_MASK) - | ((other.flags[index] << 1) & ANY_OPTIONAL_FEATURES_MASK)) - } else { - 0b11_11_11_11 - } -} +impl_feature_write_without_length!(Bolt12InvoiceFeatures); +impl_feature_write_without_length!(ChannelTypeFeatures); +impl_feature_write_without_length!(InvoiceRequestFeatures); +impl_feature_write_without_length!(OfferFeatures); #[cfg(test)] mod tests { - use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, Bolt11InvoiceFeatures, NodeFeatures, OfferFeatures, sealed}; - use bech32::{Base32Len, FromBase32, ToBase32, u5}; + use super::*; use crate::util::ser::{Readable, WithoutLength, Writeable}; - #[test] - fn sanity_test_unknown_bits() { - let features = ChannelFeatures::empty(); - assert!(!features.requires_unknown_bits()); - assert!(!features.supports_unknown_bits()); - - let mut features = ChannelFeatures::empty(); - features.set_unknown_feature_required(); - assert!(features.requires_unknown_bits()); - assert!(features.supports_unknown_bits()); - assert_eq!(features.required_unknown_bits_from(&ChannelFeatures::empty()), vec![123456788]); - - let mut features = ChannelFeatures::empty(); - features.set_unknown_feature_optional(); - assert!(!features.requires_unknown_bits()); - assert!(features.supports_unknown_bits()); - assert_eq!(features.required_unknown_bits_from(&ChannelFeatures::empty()), vec![]); - - let mut features = ChannelFeatures::empty(); - features.set_unknown_feature_required(); - features.set_custom_bit(123456786).unwrap(); - assert!(features.requires_unknown_bits()); - assert!(features.supports_unknown_bits()); - assert_eq!(features.required_unknown_bits_from(&ChannelFeatures::empty()), vec![123456786, 123456788]); - - let mut limiter = ChannelFeatures::empty(); - limiter.set_unknown_feature_optional(); - assert_eq!(features.required_unknown_bits_from(&limiter), vec![123456786]); - } - - #[test] - fn requires_unknown_bits_from() { - let mut features1 = InitFeatures::empty(); - let mut features2 = InitFeatures::empty(); - assert!(!features1.requires_unknown_bits_from(&features2)); - assert!(!features2.requires_unknown_bits_from(&features1)); - - features1.set_data_loss_protect_required(); - assert!(features1.requires_unknown_bits_from(&features2)); - assert!(!features2.requires_unknown_bits_from(&features1)); - - features2.set_data_loss_protect_optional(); - assert!(!features1.requires_unknown_bits_from(&features2)); - assert!(!features2.requires_unknown_bits_from(&features1)); - - features2.set_gossip_queries_required(); - assert!(!features1.requires_unknown_bits_from(&features2)); - assert!(features2.requires_unknown_bits_from(&features1)); - - features1.set_gossip_queries_optional(); - assert!(!features1.requires_unknown_bits_from(&features2)); - assert!(!features2.requires_unknown_bits_from(&features1)); - - features1.set_variable_length_onion_required(); - assert!(features1.requires_unknown_bits_from(&features2)); - assert!(!features2.requires_unknown_bits_from(&features1)); - - features2.set_variable_length_onion_optional(); - assert!(!features1.requires_unknown_bits_from(&features2)); - assert!(!features2.requires_unknown_bits_from(&features1)); - - features1.set_basic_mpp_required(); - features2.set_wumbo_required(); - assert!(features1.requires_unknown_bits_from(&features2)); - assert!(features2.requires_unknown_bits_from(&features1)); - } - - #[test] - fn convert_to_context_with_relevant_flags() { - let mut init_features = InitFeatures::empty(); - // Set a bunch of features we use, plus initial_routing_sync_required (which shouldn't get - // converted as it's only relevant in an init context). - init_features.set_initial_routing_sync_required(); - init_features.set_data_loss_protect_required(); - init_features.set_variable_length_onion_required(); - init_features.set_static_remote_key_required(); - init_features.set_payment_secret_required(); - init_features.set_basic_mpp_optional(); - init_features.set_wumbo_optional(); - init_features.set_anchors_zero_fee_htlc_tx_optional(); - init_features.set_route_blinding_optional(); - init_features.set_shutdown_any_segwit_optional(); - init_features.set_onion_messages_optional(); - init_features.set_channel_type_optional(); - init_features.set_scid_privacy_optional(); - init_features.set_zero_conf_optional(); - - assert!(init_features.initial_routing_sync()); - assert!(!init_features.supports_upfront_shutdown_script()); - assert!(!init_features.supports_gossip_queries()); - - let node_features: NodeFeatures = init_features.to_context(); - { - // Check that the flags are as expected: - // - option_data_loss_protect (req) - // - var_onion_optin (req) | static_remote_key (req) | payment_secret(req) - // - basic_mpp | wumbo | option_anchors_zero_fee_htlc_tx - // - option_route_blinding | opt_shutdown_anysegwit - // - onion_messages - // - option_channel_type | option_scid_alias - // - option_zeroconf - assert_eq!(node_features.flags.len(), 7); - assert_eq!(node_features.flags[0], 0b00000001); - assert_eq!(node_features.flags[1], 0b01010001); - assert_eq!(node_features.flags[2], 0b10001010); - assert_eq!(node_features.flags[3], 0b00001010); - assert_eq!(node_features.flags[4], 0b10000000); - assert_eq!(node_features.flags[5], 0b10100000); - assert_eq!(node_features.flags[6], 0b00001000); - } - - // Check that cleared flags are kept blank when converting back: - // - initial_routing_sync was not applicable to NodeContext - // - upfront_shutdown_script was cleared before converting - // - gossip_queries was cleared before converting - let features: InitFeatures = node_features.to_context_internal(); - assert!(!features.initial_routing_sync()); - assert!(!features.supports_upfront_shutdown_script()); - assert!(!init_features.supports_gossip_queries()); - } - - #[test] - fn convert_to_context_with_unknown_flags() { - // Ensure the `from` context has fewer known feature bytes than the `to` context. - assert!(::KNOWN_FEATURE_MASK.len() < - ::KNOWN_FEATURE_MASK.len()); - let mut channel_features = ChannelFeatures::empty(); - channel_features.set_unknown_feature_optional(); - assert!(channel_features.supports_unknown_bits()); - let invoice_features: Bolt11InvoiceFeatures = channel_features.to_context_internal(); - assert!(!invoice_features.supports_unknown_bits()); - } - - #[test] - fn set_feature_bits() { - let mut features = Bolt11InvoiceFeatures::empty(); - features.set_basic_mpp_optional(); - features.set_payment_secret_required(); - assert!(features.supports_basic_mpp()); - assert!(!features.requires_basic_mpp()); - assert!(features.requires_payment_secret()); - assert!(features.supports_payment_secret()); - - // Set flags manually - let mut features = NodeFeatures::empty(); - assert!(features.set_optional_feature_bit(55).is_ok()); - assert!(features.supports_keysend()); - assert!(features.set_optional_feature_bit(255).is_ok()); - assert!(features.set_required_feature_bit(256).is_err()); - } - - #[test] - fn set_custom_bits() { - let mut features = Bolt11InvoiceFeatures::empty(); - features.set_variable_length_onion_optional(); - assert_eq!(features.flags[1], 0b00000010); - - assert!(features.set_optional_custom_bit(255).is_err()); - assert!(features.set_required_custom_bit(256).is_ok()); - assert!(features.set_required_custom_bit(258).is_ok()); - assert_eq!(features.flags[31], 0b00000000); - assert_eq!(features.flags[32], 0b00000101); - - let known_bit = ::EVEN_BIT; - let byte_offset = ::BYTE_OFFSET; - assert_eq!(byte_offset, 1); - assert_eq!(features.flags[byte_offset], 0b00000010); - assert!(features.set_required_custom_bit(known_bit).is_err()); - assert_eq!(features.flags[byte_offset], 0b00000010); - - let mut features = Bolt11InvoiceFeatures::empty(); - assert!(features.set_optional_custom_bit(256).is_ok()); - assert!(features.set_optional_custom_bit(259).is_ok()); - assert_eq!(features.flags[32], 0b00001010); - - let mut features = Bolt11InvoiceFeatures::empty(); - assert!(features.set_required_custom_bit(257).is_ok()); - assert!(features.set_required_custom_bit(258).is_ok()); - assert_eq!(features.flags[32], 0b00000101); - } - #[test] fn encodes_features_without_length() { let features = OfferFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]); - assert_eq!(features.flags.len(), 8); + assert_eq!(features.le_flags().len(), 8); let mut serialized_features = Vec::new(); WithoutLength(&features).write(&mut serialized_features).unwrap(); @@ -1248,69 +117,4 @@ mod tests { WithoutLength::::read(&mut &serialized_features[..]).unwrap().0; assert_eq!(features, deserialized_features); } - - #[test] - fn invoice_features_encoding() { - let features_as_u5s = vec![ - u5::try_from_u8(6).unwrap(), - u5::try_from_u8(10).unwrap(), - u5::try_from_u8(25).unwrap(), - u5::try_from_u8(1).unwrap(), - u5::try_from_u8(10).unwrap(), - u5::try_from_u8(0).unwrap(), - u5::try_from_u8(20).unwrap(), - u5::try_from_u8(2).unwrap(), - u5::try_from_u8(0).unwrap(), - u5::try_from_u8(6).unwrap(), - u5::try_from_u8(0).unwrap(), - u5::try_from_u8(16).unwrap(), - u5::try_from_u8(1).unwrap(), - ]; - let features = Bolt11InvoiceFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]); - - // Test length calculation. - assert_eq!(features.base32_len(), 13); - - // Test serialization. - let features_serialized = features.to_base32(); - assert_eq!(features_as_u5s, features_serialized); - - // Test deserialization. - let features_deserialized = Bolt11InvoiceFeatures::from_base32(&features_as_u5s).unwrap(); - assert_eq!(features, features_deserialized); - } - - #[test] - fn test_channel_type_mapping() { - // If we map an Bolt11InvoiceFeatures with StaticRemoteKey optional, it should map into a - // required-StaticRemoteKey ChannelTypeFeatures. - let mut init_features = InitFeatures::empty(); - init_features.set_static_remote_key_optional(); - let converted_features = ChannelTypeFeatures::from_init(&init_features); - assert_eq!(converted_features, ChannelTypeFeatures::only_static_remote_key()); - assert!(!converted_features.supports_any_optional_bits()); - assert!(converted_features.requires_static_remote_key()); - } - - #[test] - #[cfg(feature = "std")] - fn test_excess_zero_bytes_ignored() { - // Checks that `Hash` and `PartialEq` ignore excess zero bytes, which may appear due to - // feature conversion or because a peer serialized their feature poorly. - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - - let mut zerod_features = InitFeatures::empty(); - zerod_features.flags = vec![0]; - let empty_features = InitFeatures::empty(); - assert!(empty_features.flags.is_empty()); - - assert_eq!(zerod_features, empty_features); - - let mut zerod_hash = DefaultHasher::new(); - zerod_features.hash(&mut zerod_hash); - let mut empty_hash = DefaultHasher::new(); - empty_features.hash(&mut empty_hash); - assert_eq!(zerod_hash.finish(), empty_hash.finish()); - } } diff --git a/lightning-invoice/src/utils.rs b/lightning/src/ln/invoice_utils.rs similarity index 93% rename from lightning-invoice/src/utils.rs rename to lightning/src/ln/invoice_utils.rs index 669e6f50f1a..5a793052cc3 100644 --- a/lightning-invoice/src/utils.rs +++ b/lightning/src/ln/invoice_utils.rs @@ -1,22 +1,23 @@ //! 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 lightning::chain; -use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; -use lightning::sign::{Recipient, NodeSigner, SignerProvider, EntropySource}; -use lightning::ln::types::{PaymentHash, PaymentSecret}; -use lightning::ln::channel_state::ChannelDetails; -use lightning::ln::channelmanager::{ChannelManager, MIN_FINAL_CLTV_EXPIRY_DELTA}; -use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA}; -use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey}; -use lightning::routing::gossip::RoutingFees; -use lightning::routing::router::{RouteHint, RouteHintHop, Router}; -use lightning::util::logger::{Logger, Record}; -use secp256k1::PublicKey; +use crate::chain; +use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; +use crate::sign::{Recipient, NodeSigner, SignerProvider, EntropySource}; +use crate::ln::types::{PaymentHash, PaymentSecret}; +use crate::ln::channel_state::ChannelDetails; +use crate::ln::channelmanager::{ChannelManager, MIN_FINAL_CLTV_EXPIRY_DELTA}; +use crate::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA}; +use crate::ln::inbound_payment::{create, create_from_hash, ExpandedKey}; +use crate::routing::gossip::RoutingFees; +use crate::routing::router::{RouteHint, RouteHintHop, Router}; +use crate::util::logger::{Logger, Record}; +use bitcoin::secp256k1::PublicKey; use alloc::collections::{btree_map, BTreeMap}; use core::ops::Deref; use core::time::Duration; @@ -54,12 +55,12 @@ use core::iter::Iterator; /// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this /// requirement). /// -/// [`PhantomKeysManager`]: lightning::sign::PhantomKeysManager -/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints -/// [`ChannelManager::create_inbound_payment`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment -/// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash -/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels -/// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA +/// [`PhantomKeysManager`]: crate::sign::PhantomKeysManager +/// [`ChannelManager::get_phantom_route_hints`]: crate::ln::channelmanager::ChannelManager::get_phantom_route_hints +/// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment +/// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash +/// [`PhantomRouteHints::channels`]: crate::ln::channelmanager::PhantomRouteHints::channels +/// [`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 /// available and the current time is supplied by the caller. @@ -111,11 +112,11 @@ where /// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this /// requirement). /// -/// [`PhantomKeysManager`]: lightning::sign::PhantomKeysManager -/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints -/// [`ChannelManager::create_inbound_payment`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment -/// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash -/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels +/// [`PhantomKeysManager`]: crate::sign::PhantomKeysManager +/// [`ChannelManager::get_phantom_route_hints`]: crate::ln::channelmanager::ChannelManager::get_phantom_route_hints +/// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment +/// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash +/// [`PhantomRouteHints::channels`]: crate::ln::channelmanager::PhantomRouteHints::channels /// /// 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. @@ -161,7 +162,7 @@ where let invoice = match 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), }; @@ -217,10 +218,8 @@ where Ok(inv) => inv, Err(e) => return Err(SignOrCreationError::CreationError(e)) }; - let hrp_str = raw_invoice.hrp.to_string(); - let hrp_bytes = hrp_str.as_bytes(); - 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)); + let signature = node_signer.sign_invoice(&raw_invoice, Recipient::PhantomNode); + let signed_raw_invoice = raw_invoice.sign(|_| signature); match signed_raw_invoice { Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()), Err(e) => Err(SignOrCreationError::SignError(e)) @@ -234,7 +233,7 @@ where /// * Select up to three channels per node. /// * 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(amt_msat: Option, phantom_route_hints: Vec, logger: L) -> impl Iterator where @@ -331,7 +330,7 @@ fn rotate_through_iterators>(mut vecs: Vec) -> impl /// 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. /// -/// [`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( channelmanager: &ChannelManager, node_signer: NS, logger: L, network: Currency, amt_msat: Option, 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 /// 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( channelmanager: &ChannelManager, node_signer: NS, logger: L, network: Currency, amt_msat: Option, description_hash: Sha256, @@ -541,7 +540,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has let invoice = match 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), }; @@ -569,10 +568,8 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has Ok(inv) => inv, Err(e) => return Err(SignOrCreationError::CreationError(e)) }; - let hrp_str = raw_invoice.hrp.to_string(); - let hrp_bytes = hrp_str.as_bytes(); - 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)); + let signature = node_signer.sign_invoice(&raw_invoice, Recipient::Node); + let signed_raw_invoice = raw_invoice.sign(|_| signature); match signed_raw_invoice { Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()), 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)] mod test { + use super::*; 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::sha256::Hash as Sha256; - use lightning::sign::PhantomKeysManager; - use lightning::events::{MessageSendEvent, MessageSendEventsProvider}; - use lightning::ln::types::PaymentHash; + use crate::sign::PhantomKeysManager; + use crate::events::{MessageSendEvent, MessageSendEventsProvider}; + use crate::ln::types::PaymentHash; #[cfg(feature = "std")] - use lightning::ln::types::PaymentPreimage; - use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry}; - use lightning::ln::functional_test_utils::*; - use lightning::ln::msgs::ChannelMessageHandler; - use lightning::routing::router::{PaymentParameters, RouteParameters}; - use lightning::util::test_utils; - use lightning::util::config::UserConfig; - use crate::utils::{create_invoice_from_channelmanager_and_duration_since_epoch, rotate_through_iterators}; + use crate::ln::types::PaymentPreimage; + use crate::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry}; + use crate::ln::functional_test_utils::*; + use crate::ln::msgs::ChannelMessageHandler; + use crate::routing::router::{PaymentParameters, RouteParameters}; + use crate::util::test_utils; + use crate::util::config::UserConfig; use std::collections::HashSet; - use lightning::util::string::UntrustedString; #[test] fn test_prefer_current_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. - 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. - 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. - 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. - 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. - 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. - 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. - 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, Some(10_000), "test".to_string(), Duration::from_secs(1234567), 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`. 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())); // 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, RecipientOnionFields::secret_only(*invoice.payment_secret()), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); - let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap(); - assert_eq!(added_monitors.len(), 1); - added_monitors.clear(); + check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); 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_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(); - assert_eq!(added_monitors.len(), 1); - added_monitors.clear(); + check_added_monitors(&nodes[1], 1); let events = nodes[1].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 2); } @@ -930,7 +921,7 @@ mod test { let nodes = create_network(2, &node_cfgs, &node_chanmgrs); 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, Some(10_000), "".into(), Duration::from_secs(1234567), 3600, 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 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, Some(10_000), "".into(), Duration::from_secs(1234567), 3600, custom_min_final_cltv_expiry_delta, @@ -967,14 +958,14 @@ mod test { let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - let description_hash = crate::Sha256(Hash::hash("Testing description_hash".as_bytes())); - let invoice = crate::utils::create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch( + let description_hash = Sha256(Hash::hash("Testing description_hash".as_bytes())); + 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, Some(10_000), description_hash, Duration::from_secs(1234567), 3600, None, ).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.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] @@ -984,14 +975,14 @@ mod test { let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); 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, Some(10_000), "test".to_string(), Duration::from_secs(1234567), 3600, payment_hash, None, ).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.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()); } @@ -1283,7 +1274,7 @@ mod test { let hints = invoice.private_routes(); 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.is_empty(), "Unmatched short channel ids: {:?}", chan_ids_to_match); @@ -1298,7 +1289,7 @@ mod test { #[cfg(feature = "std")] 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; let mut chanmon_cfgs = create_chanmon_cfgs(3); @@ -1333,7 +1324,7 @@ mod test { let non_default_invoice_expiry_secs = 4200; 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, route_hints, nodes[1].keys_manager, nodes[1].keys_manager, nodes[1].logger, 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.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.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into())); assert!(!invoice.features().unwrap().supports_basic_mpp()); @@ -1362,9 +1353,7 @@ mod test { nodes[0].node.send_payment(payment_hash, RecipientOnionFields::secret_only(*invoice.payment_secret()), PaymentId(payment_hash.0), params, Retry::Attempts(0)).unwrap(); - let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap(); - assert_eq!(added_monitors.len(), 1); - added_monitors.clear(); + check_added_monitors(&nodes[0], 1); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); @@ -1428,7 +1417,7 @@ mod test { 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".to_string(), 3600, route_hints, nodes[1].keys_manager, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap(); @@ -1444,7 +1433,7 @@ mod test { #[test] #[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 node_cfgs = create_node_cfgs(3, &chanmon_cfgs); 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(), ]; - 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 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, >( Some(payment_amt), None, non_default_invoice_expiry_secs, description_hash, @@ -1466,10 +1455,10 @@ mod test { Currency::BitcoinTestnet, None, Duration::from_secs(1234567), ) .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.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] @@ -1490,11 +1479,11 @@ mod test { let non_default_invoice_expiry_secs = 4200; let min_final_cltv_expiry_delta = Some(100); 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, "".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(); - 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.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into())); } @@ -1895,7 +1884,7 @@ mod test { .map(|route_hint| route_hint.phantom_scid) .collect::>(); - 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(), 3600, phantom_route_hints, invoice_node.keys_manager, invoice_node.keys_manager, invoice_node.logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap(); @@ -1903,7 +1892,7 @@ mod test { let invoice_hints = invoice.private_routes(); for hint in invoice_hints { - let hints = &(hint.0).0; + let hints = &hint.0; match hints.len() { 1 => { assert!(nodes_contains_public_channels); @@ -1928,7 +1917,7 @@ mod test { let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); 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, Some(10_000), "Some description".into(), Duration::from_secs(1234567), 3600, Some(MIN_FINAL_CLTV_EXPIRY_DELTA - 4), ); diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 888044b7aea..8009f31b0dc 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -25,7 +25,12 @@ pub mod features; pub mod script; 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)] pub mod peer_channel_encryptor; diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 5cc1257c461..85f1ae0aa48 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -2320,11 +2320,26 @@ impl_writeable_msg!(ChannelReady, { (1, short_channel_id_alias, option), }); +pub(crate) fn write_features_up_to_13(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 { fn write(&self, w: &mut W) -> Result<(), io::Error> { // 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. - self.features.write_up_to_13(w)?; + write_features_up_to_13(w, self.features.le_flags())?; self.features.write(w)?; encode_tlv_stream!(w, { (1, self.networks.as_ref().map(|n| WithoutLength(n)), option), diff --git a/lightning/src/ln/types.rs b/lightning/src/ln/types.rs index b813239786a..d5ab05505bb 100644 --- a/lightning/src/ln/types.rs +++ b/lightning/src/ln/types.rs @@ -121,75 +121,7 @@ impl fmt::Display for ChannelId { } } - -/// 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 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 { - if field_data.len() != 52 { - return Err(bech32::Error::InvalidLength) - } else { - let data_bytes = Vec::::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(&self, writer: &mut W) -> Result<(), ::Err> { - (&self.0[..]).write_base32(writer) - } -} - -impl Base32Len for PaymentSecret { - fn base32_len(&self) -> usize { - 52 - } -} +pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; #[cfg(test)] mod tests { diff --git a/lightning/src/routing/gossip.rs b/lightning/src/routing/gossip.rs index 31f93db905c..22a3f6e7980 100644 --- a/lightning/src/routing/gossip.rs +++ b/lightning/src/routing/gossip.rs @@ -45,6 +45,8 @@ use core::str::FromStr; use core::sync::atomic::{AtomicUsize, Ordering}; use core::{cmp, fmt}; +pub use lightning_types::routing::RoutingFees; + #[cfg(feature = "std")] 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, { (0, base_msat, required), (2, proportional_millionths, required) diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index dac9ada3800..bf894f0ebb6 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -22,7 +22,7 @@ use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT}; use crate::ln::onion_utils; use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice}; 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::sign::EntropySource; use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer}; @@ -35,6 +35,10 @@ use alloc::collections::BinaryHeap; use core::{cmp, fmt}; use core::ops::Deref; +use lightning_types::routing::RoutingFees; + +pub use lightning_types::routing::{RouteHint, RouteHintHop}; + /// A [`Router`] implemented using [`find_route`]. /// /// # Privacy @@ -1099,10 +1103,6 @@ impl ReadableArgs 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); - impl Writeable for RouteHint { fn write(&self, writer: &mut W) -> Result<(), io::Error> { (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, - /// The maximum value in msat available for routing with a single HTLC. - pub htlc_maximum_msat: Option, -} - impl_writeable_tlv_based!(RouteHintHop, { (0, src_node_id, required), (1, htlc_minimum_msat, option), diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index f5fc9bb19bd..1dd8c90f65f 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -24,7 +24,6 @@ use bitcoin::sighash::EcdsaSighashType; use bitcoin::transaction::Version; use bitcoin::transaction::{Transaction, TxIn, TxOut}; -use bech32::u5; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256d::Hash as Sha256dHash; 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, Psbt, Sequence, Txid, WPubkeyHash, Witness}; +use lightning_invoice::RawBolt11Invoice; + use crate::chain::transaction::OutPoint; use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; use crate::ln::chan_utils; @@ -69,7 +70,6 @@ use crate::sign::ecdsa::EcdsaChannelSigner; #[cfg(taproot)] use crate::sign::taproot::TaprootChannelSigner; use crate::util::atomic_counter::AtomicCounter; -use crate::util::invoice::construct_invoice_preimage; use core::convert::TryInto; use core::ops::Deref; use core::sync::atomic::{AtomicUsize, Ordering}; @@ -867,7 +867,7 @@ pub trait NodeSigner { /// /// Errors if the [`Recipient`] variant is not supported by the implementation. fn sign_invoice( - &self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, + &self, invoice: &RawBolt11Invoice, recipient: Recipient, ) -> Result; /// Signs the [`TaggedHash`] of a BOLT 12 invoice request. @@ -2174,17 +2174,14 @@ impl NodeSigner for KeysManager { } fn sign_invoice( - &self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, + &self, invoice: &RawBolt11Invoice, recipient: Recipient, ) -> Result { - let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data); + let hash = invoice.signable_hash(); let secret = match recipient { Recipient::Node => Ok(&self.node_secret), Recipient::PhantomNode => Err(()), }?; - Ok(self.secp_ctx.sign_ecdsa_recoverable( - &hash_to_message!(&Sha256::hash(&preimage).to_byte_array()), - secret, - )) + Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&hash), secret)) } fn sign_bolt12_invoice_request( @@ -2352,17 +2349,14 @@ impl NodeSigner for PhantomKeysManager { } fn sign_invoice( - &self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, + &self, invoice: &RawBolt11Invoice, recipient: Recipient, ) -> Result { - let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data); + let hash = invoice.signable_hash(); let secret = match recipient { Recipient::Node => &self.inner.node_secret, Recipient::PhantomNode => &self.phantom_secret, }; - Ok(self.inner.secp_ctx.sign_ecdsa_recoverable( - &hash_to_message!(&Sha256::hash(&preimage).to_byte_array()), - secret, - )) + Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&hash), secret)) } fn sign_bolt12_invoice_request( diff --git a/lightning/src/util/invoice.rs b/lightning/src/util/invoice.rs deleted file mode 100644 index 4a4baa45267..00000000000 --- a/lightning/src/util/invoice.rs +++ /dev/null @@ -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 { - let mut preimage = Vec::::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::::from_base32(&data_part) - .expect("No padding error may occur due to appended zero above.")); - preimage -} - diff --git a/lightning/src/util/mod.rs b/lightning/src/util/mod.rs index a81a36c5583..f19a9b8d8aa 100644 --- a/lightning/src/util/mod.rs +++ b/lightning/src/util/mod.rs @@ -18,10 +18,8 @@ pub mod ser_macros; pub mod errors; pub mod ser; pub mod message_signing; -pub mod invoice; pub mod persist; pub mod scid_utils; -pub mod string; pub mod sweep; pub mod wakers; #[cfg(fuzzing)] @@ -54,3 +52,7 @@ pub mod test_utils; #[cfg(any(test, feature = "_test_utils"))] 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}; +} diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 3d3d97252f2..29c52f6a08b 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -627,6 +627,18 @@ impl<'a> From<&'a String> for WithoutLength<&'a String> { fn from(s: &'a String) -> Self { Self(s) } } +impl Writeable for UntrustedString { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.write(w) + } +} + +impl Readable for UntrustedString { + fn read(r: &mut R) -> Result { + let s: String = Readable::read(r)?; + Ok(Self(s)) + } +} impl Writeable for WithoutLength<&UntrustedString> { #[inline] diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 113006cbb1c..f5256064edf 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -65,6 +65,8 @@ use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::schnorr; +use lightning_invoice::RawBolt11Invoice; + use crate::io; use crate::prelude::*; use core::cell::RefCell; @@ -72,7 +74,6 @@ use core::time::Duration; use crate::sync::{Mutex, Arc}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::mem; -use bech32::u5; use crate::sign::{InMemorySigner, RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider}; #[cfg(feature = "std")] @@ -1217,7 +1218,7 @@ impl NodeSigner for TestNodeSigner { Ok(SharedSecret::new(other_key, &node_secret)) } - fn sign_invoice(&self, _: &[u8], _: &[bech32::u5], _: Recipient) -> Result { + fn sign_invoice(&self, _: &RawBolt11Invoice, _: Recipient) -> Result { unreachable!() } @@ -1270,8 +1271,8 @@ impl NodeSigner for TestKeysInterface { self.backing.get_inbound_payment_key_material() } - fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result { - self.backing.sign_invoice(hrp_bytes, invoice_data, recipient) + fn sign_invoice(&self, invoice: &RawBolt11Invoice, recipient: Recipient) -> Result { + self.backing.sign_invoice(invoice, recipient) } fn sign_bolt12_invoice_request( diff --git a/rustfmt_excluded_files b/rustfmt_excluded_files index 58f77b4a91b..898a2c26e8a 100644 --- a/rustfmt_excluded_files +++ b/rustfmt_excluded_files @@ -41,6 +41,7 @@ ./lightning/src/ln/functional_test_utils.rs ./lightning/src/ln/functional_tests.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/mod.rs ./lightning/src/ln/monitor_tests.rs