-
Notifications
You must be signed in to change notification settings - Fork 414
Enable Creation of Offers and Refunds Without Blinded Path #3246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b00ec2f
71dcf21
98b2e9b
76f90b4
f8645d0
9b29554
594bb95
843b5ce
65401b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -159,7 +159,9 @@ mod test { | |
use bitcoin::secp256k1::{self, PublicKey, Secp256k1}; | ||
use bitcoin::Block; | ||
|
||
use lightning::blinded_path::message::{BlindedMessagePath, MessageContext}; | ||
use lightning::blinded_path::message::{ | ||
BlindedMessagePath, MessageContext, MessageForwardNode, | ||
}; | ||
use lightning::blinded_path::NodeIdLookUp; | ||
use lightning::events::{Event, PaymentPurpose}; | ||
use lightning::ln::channelmanager::{PaymentId, Retry}; | ||
|
@@ -230,7 +232,7 @@ mod test { | |
} | ||
|
||
fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>( | ||
&self, recipient: PublicKey, context: MessageContext, _peers: Vec<PublicKey>, | ||
&self, recipient: PublicKey, context: MessageContext, _peers: Vec<MessageForwardNode>, | ||
secp_ctx: &Secp256k1<T>, | ||
) -> Result<Vec<BlindedMessagePath>, ()> { | ||
let keys = KeysManager::new(&[0; 32], 42, 43); | ||
|
@@ -454,7 +456,7 @@ mod test { | |
#[tokio::test] | ||
async fn end_to_end_test() { | ||
let chanmon_cfgs = create_chanmon_cfgs(2); | ||
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); | ||
let node_cfgs = create_node_cfgs_with_node_id_message_router(2, &chanmon_cfgs); | ||
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); | ||
let nodes = create_network(2, &node_cfgs, &node_chanmgrs); | ||
|
||
|
@@ -480,7 +482,7 @@ mod test { | |
|
||
let name = HumanReadableName::from_encoded("[email protected]").unwrap(); | ||
|
||
let bs_offer = nodes[1].node.create_offer_builder(None).unwrap().build().unwrap(); | ||
let bs_offer = nodes[1].node.create_offer_builder().unwrap().build().unwrap(); | ||
let resolvers = vec![Destination::Node(resolver_id)]; | ||
let retry = Retry::Attempts(0); | ||
let amt = 42_000; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2212,9 +2212,8 @@ where | |
/// # | ||
/// # fn example<T: AChannelManager>(channel_manager: T) -> Result<(), Bolt12SemanticError> { | ||
/// # let channel_manager = channel_manager.get_cm(); | ||
/// # let absolute_expiry = None; | ||
/// let offer = channel_manager | ||
/// .create_offer_builder(absolute_expiry)? | ||
/// .create_offer_builder()? | ||
/// # ; | ||
/// # // Needed for compiling for c_bindings | ||
/// # let builder: lightning::offers::offer::OfferBuilder<_, _> = offer.into(); | ||
|
@@ -3022,9 +3021,7 @@ const MAX_NO_CHANNEL_PEERS: usize = 250; | |
/// short-lived, while anything with a greater expiration is considered long-lived. | ||
/// | ||
/// Using [`ChannelManager::create_offer_builder`] or [`ChannelManager::create_refund_builder`], | ||
/// will included a [`BlindedMessagePath`] created using: | ||
/// - [`MessageRouter::create_compact_blinded_paths`] when short-lived, and | ||
/// - [`MessageRouter::create_blinded_paths`] when long-lived. | ||
/// will include a [`BlindedMessagePath`] created using [`MessageRouter::create_blinded_paths`] | ||
/// | ||
/// Using compact [`BlindedMessagePath`]s may provide better privacy as the [`MessageRouter`] could select | ||
/// more hops. However, since they use short channel ids instead of pubkeys, they are more likely to | ||
|
@@ -10852,10 +10849,9 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { | |
/// | ||
/// # Privacy | ||
/// | ||
/// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the offer based on the given | ||
/// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for | ||
/// privacy implications as well as those of the parameterized [`Router`], which implements | ||
/// [`MessageRouter`]. | ||
/// Uses the [`ChannelManager`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`] for | ||
/// the offer. See those docs for privacy implications as well as those of the parameterized | ||
/// [`Router`], which implements [`MessageRouter`]. | ||
Comment on lines
-10855
to
+10854
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
/// | ||
/// Also, uses a derived signing pubkey in the offer for recipient privacy. | ||
/// | ||
|
@@ -10870,12 +10866,47 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { | |
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath | ||
/// [`Offer`]: crate::offers::offer::Offer | ||
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest | ||
pub fn create_offer_builder( | ||
&$self, absolute_expiry: Option<Duration> | ||
) -> Result<$builder, Bolt12SemanticError> { | ||
let entropy = &*$self.entropy_source; | ||
pub fn create_offer_builder(&$self) -> Result<$builder, Bolt12SemanticError> { | ||
let builder = $self.flow.create_offer_builder( | ||
&*$self.entropy_source, $self.get_peers_for_blinded_path() | ||
)?; | ||
|
||
let builder = $self.flow.create_offer_builder(entropy, absolute_expiry, $self.get_peers_for_blinded_path())?; | ||
Ok(builder.into()) | ||
} | ||
|
||
/// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the | ||
/// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. | ||
/// | ||
/// # Privacy | ||
/// | ||
/// Constructs a [`BlindedMessagePath`] for the offer using a custom [`MessageRouter`]. | ||
/// Users can implement a custom [`MessageRouter`] to define properties of the | ||
/// [`BlindedMessagePath`] as required or opt not to create any `BlindedMessagePath`. | ||
/// | ||
/// Also, uses a derived signing pubkey in the offer for recipient privacy. | ||
/// | ||
/// # Limitations | ||
/// | ||
/// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: looks like for the above we transitioned this to "See [ |
||
/// reply path. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Errors if the provided [`MessageRouter`] is unable to create a blinded path for the offer. | ||
/// | ||
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath | ||
/// [`Offer`]: crate::offers::offer::Offer | ||
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest | ||
pub fn create_offer_builder_using_router<ME: Deref>( | ||
&$self, | ||
router: ME, | ||
) -> Result<$builder, Bolt12SemanticError> | ||
where | ||
ME::Target: MessageRouter, | ||
{ | ||
let builder = $self.flow.create_offer_builder_using_router( | ||
router, &*$self.entropy_source, $self.get_peers_for_blinded_path() | ||
)?; | ||
|
||
Ok(builder.into()) | ||
} | ||
|
@@ -10904,10 +10935,8 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { | |
/// | ||
/// # Privacy | ||
/// | ||
/// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given | ||
/// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for | ||
/// privacy implications as well as those of the parameterized [`Router`], which implements | ||
/// [`MessageRouter`]. | ||
/// Uses the [`ChannelManager`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`] | ||
/// for the offer. See those docs for privacy implications. | ||
/// | ||
/// Also, uses a derived payer id in the refund for payer privacy. | ||
/// | ||
|
@@ -10945,6 +10974,77 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { | |
|
||
Ok(builder.into()) | ||
} | ||
|
||
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the | ||
/// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund. | ||
/// | ||
/// # Payment | ||
/// | ||
/// The provided `payment_id` is used to ensure that only one invoice is paid for the refund. | ||
/// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent. | ||
/// | ||
/// The builder will have the provided expiration set. Any changes to the expiration on the | ||
/// returned builder will not be honored by [`ChannelManager`]. For non-`std`, the highest seen | ||
/// block time minus two hours is used for the current time when determining if the refund has | ||
/// expired. | ||
/// | ||
/// To revoke the refund, use [`ChannelManager::abandon_payment`] prior to receiving the | ||
/// invoice. If abandoned, or an invoice isn't received before expiration, the payment will fail | ||
/// with an [`Event::PaymentFailed`]. | ||
/// | ||
/// If `max_total_routing_fee_msat` is not specified, The default from | ||
/// [`RouteParameters::from_payment_params_and_value`] is applied. | ||
/// | ||
/// # Privacy | ||
/// | ||
/// Constructs a [`BlindedMessagePath`] for the refund using a custom [`MessageRouter`]. | ||
/// Users can implement a custom [`MessageRouter`] to define properties of the | ||
/// [`BlindedMessagePath`] as required or opt not to create any `BlindedMessagePath`. | ||
/// | ||
/// Also, uses a derived payer id in the refund for payer privacy. | ||
/// | ||
/// # Limitations | ||
/// | ||
/// Requires a direct connection to an introduction node in the responding | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, though this was removed entirely from |
||
/// [`Bolt12Invoice::payment_paths`]. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Errors if: | ||
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link, | ||
/// - `amount_msats` is invalid, or | ||
/// - the provided [`MessageRouter`] is unable to create a blinded path for the refund. | ||
/// | ||
/// [`Refund`]: crate::offers::refund::Refund | ||
/// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath | ||
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice | ||
/// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths | ||
/// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments | ||
pub fn create_refund_builder_using_router<ME: Deref>( | ||
&$self, router: ME, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId, | ||
retry_strategy: Retry, route_params_config: RouteParametersConfig | ||
) -> Result<$builder, Bolt12SemanticError> | ||
where | ||
ME::Target: MessageRouter, | ||
{ | ||
let entropy = &*$self.entropy_source; | ||
|
||
let builder = $self.flow.create_refund_builder_using_router( | ||
router, entropy, amount_msats, absolute_expiry, | ||
payment_id, $self.get_peers_for_blinded_path() | ||
)?; | ||
|
||
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self); | ||
|
||
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry); | ||
$self.pending_outbound_payments | ||
.add_new_awaiting_invoice( | ||
payment_id, expiration, retry_strategy, route_params_config, None, | ||
) | ||
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; | ||
|
||
Ok(builder.into()) | ||
} | ||
} } | ||
|
||
impl< | ||
|
@@ -17077,7 +17177,7 @@ pub mod bench { | |
let scorer = RwLock::new(test_utils::TestScorer::new()); | ||
let entropy = test_utils::TestKeysInterface::new(&[0u8; 32], network); | ||
let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &logger_a, &scorer); | ||
let message_router = test_utils::TestMessageRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &entropy); | ||
let message_router = test_utils::TestMessageRouter::new_default(Arc::new(NetworkGraph::new(network, &logger_a)), &entropy); | ||
|
||
let mut config: UserConfig = Default::default(); | ||
config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,7 +38,7 @@ use crate::util::logger::Logger; | |
use crate::util::scid_utils; | ||
use crate::util::test_channel_signer::TestChannelSigner; | ||
use crate::util::test_channel_signer::SignerOp; | ||
use crate::util::test_utils; | ||
use crate::util::test_utils::{self, TestLogger}; | ||
use crate::util::test_utils::{TestChainMonitor, TestScorer, TestKeysInterface}; | ||
use crate::util::ser::{ReadableArgs, Writeable}; | ||
|
||
|
@@ -728,7 +728,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { | |
signer_provider: self.keys_manager, | ||
fee_estimator: &test_utils::TestFeeEstimator::new(253), | ||
router: &test_utils::TestRouter::new(Arc::clone(&network_graph), &self.logger, &scorer), | ||
message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager), | ||
message_router: &test_utils::TestMessageRouter::new_default(network_graph, self.keys_manager), | ||
chain_monitor: self.chain_monitor, | ||
tx_broadcaster: &broadcaster, | ||
logger: &self.logger, | ||
|
@@ -3345,26 +3345,39 @@ pub fn create_chanmon_cfgs_with_keys(node_count: usize, predefined_keys_ids: Opt | |
chan_mon_cfgs | ||
} | ||
|
||
pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec<TestChanMonCfg>) -> Vec<NodeCfg<'a>> { | ||
create_node_cfgs_with_persisters(node_count, chanmon_cfgs, chanmon_cfgs.iter().map(|c| &c.persister).collect()) | ||
} | ||
|
||
pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a Vec<TestChanMonCfg>, persisters: Vec<&'a impl test_utils::SyncPersist>) -> Vec<NodeCfg<'a>> { | ||
fn create_node_cfgs_internal<'a, F>( | ||
node_count: usize, | ||
chanmon_cfgs: &'a Vec<TestChanMonCfg>, | ||
persisters: Vec<&'a impl test_utils::SyncPersist>, | ||
message_router_constructor: F, | ||
) -> Vec<NodeCfg<'a>> | ||
where | ||
F: Fn(Arc<NetworkGraph<&'a TestLogger>>, &'a TestKeysInterface) -> test_utils::TestMessageRouter<'a>, | ||
{ | ||
let mut nodes = Vec::new(); | ||
|
||
for i in 0..node_count { | ||
let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[i].chain_source), &chanmon_cfgs[i].tx_broadcaster, &chanmon_cfgs[i].logger, &chanmon_cfgs[i].fee_estimator, persisters[i], &chanmon_cfgs[i].keys_manager); | ||
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[i].logger)); | ||
let cfg = &chanmon_cfgs[i]; | ||
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &cfg.logger)); | ||
let chain_monitor = test_utils::TestChainMonitor::new( | ||
Some(&cfg.chain_source), | ||
&cfg.tx_broadcaster, | ||
&cfg.logger, | ||
&cfg.fee_estimator, | ||
persisters[i], | ||
&cfg.keys_manager, | ||
); | ||
|
||
let seed = [i as u8; 32]; | ||
nodes.push(NodeCfg { | ||
chain_source: &chanmon_cfgs[i].chain_source, | ||
logger: &chanmon_cfgs[i].logger, | ||
tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster, | ||
fee_estimator: &chanmon_cfgs[i].fee_estimator, | ||
router: test_utils::TestRouter::new(Arc::clone(&network_graph), &chanmon_cfgs[i].logger, &chanmon_cfgs[i].scorer), | ||
message_router: test_utils::TestMessageRouter::new(Arc::clone(&network_graph), &chanmon_cfgs[i].keys_manager), | ||
chain_source: &cfg.chain_source, | ||
logger: &cfg.logger, | ||
tx_broadcaster: &cfg.tx_broadcaster, | ||
fee_estimator: &cfg.fee_estimator, | ||
router: test_utils::TestRouter::new(Arc::clone(&network_graph), &cfg.logger, &cfg.scorer), | ||
message_router: message_router_constructor(Arc::clone(&network_graph), &cfg.keys_manager), | ||
chain_monitor, | ||
keys_manager: &chanmon_cfgs[i].keys_manager, | ||
keys_manager: &cfg.keys_manager, | ||
node_seed: seed, | ||
network_graph, | ||
override_init_features: Rc::new(RefCell::new(None)), | ||
|
@@ -3374,6 +3387,33 @@ pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a | |
nodes | ||
} | ||
|
||
pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec<TestChanMonCfg>) -> Vec<NodeCfg<'a>> { | ||
let persisters = chanmon_cfgs.iter().map(|c| &c.persister).collect(); | ||
create_node_cfgs_internal(node_count, chanmon_cfgs, persisters, |graph, keys_manager| { | ||
test_utils::TestMessageRouter::new_default(graph, keys_manager) | ||
}) | ||
} | ||
|
||
pub fn create_node_cfgs_with_persisters<'a>( | ||
node_count: usize, | ||
chanmon_cfgs: &'a Vec<TestChanMonCfg>, | ||
persisters: Vec<&'a impl test_utils::SyncPersist>, | ||
) -> Vec<NodeCfg<'a>> { | ||
create_node_cfgs_internal(node_count, chanmon_cfgs, persisters, |graph, keys_manager| { | ||
test_utils::TestMessageRouter::new_default(graph, keys_manager) | ||
}) | ||
} | ||
|
||
pub fn create_node_cfgs_with_node_id_message_router<'a>( | ||
node_count: usize, | ||
chanmon_cfgs: &'a Vec<TestChanMonCfg>, | ||
) -> Vec<NodeCfg<'a>> { | ||
let persisters = chanmon_cfgs.iter().map(|c| &c.persister).collect(); | ||
create_node_cfgs_internal(node_count, chanmon_cfgs, persisters, |graph, keys_manager| { | ||
test_utils::TestMessageRouter::new_node_id_router(graph, keys_manager) | ||
}) | ||
} | ||
Comment on lines
+3390
to
+3415
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: For these, you don't need to pass a closure because the constructors take the same arguments as the expected closure type. So just pass |
||
|
||
pub fn test_default_channel_config() -> UserConfig { | ||
let mut default_config = UserConfig::default(); | ||
// Set cltv_expiry_delta slightly lower to keep the final CLTV values inside one byte in our | ||
|
Uh oh!
There was an error while loading. Please reload this page.