mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-26 21:42:35 +01:00
Merge pull request #2781 from jkczyz/2023-09-multihop-paths
Multi-hop `BlindedPath` creation interface
This commit is contained in:
commit
b9797ebdd9
11 changed files with 459 additions and 126 deletions
|
@ -30,6 +30,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
|
||||||
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
|
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
|
||||||
use bitcoin::hash_types::{BlockHash, WPubkeyHash};
|
use bitcoin::hash_types::{BlockHash, WPubkeyHash};
|
||||||
|
|
||||||
|
use lightning::blinded_path::BlindedPath;
|
||||||
|
use lightning::blinded_path::payment::ReceiveTlvs;
|
||||||
use lightning::chain;
|
use lightning::chain;
|
||||||
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, chainmonitor, channelmonitor, Confirm, Watch};
|
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, chainmonitor, channelmonitor, Confirm, Watch};
|
||||||
use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent};
|
use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent};
|
||||||
|
@ -44,8 +46,9 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
|
||||||
use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
|
use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
|
||||||
use lightning::ln::script::ShutdownScript;
|
use lightning::ln::script::ShutdownScript;
|
||||||
use lightning::ln::functional_test_utils::*;
|
use lightning::ln::functional_test_utils::*;
|
||||||
use lightning::offers::invoice::UnsignedBolt12Invoice;
|
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
|
||||||
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
||||||
|
use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
|
||||||
use lightning::util::test_channel_signer::{TestChannelSigner, EnforcementState};
|
use lightning::util::test_channel_signer::{TestChannelSigner, EnforcementState};
|
||||||
use lightning::util::errors::APIError;
|
use lightning::util::errors::APIError;
|
||||||
use lightning::util::logger::Logger;
|
use lightning::util::logger::Logger;
|
||||||
|
@ -56,7 +59,7 @@ use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RoutePara
|
||||||
use crate::utils::test_logger::{self, Output};
|
use crate::utils::test_logger::{self, Output};
|
||||||
use crate::utils::test_persister::TestPersister;
|
use crate::utils::test_persister::TestPersister;
|
||||||
|
|
||||||
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
|
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1, self};
|
||||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||||
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
||||||
use bitcoin::secp256k1::schnorr;
|
use bitcoin::secp256k1::schnorr;
|
||||||
|
@ -99,6 +102,32 @@ impl Router for FuzzRouter {
|
||||||
action: msgs::ErrorAction::IgnoreError
|
action: msgs::ErrorAction::IgnoreError
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_blinded_payment_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
|
||||||
|
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageRouter for FuzzRouter {
|
||||||
|
fn find_path(
|
||||||
|
&self, _sender: PublicKey, _peers: Vec<PublicKey>, _destination: Destination
|
||||||
|
) -> Result<OnionMessagePath, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _entropy_source: &ES,
|
||||||
|
_secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestBroadcaster {}
|
pub struct TestBroadcaster {}
|
||||||
|
|
|
@ -28,6 +28,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
|
||||||
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
|
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
|
||||||
use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
|
use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
|
||||||
|
|
||||||
|
use lightning::blinded_path::BlindedPath;
|
||||||
|
use lightning::blinded_path::payment::ReceiveTlvs;
|
||||||
use lightning::chain;
|
use lightning::chain;
|
||||||
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
|
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
|
||||||
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
|
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
|
||||||
|
@ -41,8 +43,9 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig
|
||||||
use lightning::ln::msgs::{self, DecodeError};
|
use lightning::ln::msgs::{self, DecodeError};
|
||||||
use lightning::ln::script::ShutdownScript;
|
use lightning::ln::script::ShutdownScript;
|
||||||
use lightning::ln::functional_test_utils::*;
|
use lightning::ln::functional_test_utils::*;
|
||||||
use lightning::offers::invoice::UnsignedBolt12Invoice;
|
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
|
||||||
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
||||||
|
use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
|
||||||
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
|
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
|
||||||
use lightning::routing::utxo::UtxoLookup;
|
use lightning::routing::utxo::UtxoLookup;
|
||||||
use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
|
use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
|
||||||
|
@ -55,7 +58,7 @@ use lightning::util::ser::{ReadableArgs, Writeable};
|
||||||
use crate::utils::test_logger;
|
use crate::utils::test_logger;
|
||||||
use crate::utils::test_persister::TestPersister;
|
use crate::utils::test_persister::TestPersister;
|
||||||
|
|
||||||
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1};
|
use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1, self};
|
||||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||||
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
||||||
use bitcoin::secp256k1::schnorr;
|
use bitcoin::secp256k1::schnorr;
|
||||||
|
@ -142,6 +145,32 @@ impl Router for FuzzRouter {
|
||||||
action: msgs::ErrorAction::IgnoreError
|
action: msgs::ErrorAction::IgnoreError
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_blinded_payment_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
|
||||||
|
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageRouter for FuzzRouter {
|
||||||
|
fn find_path(
|
||||||
|
&self, _sender: PublicKey, _peers: Vec<PublicKey>, _destination: Destination
|
||||||
|
) -> Result<OnionMessagePath, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _entropy_source: &ES,
|
||||||
|
_secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestBroadcaster {
|
struct TestBroadcaster {
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
// Imports that need to be added manually
|
// Imports that need to be added manually
|
||||||
use bitcoin::bech32::u5;
|
use bitcoin::bech32::u5;
|
||||||
use bitcoin::blockdata::script::ScriptBuf;
|
use bitcoin::blockdata::script::ScriptBuf;
|
||||||
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
|
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, self};
|
||||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||||
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
|
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
|
||||||
use bitcoin::secp256k1::schnorr;
|
use bitcoin::secp256k1::schnorr;
|
||||||
|
|
||||||
use lightning::sign::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
|
use lightning::blinded_path::BlindedPath;
|
||||||
use lightning::ln::features::InitFeatures;
|
use lightning::ln::features::InitFeatures;
|
||||||
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
|
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
|
||||||
use lightning::ln::script::ShutdownScript;
|
use lightning::ln::script::ShutdownScript;
|
||||||
use lightning::offers::invoice::UnsignedBolt12Invoice;
|
use lightning::offers::invoice::UnsignedBolt12Invoice;
|
||||||
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
|
||||||
|
use lightning::sign::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
|
||||||
use lightning::util::test_channel_signer::TestChannelSigner;
|
use lightning::util::test_channel_signer::TestChannelSigner;
|
||||||
use lightning::util::logger::Logger;
|
use lightning::util::logger::Logger;
|
||||||
use lightning::util::ser::{Readable, Writeable, Writer};
|
use lightning::util::ser::{Readable, Writeable, Writer};
|
||||||
|
@ -82,6 +83,15 @@ impl MessageRouter for TestMessageRouter {
|
||||||
first_node_addresses: None,
|
first_node_addresses: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _entropy_source: &ES,
|
||||||
|
_secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestOffersMessageHandler {}
|
struct TestOffersMessageHandler {}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::blinded_path::BlindedHop;
|
||||||
use crate::blinded_path::utils;
|
use crate::blinded_path::utils;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::ln::PaymentSecret;
|
use crate::ln::PaymentSecret;
|
||||||
|
use crate::ln::channelmanager::CounterpartyForwardingInfo;
|
||||||
use crate::ln::features::BlindedHopFeatures;
|
use crate::ln::features::BlindedHopFeatures;
|
||||||
use crate::ln::msgs::DecodeError;
|
use crate::ln::msgs::DecodeError;
|
||||||
use crate::offers::invoice::BlindedPayInfo;
|
use crate::offers::invoice::BlindedPayInfo;
|
||||||
|
@ -96,6 +97,15 @@ pub struct PaymentConstraints {
|
||||||
pub htlc_minimum_msat: u64,
|
pub htlc_minimum_msat: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<CounterpartyForwardingInfo> for PaymentRelay {
|
||||||
|
fn from(info: CounterpartyForwardingInfo) -> Self {
|
||||||
|
let CounterpartyForwardingInfo {
|
||||||
|
fee_base_msat, fee_proportional_millionths, cltv_expiry_delta
|
||||||
|
} = info;
|
||||||
|
Self { cltv_expiry_delta, fee_proportional_millionths, fee_base_msat }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Writeable for ForwardTlvs {
|
impl Writeable for ForwardTlvs {
|
||||||
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
||||||
encode_tlv_stream!(w, {
|
encode_tlv_stream!(w, {
|
||||||
|
|
|
@ -65,7 +65,7 @@ use crate::offers::merkle::SignError;
|
||||||
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
|
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
|
||||||
use crate::offers::parse::Bolt12SemanticError;
|
use crate::offers::parse::Bolt12SemanticError;
|
||||||
use crate::offers::refund::{Refund, RefundBuilder};
|
use crate::offers::refund::{Refund, RefundBuilder};
|
||||||
use crate::onion_message::{Destination, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message};
|
use crate::onion_message::{Destination, MessageRouter, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message};
|
||||||
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider};
|
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider};
|
||||||
use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
|
use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
|
||||||
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
|
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
|
||||||
|
@ -7483,32 +7483,43 @@ where
|
||||||
///
|
///
|
||||||
/// # Privacy
|
/// # Privacy
|
||||||
///
|
///
|
||||||
/// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the
|
/// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the offer.
|
||||||
/// introduction node and a derived signing pubkey for recipient privacy. As such, currently,
|
/// However, if one is not found, uses a one-hop [`BlindedPath`] with
|
||||||
/// the node must be announced. Otherwise, there is no way to find a path to the introduction
|
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
|
||||||
/// node in order to send the [`InvoiceRequest`].
|
/// the node must be announced, otherwise, there is no way to find a path to the introduction in
|
||||||
|
/// order to send the [`InvoiceRequest`].
|
||||||
|
///
|
||||||
|
/// Also, uses a derived signing pubkey in the offer for recipient privacy.
|
||||||
///
|
///
|
||||||
/// # Limitations
|
/// # Limitations
|
||||||
///
|
///
|
||||||
/// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
|
/// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
|
||||||
/// reply path.
|
/// reply path.
|
||||||
///
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Errors if the parameterized [`Router`] is unable to create a blinded path for the offer.
|
||||||
|
///
|
||||||
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
|
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
|
||||||
///
|
///
|
||||||
/// [`Offer`]: crate::offers::offer::Offer
|
/// [`Offer`]: crate::offers::offer::Offer
|
||||||
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
|
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
|
||||||
pub fn create_offer_builder(
|
pub fn create_offer_builder(
|
||||||
&self, description: String
|
&self, description: String
|
||||||
) -> OfferBuilder<DerivedMetadata, secp256k1::All> {
|
) -> Result<OfferBuilder<DerivedMetadata, secp256k1::All>, Bolt12SemanticError> {
|
||||||
let node_id = self.get_our_node_id();
|
let node_id = self.get_our_node_id();
|
||||||
let expanded_key = &self.inbound_payment_key;
|
let expanded_key = &self.inbound_payment_key;
|
||||||
let entropy = &*self.entropy_source;
|
let entropy = &*self.entropy_source;
|
||||||
let secp_ctx = &self.secp_ctx;
|
let secp_ctx = &self.secp_ctx;
|
||||||
let path = self.create_one_hop_blinded_path();
|
|
||||||
|
|
||||||
OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx)
|
let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
|
||||||
|
let builder = OfferBuilder::deriving_signing_pubkey(
|
||||||
|
description, node_id, expanded_key, entropy, secp_ctx
|
||||||
|
)
|
||||||
.chain_hash(self.chain_hash)
|
.chain_hash(self.chain_hash)
|
||||||
.path(path)
|
.path(path);
|
||||||
|
|
||||||
|
Ok(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
|
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
|
||||||
|
@ -7533,10 +7544,13 @@ where
|
||||||
///
|
///
|
||||||
/// # Privacy
|
/// # Privacy
|
||||||
///
|
///
|
||||||
/// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as
|
/// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the refund.
|
||||||
/// the introduction node and a derived payer id for payer privacy. As such, currently, the
|
/// However, if one is not found, uses a one-hop [`BlindedPath`] with
|
||||||
/// node must be announced. Otherwise, there is no way to find a path to the introduction node
|
/// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
|
||||||
/// in order to send the [`Bolt12Invoice`].
|
/// the node must be announced, otherwise, there is no way to find a path to the introduction in
|
||||||
|
/// order to send the [`Bolt12Invoice`].
|
||||||
|
///
|
||||||
|
/// Also, uses a derived payer id in the refund for payer privacy.
|
||||||
///
|
///
|
||||||
/// # Limitations
|
/// # Limitations
|
||||||
///
|
///
|
||||||
|
@ -7545,14 +7559,17 @@ where
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
|
/// Errors if:
|
||||||
/// or if `amount_msats` is invalid.
|
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
|
||||||
|
/// - `amount_msats` is invalid, or
|
||||||
|
/// - the parameterized [`Router`] is unable to create a blinded path for the refund.
|
||||||
///
|
///
|
||||||
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
|
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
|
||||||
///
|
///
|
||||||
/// [`Refund`]: crate::offers::refund::Refund
|
/// [`Refund`]: crate::offers::refund::Refund
|
||||||
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
|
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
|
||||||
/// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
|
/// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
|
||||||
|
/// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
|
||||||
pub fn create_refund_builder(
|
pub fn create_refund_builder(
|
||||||
&self, description: String, amount_msats: u64, absolute_expiry: Duration,
|
&self, description: String, amount_msats: u64, absolute_expiry: Duration,
|
||||||
payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
|
payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
|
||||||
|
@ -7561,8 +7578,8 @@ where
|
||||||
let expanded_key = &self.inbound_payment_key;
|
let expanded_key = &self.inbound_payment_key;
|
||||||
let entropy = &*self.entropy_source;
|
let entropy = &*self.entropy_source;
|
||||||
let secp_ctx = &self.secp_ctx;
|
let secp_ctx = &self.secp_ctx;
|
||||||
let path = self.create_one_hop_blinded_path();
|
|
||||||
|
|
||||||
|
let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
|
||||||
let builder = RefundBuilder::deriving_payer_id(
|
let builder = RefundBuilder::deriving_payer_id(
|
||||||
description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
|
description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
|
||||||
)?
|
)?
|
||||||
|
@ -7620,8 +7637,11 @@ where
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
|
/// Errors if:
|
||||||
/// or if the provided parameters are invalid for the offer.
|
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
|
||||||
|
/// - the provided parameters are invalid for the offer,
|
||||||
|
/// - the parameterized [`Router`] is unable to create a blinded reply path for the invoice
|
||||||
|
/// request.
|
||||||
///
|
///
|
||||||
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
|
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
|
||||||
/// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
|
/// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
|
||||||
|
@ -7654,9 +7674,8 @@ where
|
||||||
None => builder,
|
None => builder,
|
||||||
Some(payer_note) => builder.payer_note(payer_note),
|
Some(payer_note) => builder.payer_note(payer_note),
|
||||||
};
|
};
|
||||||
|
|
||||||
let invoice_request = builder.build_and_sign()?;
|
let invoice_request = builder.build_and_sign()?;
|
||||||
let reply_path = self.create_one_hop_blinded_path();
|
let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
|
||||||
|
|
||||||
let expiration = StaleExpiration::TimerTicks(1);
|
let expiration = StaleExpiration::TimerTicks(1);
|
||||||
self.pending_outbound_payments
|
self.pending_outbound_payments
|
||||||
|
@ -7705,6 +7724,11 @@ where
|
||||||
/// node meeting the aforementioned criteria, but there's no guarantee that they will be
|
/// node meeting the aforementioned criteria, but there's no guarantee that they will be
|
||||||
/// received and no retries will be made.
|
/// received and no retries will be made.
|
||||||
///
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Errors if the parameterized [`Router`] is unable to create a blinded payment path or reply
|
||||||
|
/// path for the invoice.
|
||||||
|
///
|
||||||
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
|
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
|
||||||
pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> {
|
pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> {
|
||||||
let expanded_key = &self.inbound_payment_key;
|
let expanded_key = &self.inbound_payment_key;
|
||||||
|
@ -7716,9 +7740,9 @@ where
|
||||||
|
|
||||||
match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) {
|
match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) {
|
||||||
Ok((payment_hash, payment_secret)) => {
|
Ok((payment_hash, payment_secret)) => {
|
||||||
let payment_paths = vec![
|
let payment_paths = self.create_blinded_payment_paths(amount_msats, payment_secret)
|
||||||
self.create_one_hop_blinded_payment_path(payment_secret),
|
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
|
||||||
];
|
|
||||||
#[cfg(not(feature = "no-std"))]
|
#[cfg(not(feature = "no-std"))]
|
||||||
let builder = refund.respond_using_derived_keys(
|
let builder = refund.respond_using_derived_keys(
|
||||||
payment_paths, payment_hash, expanded_key, entropy
|
payment_paths, payment_hash, expanded_key, entropy
|
||||||
|
@ -7732,7 +7756,8 @@ where
|
||||||
payment_paths, payment_hash, created_at, expanded_key, entropy
|
payment_paths, payment_hash, created_at, expanded_key, entropy
|
||||||
)?;
|
)?;
|
||||||
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
|
let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
|
||||||
let reply_path = self.create_one_hop_blinded_path();
|
let reply_path = self.create_blinded_path()
|
||||||
|
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
|
||||||
|
|
||||||
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
|
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
|
||||||
if refund.paths().is_empty() {
|
if refund.paths().is_empty() {
|
||||||
|
@ -7859,24 +7884,37 @@ where
|
||||||
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
|
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
|
/// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
|
||||||
/// node.
|
///
|
||||||
fn create_one_hop_blinded_path(&self) -> BlindedPath {
|
/// Errors if the `MessageRouter` errors or returns an empty `Vec`.
|
||||||
|
fn create_blinded_path(&self) -> Result<BlindedPath, ()> {
|
||||||
|
let recipient = self.get_our_node_id();
|
||||||
let entropy_source = self.entropy_source.deref();
|
let entropy_source = self.entropy_source.deref();
|
||||||
let secp_ctx = &self.secp_ctx;
|
let secp_ctx = &self.secp_ctx;
|
||||||
BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap()
|
|
||||||
|
let peers = self.per_peer_state.read().unwrap()
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, peer)| peer.lock().unwrap().latest_features.supports_onion_messages())
|
||||||
|
.map(|(node_id, _)| *node_id)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
self.router
|
||||||
|
.create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
|
||||||
|
.and_then(|paths| paths.into_iter().next().ok_or(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
|
/// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to
|
||||||
/// node.
|
/// [`Router::create_blinded_payment_paths`].
|
||||||
fn create_one_hop_blinded_payment_path(
|
fn create_blinded_payment_paths(
|
||||||
&self, payment_secret: PaymentSecret
|
&self, amount_msats: u64, payment_secret: PaymentSecret
|
||||||
) -> (BlindedPayInfo, BlindedPath) {
|
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
|
||||||
let entropy_source = self.entropy_source.deref();
|
let entropy_source = self.entropy_source.deref();
|
||||||
let secp_ctx = &self.secp_ctx;
|
let secp_ctx = &self.secp_ctx;
|
||||||
|
|
||||||
|
let first_hops = self.list_usable_channels();
|
||||||
let payee_node_id = self.get_our_node_id();
|
let payee_node_id = self.get_our_node_id();
|
||||||
let max_cltv_expiry = self.best_block.read().unwrap().height() + LATENCY_GRACE_PERIOD_BLOCKS;
|
let max_cltv_expiry = self.best_block.read().unwrap().height() + CLTV_FAR_FAR_AWAY
|
||||||
|
+ LATENCY_GRACE_PERIOD_BLOCKS;
|
||||||
let payee_tlvs = ReceiveTlvs {
|
let payee_tlvs = ReceiveTlvs {
|
||||||
payment_secret,
|
payment_secret,
|
||||||
payment_constraints: PaymentConstraints {
|
payment_constraints: PaymentConstraints {
|
||||||
|
@ -7884,10 +7922,9 @@ where
|
||||||
htlc_minimum_msat: 1,
|
htlc_minimum_msat: 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// TODO: Err for overflow?
|
self.router.create_blinded_payment_paths(
|
||||||
BlindedPath::one_hop_for_payment(
|
payee_node_id, first_hops, payee_tlvs, amount_msats, entropy_source, secp_ctx
|
||||||
payee_node_id, payee_tlvs, entropy_source, secp_ctx
|
)
|
||||||
).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
|
/// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
|
||||||
|
@ -9127,7 +9164,7 @@ where
|
||||||
let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
|
let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
|
||||||
&invoice_request
|
&invoice_request
|
||||||
) {
|
) {
|
||||||
Ok(amount_msats) => Some(amount_msats),
|
Ok(amount_msats) => amount_msats,
|
||||||
Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
|
Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
|
||||||
};
|
};
|
||||||
let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
|
let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
|
||||||
|
@ -9137,64 +9174,69 @@ where
|
||||||
return Some(OffersMessage::InvoiceError(error.into()));
|
return Some(OffersMessage::InvoiceError(error.into()));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
|
|
||||||
|
|
||||||
match self.create_inbound_payment(amount_msats, relative_expiry, None) {
|
let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
|
||||||
Ok((payment_hash, payment_secret)) if invoice_request.keys.is_some() => {
|
let (payment_hash, payment_secret) = match self.create_inbound_payment(
|
||||||
let payment_paths = vec![
|
Some(amount_msats), relative_expiry, None
|
||||||
self.create_one_hop_blinded_payment_path(payment_secret),
|
) {
|
||||||
];
|
Ok((payment_hash, payment_secret)) => (payment_hash, payment_secret),
|
||||||
#[cfg(not(feature = "no-std"))]
|
|
||||||
let builder = invoice_request.respond_using_derived_keys(
|
|
||||||
payment_paths, payment_hash
|
|
||||||
);
|
|
||||||
#[cfg(feature = "no-std")]
|
|
||||||
let created_at = Duration::from_secs(
|
|
||||||
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
|
|
||||||
);
|
|
||||||
#[cfg(feature = "no-std")]
|
|
||||||
let builder = invoice_request.respond_using_derived_keys_no_std(
|
|
||||||
payment_paths, payment_hash, created_at
|
|
||||||
);
|
|
||||||
match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
|
|
||||||
Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
|
|
||||||
Err(error) => Some(OffersMessage::InvoiceError(error.into())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Ok((payment_hash, payment_secret)) => {
|
|
||||||
let payment_paths = vec![
|
|
||||||
self.create_one_hop_blinded_payment_path(payment_secret),
|
|
||||||
];
|
|
||||||
#[cfg(not(feature = "no-std"))]
|
|
||||||
let builder = invoice_request.respond_with(payment_paths, payment_hash);
|
|
||||||
#[cfg(feature = "no-std")]
|
|
||||||
let created_at = Duration::from_secs(
|
|
||||||
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
|
|
||||||
);
|
|
||||||
#[cfg(feature = "no-std")]
|
|
||||||
let builder = invoice_request.respond_with_no_std(
|
|
||||||
payment_paths, payment_hash, created_at
|
|
||||||
);
|
|
||||||
let response = builder.and_then(|builder| builder.allow_mpp().build())
|
|
||||||
.map_err(|e| OffersMessage::InvoiceError(e.into()))
|
|
||||||
.and_then(|invoice|
|
|
||||||
match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
|
|
||||||
Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
|
|
||||||
Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
|
|
||||||
InvoiceError::from_string("Failed signing invoice".to_string())
|
|
||||||
)),
|
|
||||||
Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
|
|
||||||
InvoiceError::from_string("Failed invoice signature verification".to_string())
|
|
||||||
)),
|
|
||||||
});
|
|
||||||
match response {
|
|
||||||
Ok(invoice) => Some(invoice),
|
|
||||||
Err(error) => Some(error),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
Some(OffersMessage::InvoiceError(Bolt12SemanticError::InvalidAmount.into()))
|
let error = Bolt12SemanticError::InvalidAmount;
|
||||||
|
return Some(OffersMessage::InvoiceError(error.into()));
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let payment_paths = match self.create_blinded_payment_paths(
|
||||||
|
amount_msats, payment_secret
|
||||||
|
) {
|
||||||
|
Ok(payment_paths) => payment_paths,
|
||||||
|
Err(()) => {
|
||||||
|
let error = Bolt12SemanticError::MissingPaths;
|
||||||
|
return Some(OffersMessage::InvoiceError(error.into()));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "no-std")]
|
||||||
|
let created_at = Duration::from_secs(
|
||||||
|
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
|
||||||
|
);
|
||||||
|
|
||||||
|
if invoice_request.keys.is_some() {
|
||||||
|
#[cfg(not(feature = "no-std"))]
|
||||||
|
let builder = invoice_request.respond_using_derived_keys(
|
||||||
|
payment_paths, payment_hash
|
||||||
|
);
|
||||||
|
#[cfg(feature = "no-std")]
|
||||||
|
let builder = invoice_request.respond_using_derived_keys_no_std(
|
||||||
|
payment_paths, payment_hash, created_at
|
||||||
|
);
|
||||||
|
match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
|
||||||
|
Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
|
||||||
|
Err(error) => Some(OffersMessage::InvoiceError(error.into())),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#[cfg(not(feature = "no-std"))]
|
||||||
|
let builder = invoice_request.respond_with(payment_paths, payment_hash);
|
||||||
|
#[cfg(feature = "no-std")]
|
||||||
|
let builder = invoice_request.respond_with_no_std(
|
||||||
|
payment_paths, payment_hash, created_at
|
||||||
|
);
|
||||||
|
let response = builder.and_then(|builder| builder.allow_mpp().build())
|
||||||
|
.map_err(|e| OffersMessage::InvoiceError(e.into()))
|
||||||
|
.and_then(|invoice|
|
||||||
|
match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
|
||||||
|
Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
|
||||||
|
Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
|
||||||
|
InvoiceError::from_string("Failed signing invoice".to_string())
|
||||||
|
)),
|
||||||
|
Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
|
||||||
|
InvoiceError::from_string("Failed invoice signature verification".to_string())
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
match response {
|
||||||
|
Ok(invoice) => Some(invoice),
|
||||||
|
Err(error) => Some(error),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
OffersMessage::Invoice(invoice) => {
|
OffersMessage::Invoice(invoice) => {
|
||||||
|
|
|
@ -41,6 +41,12 @@
|
||||||
//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#basic-multi-part-payments) for more information).
|
//! (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.
|
//! - `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).
|
//! (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`
|
//! - `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).
|
//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information).
|
||||||
//! - `OnionMessages` - requires/supports forwarding onion messages
|
//! - `OnionMessages` - requires/supports forwarding onion messages
|
||||||
|
@ -60,10 +66,6 @@
|
||||||
//! for more info).
|
//! for more info).
|
||||||
//! - `Keysend` - send funds to a node without an invoice
|
//! - `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).
|
//! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) 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).
|
|
||||||
//!
|
//!
|
||||||
//! LDK knows about the following features, but does not support them:
|
//! LDK knows about the following features, but does not support them:
|
||||||
//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be
|
//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be
|
||||||
|
@ -143,7 +145,7 @@ mod sealed {
|
||||||
// Byte 2
|
// Byte 2
|
||||||
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
|
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
|
||||||
// Byte 3
|
// Byte 3
|
||||||
ShutdownAnySegwit | Taproot,
|
RouteBlinding | ShutdownAnySegwit | Taproot,
|
||||||
// Byte 4
|
// Byte 4
|
||||||
OnionMessages,
|
OnionMessages,
|
||||||
// Byte 5
|
// Byte 5
|
||||||
|
@ -159,7 +161,7 @@ mod sealed {
|
||||||
// Byte 2
|
// Byte 2
|
||||||
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
|
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
|
||||||
// Byte 3
|
// Byte 3
|
||||||
ShutdownAnySegwit | Taproot,
|
RouteBlinding | ShutdownAnySegwit | Taproot,
|
||||||
// Byte 4
|
// Byte 4
|
||||||
OnionMessages,
|
OnionMessages,
|
||||||
// Byte 5
|
// Byte 5
|
||||||
|
@ -391,6 +393,9 @@ mod sealed {
|
||||||
define_feature!(23, AnchorsZeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext],
|
define_feature!(23, AnchorsZeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext],
|
||||||
"Feature flags for `option_anchors_zero_fee_htlc_tx`.", set_anchors_zero_fee_htlc_tx_optional,
|
"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);
|
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],
|
define_feature!(27, ShutdownAnySegwit, [InitContext, NodeContext],
|
||||||
"Feature flags for `opt_shutdown_anysegwit`.", set_shutdown_any_segwit_optional,
|
"Feature flags for `opt_shutdown_anysegwit`.", set_shutdown_any_segwit_optional,
|
||||||
set_shutdown_any_segwit_required, supports_shutdown_anysegwit, requires_shutdown_anysegwit);
|
set_shutdown_any_segwit_required, supports_shutdown_anysegwit, requires_shutdown_anysegwit);
|
||||||
|
@ -1053,6 +1058,7 @@ mod tests {
|
||||||
init_features.set_basic_mpp_optional();
|
init_features.set_basic_mpp_optional();
|
||||||
init_features.set_wumbo_optional();
|
init_features.set_wumbo_optional();
|
||||||
init_features.set_anchors_zero_fee_htlc_tx_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_shutdown_any_segwit_optional();
|
||||||
init_features.set_onion_messages_optional();
|
init_features.set_onion_messages_optional();
|
||||||
init_features.set_channel_type_optional();
|
init_features.set_channel_type_optional();
|
||||||
|
@ -1068,8 +1074,8 @@ mod tests {
|
||||||
// Check that the flags are as expected:
|
// Check that the flags are as expected:
|
||||||
// - option_data_loss_protect (req)
|
// - option_data_loss_protect (req)
|
||||||
// - var_onion_optin (req) | static_remote_key (req) | payment_secret(req)
|
// - var_onion_optin (req) | static_remote_key (req) | payment_secret(req)
|
||||||
// - basic_mpp | wumbo | anchors_zero_fee_htlc_tx
|
// - basic_mpp | wumbo | option_anchors_zero_fee_htlc_tx
|
||||||
// - opt_shutdown_anysegwit
|
// - option_route_blinding | opt_shutdown_anysegwit
|
||||||
// - onion_messages
|
// - onion_messages
|
||||||
// - option_channel_type | option_scid_alias
|
// - option_channel_type | option_scid_alias
|
||||||
// - option_zeroconf
|
// - option_zeroconf
|
||||||
|
@ -1077,7 +1083,7 @@ mod tests {
|
||||||
assert_eq!(node_features.flags[0], 0b00000001);
|
assert_eq!(node_features.flags[0], 0b00000001);
|
||||||
assert_eq!(node_features.flags[1], 0b01010001);
|
assert_eq!(node_features.flags[1], 0b01010001);
|
||||||
assert_eq!(node_features.flags[2], 0b10001010);
|
assert_eq!(node_features.flags[2], 0b10001010);
|
||||||
assert_eq!(node_features.flags[3], 0b00001000);
|
assert_eq!(node_features.flags[3], 0b00001010);
|
||||||
assert_eq!(node_features.flags[4], 0b10000000);
|
assert_eq!(node_features.flags[4], 0b10000000);
|
||||||
assert_eq!(node_features.flags[5], 0b10100000);
|
assert_eq!(node_features.flags[5], 0b10100000);
|
||||||
assert_eq!(node_features.flags[6], 0b00001000);
|
assert_eq!(node_features.flags[6], 0b00001000);
|
||||||
|
|
|
@ -13,14 +13,14 @@ use crate::blinded_path::BlindedPath;
|
||||||
use crate::events::{Event, EventsProvider};
|
use crate::events::{Event, EventsProvider};
|
||||||
use crate::ln::features::InitFeatures;
|
use crate::ln::features::InitFeatures;
|
||||||
use crate::ln::msgs::{self, DecodeError, OnionMessageHandler, SocketAddress};
|
use crate::ln::msgs::{self, DecodeError, OnionMessageHandler, SocketAddress};
|
||||||
use crate::sign::{NodeSigner, Recipient};
|
use crate::sign::{EntropySource, NodeSigner, Recipient};
|
||||||
use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer};
|
use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer};
|
||||||
use crate::util::test_utils;
|
use crate::util::test_utils;
|
||||||
use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError};
|
use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError};
|
||||||
|
|
||||||
use bitcoin::network::constants::Network;
|
use bitcoin::network::constants::Network;
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
|
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self};
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::io_extras::read_to_end;
|
use crate::io_extras::read_to_end;
|
||||||
|
@ -55,6 +55,15 @@ impl MessageRouter for TestMessageRouter {
|
||||||
Some(vec![SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 1000 }]),
|
Some(vec![SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 1000 }]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _entropy_source: &ES,
|
||||||
|
_secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestOffersMessageHandler {}
|
struct TestOffersMessageHandler {}
|
||||||
|
|
|
@ -64,9 +64,9 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
|
||||||
/// # extern crate bitcoin;
|
/// # extern crate bitcoin;
|
||||||
/// # use bitcoin::hashes::_export::_core::time::Duration;
|
/// # use bitcoin::hashes::_export::_core::time::Duration;
|
||||||
/// # use bitcoin::hashes::hex::FromHex;
|
/// # use bitcoin::hashes::hex::FromHex;
|
||||||
/// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
|
/// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self};
|
||||||
/// # use lightning::blinded_path::BlindedPath;
|
/// # use lightning::blinded_path::BlindedPath;
|
||||||
/// # use lightning::sign::KeysManager;
|
/// # use lightning::sign::{EntropySource, KeysManager};
|
||||||
/// # use lightning::ln::peer_handler::IgnoringMessageHandler;
|
/// # use lightning::ln::peer_handler::IgnoringMessageHandler;
|
||||||
/// # use lightning::onion_message::{OnionMessageContents, Destination, MessageRouter, OnionMessagePath, OnionMessenger};
|
/// # use lightning::onion_message::{OnionMessageContents, Destination, MessageRouter, OnionMessagePath, OnionMessenger};
|
||||||
/// # use lightning::util::logger::{Logger, Record};
|
/// # use lightning::util::logger::{Logger, Record};
|
||||||
|
@ -90,6 +90,11 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
|
||||||
/// # first_node_addresses: None,
|
/// # first_node_addresses: None,
|
||||||
/// # })
|
/// # })
|
||||||
/// # }
|
/// # }
|
||||||
|
/// # fn create_blinded_paths<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
|
||||||
|
/// # &self, _recipient: PublicKey, _peers: Vec<PublicKey>, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
|
||||||
|
/// # ) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
/// # unreachable!()
|
||||||
|
/// # }
|
||||||
/// # }
|
/// # }
|
||||||
/// # let seed = [42u8; 32];
|
/// # let seed = [42u8; 32];
|
||||||
/// # let time = Duration::from_secs(123456);
|
/// # let time = Duration::from_secs(123456);
|
||||||
|
@ -270,6 +275,15 @@ pub trait MessageRouter {
|
||||||
fn find_path(
|
fn find_path(
|
||||||
&self, sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
|
&self, sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
|
||||||
) -> Result<OnionMessagePath, ()>;
|
) -> Result<OnionMessagePath, ()>;
|
||||||
|
|
||||||
|
/// Creates [`BlindedPath`]s to the `recipient` node. The nodes in `peers` are assumed to be
|
||||||
|
/// direct peers with the `recipient`.
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, recipient: PublicKey, peers: Vec<PublicKey>, entropy_source: &ES,
|
||||||
|
secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`MessageRouter`] that can only route to a directly connected [`Destination`].
|
/// A [`MessageRouter`] that can only route to a directly connected [`Destination`].
|
||||||
|
@ -321,6 +335,47 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, recipient: PublicKey, peers: Vec<PublicKey>, entropy_source: &ES,
|
||||||
|
secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
// Limit the number of blinded paths that are computed.
|
||||||
|
const MAX_PATHS: usize = 3;
|
||||||
|
|
||||||
|
// Ensure peers have at least three channels so that it is more difficult to infer the
|
||||||
|
// recipient's node_id.
|
||||||
|
const MIN_PEER_CHANNELS: usize = 3;
|
||||||
|
|
||||||
|
let network_graph = self.network_graph.deref().read_only();
|
||||||
|
let paths = peers.iter()
|
||||||
|
// Limit to peers with announced channels
|
||||||
|
.filter(|pubkey|
|
||||||
|
network_graph
|
||||||
|
.node(&NodeId::from_pubkey(pubkey))
|
||||||
|
.map(|info| &info.channels[..])
|
||||||
|
.map(|channels| channels.len() >= MIN_PEER_CHANNELS)
|
||||||
|
.unwrap_or(false)
|
||||||
|
)
|
||||||
|
.map(|pubkey| vec![*pubkey, recipient])
|
||||||
|
.map(|node_pks| BlindedPath::new_for_message(&node_pks, entropy_source, secp_ctx))
|
||||||
|
.take(MAX_PATHS)
|
||||||
|
.collect::<Result<Vec<_>, _>>();
|
||||||
|
|
||||||
|
match paths {
|
||||||
|
Ok(paths) if !paths.is_empty() => Ok(paths),
|
||||||
|
_ => {
|
||||||
|
if network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient)) {
|
||||||
|
BlindedPath::one_hop_for_message(recipient, entropy_source, secp_ctx)
|
||||||
|
.map(|path| vec![path])
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A path for sending an [`OnionMessage`].
|
/// A path for sending an [`OnionMessage`].
|
||||||
|
|
|
@ -9,18 +9,21 @@
|
||||||
|
|
||||||
//! The router finds paths within a [`NetworkGraph`] for a payment.
|
//! The router finds paths within a [`NetworkGraph`] for a payment.
|
||||||
|
|
||||||
use bitcoin::secp256k1::PublicKey;
|
use bitcoin::secp256k1::{PublicKey, Secp256k1, self};
|
||||||
use bitcoin::hashes::Hash;
|
use bitcoin::hashes::Hash;
|
||||||
use bitcoin::hashes::sha256::Hash as Sha256;
|
use bitcoin::hashes::sha256::Hash as Sha256;
|
||||||
|
|
||||||
use crate::blinded_path::{BlindedHop, BlindedPath};
|
use crate::blinded_path::{BlindedHop, BlindedPath};
|
||||||
|
use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
|
||||||
use crate::ln::PaymentHash;
|
use crate::ln::PaymentHash;
|
||||||
use crate::ln::channelmanager::{ChannelDetails, PaymentId};
|
use crate::ln::channelmanager::{ChannelDetails, PaymentId};
|
||||||
use crate::ln::features::{Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
|
use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
|
||||||
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
|
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
|
||||||
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
|
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
|
||||||
|
use crate::onion_message::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
|
||||||
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
|
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
|
||||||
use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp};
|
use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp};
|
||||||
|
use crate::sign::EntropySource;
|
||||||
use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer};
|
use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer};
|
||||||
use crate::util::logger::{Level, Logger};
|
use crate::util::logger::{Level, Logger};
|
||||||
use crate::util::chacha20::ChaCha20;
|
use crate::util::chacha20::ChaCha20;
|
||||||
|
@ -33,7 +36,7 @@ use core::{cmp, fmt};
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
/// A [`Router`] implemented using [`find_route`].
|
/// A [`Router`] implemented using [`find_route`].
|
||||||
pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> where
|
pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> where
|
||||||
L::Target: Logger,
|
L::Target: Logger,
|
||||||
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
|
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
|
||||||
{
|
{
|
||||||
|
@ -41,21 +44,23 @@ pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref,
|
||||||
logger: L,
|
logger: L,
|
||||||
random_seed_bytes: Mutex<[u8; 32]>,
|
random_seed_bytes: Mutex<[u8; 32]>,
|
||||||
scorer: S,
|
scorer: S,
|
||||||
score_params: SP
|
score_params: SP,
|
||||||
|
message_router: DefaultMessageRouter<G, L>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> DefaultRouter<G, L, S, SP, Sc> where
|
impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> DefaultRouter<G, L, S, SP, Sc> where
|
||||||
L::Target: Logger,
|
L::Target: Logger,
|
||||||
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
|
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
|
||||||
{
|
{
|
||||||
/// Creates a new router.
|
/// Creates a new router.
|
||||||
pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32], scorer: S, score_params: SP) -> Self {
|
pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32], scorer: S, score_params: SP) -> Self {
|
||||||
let random_seed_bytes = Mutex::new(random_seed_bytes);
|
let random_seed_bytes = Mutex::new(random_seed_bytes);
|
||||||
Self { network_graph, logger, random_seed_bytes, scorer, score_params }
|
let message_router = DefaultMessageRouter::new(network_graph.clone());
|
||||||
|
Self { network_graph, logger, random_seed_bytes, scorer, score_params, message_router }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl< G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> Router for DefaultRouter<G, L, S, SP, Sc> where
|
impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> Router for DefaultRouter<G, L, S, SP, Sc> where
|
||||||
L::Target: Logger,
|
L::Target: Logger,
|
||||||
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
|
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
|
||||||
{
|
{
|
||||||
|
@ -78,10 +83,109 @@ impl< G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: Sco
|
||||||
&random_seed_bytes
|
&random_seed_bytes
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_blinded_payment_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
|
||||||
|
amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
|
||||||
|
// Limit the number of blinded paths that are computed.
|
||||||
|
const MAX_PAYMENT_PATHS: usize = 3;
|
||||||
|
|
||||||
|
// Ensure peers have at least three channels so that it is more difficult to infer the
|
||||||
|
// recipient's node_id.
|
||||||
|
const MIN_PEER_CHANNELS: usize = 3;
|
||||||
|
|
||||||
|
let network_graph = self.network_graph.deref().read_only();
|
||||||
|
let paths = first_hops.into_iter()
|
||||||
|
.filter(|details| details.counterparty.features.supports_route_blinding())
|
||||||
|
.filter(|details| amount_msats <= details.inbound_capacity_msat)
|
||||||
|
.filter(|details| amount_msats >= details.inbound_htlc_minimum_msat.unwrap_or(0))
|
||||||
|
.filter(|details| amount_msats <= details.inbound_htlc_maximum_msat.unwrap_or(0))
|
||||||
|
.filter(|details| network_graph
|
||||||
|
.node(&NodeId::from_pubkey(&details.counterparty.node_id))
|
||||||
|
.map(|node_info| node_info.channels.len() >= MIN_PEER_CHANNELS)
|
||||||
|
.unwrap_or(false)
|
||||||
|
)
|
||||||
|
.filter_map(|details| {
|
||||||
|
let short_channel_id = match details.get_inbound_payment_scid() {
|
||||||
|
Some(short_channel_id) => short_channel_id,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
let payment_relay: PaymentRelay = match details.counterparty.forwarding_info {
|
||||||
|
Some(forwarding_info) => forwarding_info.into(),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Avoid exposing esoteric CLTV expiry deltas
|
||||||
|
let cltv_expiry_delta = match payment_relay.cltv_expiry_delta {
|
||||||
|
0..=40 => 40u32,
|
||||||
|
41..=80 => 80u32,
|
||||||
|
81..=144 => 144u32,
|
||||||
|
145..=216 => 216u32,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let payment_constraints = PaymentConstraints {
|
||||||
|
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
|
||||||
|
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
|
||||||
|
};
|
||||||
|
Some(ForwardNode {
|
||||||
|
tlvs: ForwardTlvs {
|
||||||
|
short_channel_id,
|
||||||
|
payment_relay,
|
||||||
|
payment_constraints,
|
||||||
|
features: BlindedHopFeatures::empty(),
|
||||||
|
},
|
||||||
|
node_id: details.counterparty.node_id,
|
||||||
|
htlc_maximum_msat: details.inbound_htlc_maximum_msat.unwrap_or(0),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(|forward_node| {
|
||||||
|
BlindedPath::new_for_payment(
|
||||||
|
&[forward_node], recipient, tlvs.clone(), u64::MAX, entropy_source, secp_ctx
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.take(MAX_PAYMENT_PATHS)
|
||||||
|
.collect::<Result<Vec<_>, _>>();
|
||||||
|
|
||||||
|
match paths {
|
||||||
|
Ok(paths) if !paths.is_empty() => Ok(paths),
|
||||||
|
_ => {
|
||||||
|
if network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient)) {
|
||||||
|
BlindedPath::one_hop_for_payment(recipient, tlvs, entropy_source, secp_ctx)
|
||||||
|
.map(|path| vec![path])
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl< G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> MessageRouter for DefaultRouter<G, L, S, SP, Sc> where
|
||||||
|
L::Target: Logger,
|
||||||
|
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
|
||||||
|
{
|
||||||
|
fn find_path(
|
||||||
|
&self, sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
|
||||||
|
) -> Result<OnionMessagePath, ()> {
|
||||||
|
self.message_router.find_path(sender, peers, destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, recipient: PublicKey, peers: Vec<PublicKey>, entropy_source: &ES,
|
||||||
|
secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
self.message_router.create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait defining behavior for routing a payment.
|
/// A trait defining behavior for routing a payment.
|
||||||
pub trait Router {
|
pub trait Router: MessageRouter {
|
||||||
/// Finds a [`Route`] for a payment between the given `payer` and a payee.
|
/// Finds a [`Route`] for a payment between the given `payer` and a payee.
|
||||||
///
|
///
|
||||||
/// The `payee` and the payment's value are given in [`RouteParameters::payment_params`]
|
/// The `payee` and the payment's value are given in [`RouteParameters::payment_params`]
|
||||||
|
@ -105,6 +209,16 @@ pub trait Router {
|
||||||
) -> Result<Route, LightningError> {
|
) -> Result<Route, LightningError> {
|
||||||
self.find_route(payer, route_params, first_hops, inflight_htlcs)
|
self.find_route(payer, route_params, first_hops, inflight_htlcs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates [`BlindedPath`]s for payment to the `recipient` node. The channels in `first_hops`
|
||||||
|
/// are assumed to be with the `recipient`'s peers. The payment secret and any constraints are
|
||||||
|
/// given in `tlvs`.
|
||||||
|
fn create_blinded_payment_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
|
||||||
|
amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`ScoreLookUp`] implementation that factors in in-flight HTLC liquidity.
|
/// [`ScoreLookUp`] implementation that factors in in-flight HTLC liquidity.
|
||||||
|
|
|
@ -3442,7 +3442,7 @@ mod tests {
|
||||||
Some(([0; 32], [0; 32])));
|
Some(([0; 32], [0; 32])));
|
||||||
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 1, ¶ms), None);
|
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 1, ¶ms), None);
|
||||||
|
|
||||||
let mut usage = ChannelUsage {
|
let usage = ChannelUsage {
|
||||||
amount_msat: 100,
|
amount_msat: 100,
|
||||||
inflight_htlc_msat: 1024,
|
inflight_htlc_msat: 1024,
|
||||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
|
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
// You may not use this file except in accordance with one or both of these
|
// You may not use this file except in accordance with one or both of these
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
|
use crate::blinded_path::BlindedPath;
|
||||||
|
use crate::blinded_path::payment::ReceiveTlvs;
|
||||||
use crate::chain;
|
use crate::chain;
|
||||||
use crate::chain::WatchedOutput;
|
use crate::chain::WatchedOutput;
|
||||||
use crate::chain::chaininterface;
|
use crate::chain::chaininterface;
|
||||||
|
@ -22,14 +24,15 @@ use crate::sign;
|
||||||
use crate::events;
|
use crate::events;
|
||||||
use crate::events::bump_transaction::{WalletSource, Utxo};
|
use crate::events::bump_transaction::{WalletSource, Utxo};
|
||||||
use crate::ln::ChannelId;
|
use crate::ln::ChannelId;
|
||||||
use crate::ln::channelmanager;
|
use crate::ln::channelmanager::{ChannelDetails, self};
|
||||||
use crate::ln::chan_utils::CommitmentTransaction;
|
use crate::ln::chan_utils::CommitmentTransaction;
|
||||||
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
|
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
|
||||||
use crate::ln::{msgs, wire};
|
use crate::ln::{msgs, wire};
|
||||||
use crate::ln::msgs::LightningError;
|
use crate::ln::msgs::LightningError;
|
||||||
use crate::ln::script::ShutdownScript;
|
use crate::ln::script::ShutdownScript;
|
||||||
use crate::offers::invoice::UnsignedBolt12Invoice;
|
use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
|
||||||
use crate::offers::invoice_request::UnsignedInvoiceRequest;
|
use crate::offers::invoice_request::UnsignedInvoiceRequest;
|
||||||
|
use crate::onion_message::{Destination, MessageRouter, OnionMessagePath};
|
||||||
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
|
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
|
||||||
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
|
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
|
||||||
use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs};
|
use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs};
|
||||||
|
@ -51,7 +54,7 @@ use bitcoin::network::constants::Network;
|
||||||
use bitcoin::hash_types::{BlockHash, Txid};
|
use bitcoin::hash_types::{BlockHash, Txid};
|
||||||
use bitcoin::sighash::{SighashCache, EcdsaSighashType};
|
use bitcoin::sighash::{SighashCache, EcdsaSighashType};
|
||||||
|
|
||||||
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
|
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, self};
|
||||||
use bitcoin::secp256k1::ecdh::SharedSecret;
|
use bitcoin::secp256k1::ecdh::SharedSecret;
|
||||||
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
|
||||||
use bitcoin::secp256k1::schnorr;
|
use bitcoin::secp256k1::schnorr;
|
||||||
|
@ -119,7 +122,7 @@ impl<'a> TestRouter<'a> {
|
||||||
|
|
||||||
impl<'a> Router for TestRouter<'a> {
|
impl<'a> Router for TestRouter<'a> {
|
||||||
fn find_route(
|
fn find_route(
|
||||||
&self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&channelmanager::ChannelDetails]>,
|
&self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>,
|
||||||
inflight_htlcs: InFlightHtlcs
|
inflight_htlcs: InFlightHtlcs
|
||||||
) -> Result<Route, msgs::LightningError> {
|
) -> Result<Route, msgs::LightningError> {
|
||||||
if let Some((find_route_query, find_route_res)) = self.next_routes.lock().unwrap().pop_front() {
|
if let Some((find_route_query, find_route_res)) = self.next_routes.lock().unwrap().pop_front() {
|
||||||
|
@ -189,6 +192,32 @@ impl<'a> Router for TestRouter<'a> {
|
||||||
&[42; 32]
|
&[42; 32]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_blinded_payment_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
|
||||||
|
_amount_msats: u64, _entropy_source: &ES, _secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageRouter for TestRouter<'a> {
|
||||||
|
fn find_path(
|
||||||
|
&self, _sender: PublicKey, _peers: Vec<PublicKey>, _destination: Destination
|
||||||
|
) -> Result<OnionMessagePath, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_blinded_paths<
|
||||||
|
ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
|
||||||
|
>(
|
||||||
|
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _entropy_source: &ES,
|
||||||
|
_secp_ctx: &Secp256k1<T>
|
||||||
|
) -> Result<Vec<BlindedPath>, ()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for TestRouter<'a> {
|
impl<'a> Drop for TestRouter<'a> {
|
||||||
|
|
Loading…
Add table
Reference in a new issue