lightning/target
lightning/ldk-net_graph-*.bin
lightning-custom-message/target
+lightning-transaction-sync/target
no-std-check/target
+# 0.0.115 - Apr 24, 2023 - "Rebroadcast the Bugfixes"
+
+## API Updates
+ * The MSRV of the main LDK crates has been increased to 1.48 (#2107).
+ * Attempting to claim an un-expired payment on a channel which has closed no
+ longer fails. The expiry time of payments is exposed via
+ `PaymentClaimable::claim_deadline` (#2148).
+ * `payment_metadata` is now supported in `Invoice` deserialization, sending,
+ and receiving (via a new `RecipientOnionFields` struct) (#2139, #2127).
+ * `Event::PaymentFailed` now exposes a failure reason (#2142).
+ * BOLT12 messages now support stateless generation and validation (#1989).
+ * The `NetworkGraph` is now pruned of stale data after RGS processing (#2161).
+ * Max inbound HTLCs in-flight can be changed in the handshake config (#2138).
+ * `lightning-transaction-sync` feature `esplora-async-https` was added (#2085).
+ * A `ChannelPending` event is now emitted after the initial handshake (#2098).
+ * `PaymentForwarded::outbound_amount_forwarded_msat` was added (#2136).
+ * `ChannelManager::list_channels_by_counterparty` was added (#2079).
+ * `ChannelDetails::feerate_sat_per_1000_weight` was added (#2094).
+ * `Invoice::fallback_addresses` was added to fetch `bitcoin` types (#2023).
+ * The offer/refund description is now exposed in `Invoice{,Request}` (#2206).
+
+## Backwards Compatibility
+ * Payments sent with the legacy `*_with_route` methods on LDK 0.0.115+ will no
+ longer be retryable via the LDK 0.0.114- `retry_payment` method (#2139).
+ * `Event::PaymentPathFailed::retry` was removed and will always be `None` for
+ payments initiated on 0.0.115 which fail on an earlier version (#2063).
+ * `Route`s and `PaymentParameters` with blinded path information will not be
+ readable on prior versions of LDK. Such objects are not currently constructed
+ by LDK, but may be when processing BOLT12 data in a coming release (#2146).
+ * Providing `ChannelMonitorUpdate`s generated by LDK 0.0.115 to a
+ `ChannelMonitor` on 0.0.114 or before may panic (#2059). Note that this is
+ in general unsupported, and included here only for completeness.
+
+## Bug Fixes
+ * Fixed a case where `process_events_async` may `poll` a `Future` which has
+ already completed (#2081).
+ * Fixed deserialization of `u16` arrays. This bug may have previously corrupted
+ the historical buckets in a `ProbabilisticScorer`. Users relying on the
+ historical buckets may wish to wipe their scorer on upgrade to remove corrupt
+ data rather than waiting on it to decay (#2191).
+ * The `process_events_async` task is now `Send` and can thus be polled on a
+ multi-threaded runtime (#2199).
+ * Fixed a missing macro export causing
+ `impl_writeable_tlv_based_enum{,_upgradable}` calls to not compile (#2091).
+ * Fixed compilation of `lightning-invoice` with both `no-std` and serde (#2187)
+ * Fix an issue where the `background-processor` would not wake when a
+ `ChannelMonitorUpdate` completed asynchronously, causing delays (#2090).
+ * Fix an issue where `process_events_async` would exit immediately (#2145).
+ * `Router` calls from the `ChannelManager` now call `find_route_with_id` rather
+ than `find_route`, as was intended and described in the API (#2092).
+ * Ensure `process_events_async` always exits if any sleep future returns true,
+ not just if all sleep futures repeatedly return true (#2145).
+ * `channel_update` messages no longer set the disable bit unless the peer has
+ been disconnected for some time. This should resolve cases where channels are
+ disabled for extended periods of time (#2198).
+ * We no longer remove CLN nodes from the network graph for violating the BOLT
+ spec in some cases after failing to pay through them (#2220).
+ * Fixed a debug assertion which may panic under heavy load (#2172).
+ * `CounterpartyForceClosed::peer_msg` is now wrapped in UntrustedString (#2114)
+ * Fixed a potential deadlock in `funding_transaction_generated` (#2158).
+
+## Security
+ * Transaction re-broadcasting is now substantially more aggressive, including a
+ new regular rebroadcast feature called on a timer from the
+ `background-processor` or from `ChainMonitor::rebroadcast_pending_claims`.
+ This should substantially increase transaction confirmation reliability
+ without relying on downstream `TransactionBroadcaster` implementations for
+ rebroadcasting (#2203, #2205, #2208).
+ * Implemented the changes from BOLT PRs #1031, #1032, and #1040 which resolve a
+ privacy vulnerability which allows an intermediate node on the path to
+ discover the final destination for a payment (#2062).
+
+In total, this release features 110 files changed, 11928 insertions, 6368
+deletions in 215 commits from 21 authors, in alphabetical order:
+ * Advait
+ * Alan Cohen
+ * Alec Chen
+ * Allan Douglas R. de Oliveira
+ * Arik Sosman
+ * Elias Rohrer
+ * Evan Feenstra
+ * Jeffrey Czyz
+ * John Cantrell
+ * Lucas Soriano del Pino
+ * Marc Tyndel
+ * Matt Corallo
+ * Paul Miller
+ * Steven
+ * Steven Williamson
+ * Steven Zhao
+ * Tony Giorgio
+ * Valentine Wallace
+ * Wilmer Paulino
+ * benthecarman
+ * munjesi
+
+
# 0.0.114 - Mar 3, 2023 - "Faster Async BOLT12 Retries"
## API Updates
GEN_TEST msg_pong msg_targets::
GEN_TEST msg_channel_details msg_targets::
+
+GEN_TEST msg_open_channel_v2 msg_targets::
+GEN_TEST msg_accept_channel_v2 msg_targets::
+GEN_TEST msg_tx_add_input msg_targets::
+GEN_TEST msg_tx_add_output msg_targets::
+GEN_TEST msg_tx_remove_input msg_targets::
+GEN_TEST msg_tx_remove_output msg_targets::
+GEN_TEST msg_tx_complete msg_targets::
+GEN_TEST msg_tx_signatures msg_targets::
+GEN_TEST msg_tx_init_rbf msg_targets::
+GEN_TEST msg_tx_ack_rbf msg_targets::
+GEN_TEST msg_tx_abort msg_targets::
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_accept_channel_v2::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_accept_channel_v2_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_accept_channel_v2_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_accept_channel_v2_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_accept_channel_v2_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_accept_channel_v2_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_accept_channel_v2") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_accept_channel_v2_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_open_channel_v2::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_open_channel_v2_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_open_channel_v2_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_open_channel_v2_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_open_channel_v2_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_open_channel_v2_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_open_channel_v2") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_open_channel_v2_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_abort::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_abort_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_abort_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_abort_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_abort_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_abort_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_abort") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_abort_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_ack_rbf::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_ack_rbf_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_ack_rbf_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_ack_rbf_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_ack_rbf_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_ack_rbf_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_ack_rbf") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_ack_rbf_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_add_input::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_add_input_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_add_input_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_add_input_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_add_input_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_add_input_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_add_input") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_add_input_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_add_output::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_add_output_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_add_output_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_add_output_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_add_output_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_add_output_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_add_output") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_add_output_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_complete::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_complete_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_complete_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_complete_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_complete_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_complete_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_complete") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_complete_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_init_rbf::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_init_rbf_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_init_rbf_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_init_rbf_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_init_rbf_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_init_rbf_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_init_rbf") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_init_rbf_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_remove_input::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_remove_input_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_remove_input_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_remove_input_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_remove_input_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_remove_input_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_remove_input") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_remove_input_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_remove_output::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_remove_output_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_remove_output_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_remove_output_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_remove_output_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_remove_output_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_remove_output") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_remove_output_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::msg_targets::msg_tx_signatures::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+ fuzz!(|data| {
+ msg_tx_signatures_run(data.as_ptr(), data.len());
+ });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+ loop {
+ fuzz!(|data| {
+ msg_tx_signatures_run(data.as_ptr(), data.len());
+ });
+ }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+ msg_tx_signatures_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+ use std::io::Read;
+
+ let mut data = Vec::with_capacity(8192);
+ std::io::stdin().read_to_end(&mut data).unwrap();
+ msg_tx_signatures_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+ use std::fs;
+ use std::io::Read;
+ use lightning_fuzz::utils::test_logger::StringBuffer;
+
+ use std::sync::{atomic, Arc};
+ {
+ let data: Vec<u8> = vec![0];
+ msg_tx_signatures_run(data.as_ptr(), data.len());
+ }
+ let mut threads = Vec::new();
+ let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+ if let Ok(tests) = fs::read_dir("test_cases/msg_tx_signatures") {
+ for test in tests {
+ let mut data: Vec<u8> = Vec::new();
+ let path = test.unwrap().path();
+ fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+ threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+ let thread_count_ref = Arc::clone(&threads_running);
+ let main_thread_ref = std::thread::current();
+ threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+ std::thread::spawn(move || {
+ let string_logger = StringBuffer::new();
+
+ let panic_logger = string_logger.clone();
+ let res = if ::std::panic::catch_unwind(move || {
+ msg_tx_signatures_test(&data, panic_logger);
+ }).is_err() {
+ Some(string_logger.into_string())
+ } else { None };
+ thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+ main_thread_ref.unpark();
+ res
+ })
+ ));
+ while threads_running.load(atomic::Ordering::Acquire) > 32 {
+ std::thread::park();
+ }
+ }
+ }
+ let mut failed_outputs = Vec::new();
+ for (test, thread) in threads.drain(..) {
+ if let Some(output) = thread.join().unwrap() {
+ println!("\nOutput of {}:\n{}\n", test, output);
+ failed_outputs.push(test);
+ }
+ }
+ if !failed_outputs.is_empty() {
+ println!("Test cases which failed: ");
+ for case in failed_outputs {
+ println!("{}", case);
+ }
+ panic!();
+ }
+}
//! send-side handling is correct, other peers. We consider it a failure if any action results in a
//! channel being force-closed.
-use bitcoin::TxMerkleNode;
-use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::blockdata::script::{Builder, Script};
use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent};
use lightning::chain::transaction::OutPoint;
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
-use lightning::chain::keysinterface::{KeyMaterial, InMemorySigner, Recipient, EntropySource, NodeSigner, SignerProvider};
+use lightning::sign::{KeyMaterial, InMemorySigner, Recipient, EntropySource, NodeSigner, SignerProvider};
use lightning::events;
use lightning::events::MessageSendEventsProvider;
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
use lightning::ln::script::ShutdownScript;
+use lightning::ln::functional_test_utils::*;
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use lightning::util::errors::APIError;
use lightning::util::logger::Logger;
})
}
- fn get_destination_script(&self) -> Script {
+ fn get_destination_script(&self) -> Result<Script, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_monitor_claim_key = SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, self.node_secret[31]]).unwrap();
let our_channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());
- Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script()
+ Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script())
}
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
let secp_ctx = Secp256k1::signing_only();
let secret_key = SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, self.node_secret[31]]).unwrap();
let pubkey_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &secret_key).serialize());
- ShutdownScript::new_p2wpkh(&pubkey_hash)
+ Ok(ShutdownScript::new_p2wpkh(&pubkey_hash))
}
}
macro_rules! confirm_txn {
($node: expr) => { {
let chain_hash = genesis_block(Network::Bitcoin).block_hash();
- let mut header = BlockHeader { version: 0x20000000, prev_blockhash: chain_hash, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
+ let mut header = create_dummy_header(chain_hash, 42);
let txdata: Vec<_> = channel_txn.iter().enumerate().map(|(i, tx)| (i + 1, tx)).collect();
$node.transactions_confirmed(&header, &txdata, 1);
for _ in 2..100 {
- header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
+ header = create_dummy_header(header.block_hash(), 42);
}
$node.best_block_updated(&header, 99);
} }
//! or payments to send/ways to handle events generated.
//! This test has been very useful, though due to its complexity good starting inputs are critical.
-use bitcoin::TxMerkleNode;
-use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::blockdata::script::{Builder, Script};
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
use lightning::chain::chainmonitor;
use lightning::chain::transaction::OutPoint;
-use lightning::chain::keysinterface::{InMemorySigner, Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
+use lightning::sign::{InMemorySigner, Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
use lightning::events::Event;
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::ln::channelmanager::{ChainParameters, ChannelDetails, ChannelManager, PaymentId, RecipientOnionFields, Retry};
use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,IgnoringMessageHandler};
use lightning::ln::msgs::{self, DecodeError};
use lightning::ln::script::ShutdownScript;
+use lightning::ln::functional_test_utils::*;
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
use lightning::routing::utxo::UtxoLookup;
use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
use lightning::util::errors::APIError;
use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use lightning::util::logger::Logger;
-use lightning::util::ser::{Readable, ReadableArgs, Writeable};
+use lightning::util::ser::{ReadableArgs, Writeable};
use crate::utils::test_logger;
use crate::utils::test_persister::TestPersister;
}
self.blocks_connected += 1;
- let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height].0, merkle_root: TxMerkleNode::all_zeros(), time: self.blocks_connected, bits: 42, nonce: 42 };
+ let header = create_dummy_header(self.header_hashes[self.height].0, self.blocks_connected);
self.height += 1;
self.manager.transactions_confirmed(&header, &txdata, self.height as u32);
self.manager.best_block_updated(&header, self.height as u32);
fn disconnect_block(&mut self) {
if self.height > 0 && (self.max_height < 6 || self.height >= self.max_height - 6) {
- let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height - 1].0, merkle_root: TxMerkleNode::all_zeros(), time: self.header_hashes[self.height].1, bits: 42, nonce: 42 };
+ let header = create_dummy_header(self.header_hashes[self.height - 1].0, self.header_hashes[self.height].1);
self.manager.block_disconnected(&header, self.height as u32);
self.monitor.block_disconnected(&header, self.height as u32);
self.height -= 1;
))
}
- fn get_destination_script(&self) -> Script {
+ fn get_destination_script(&self) -> Result<Script, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_monitor_claim_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
let our_channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());
- Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script()
+ Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script())
}
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
let secp_ctx = Secp256k1::signing_only();
let secret_key = SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]).unwrap();
let pubkey_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &secret_key).serialize());
- ShutdownScript::new_p2wpkh(&pubkey_hash)
+ Ok(ShutdownScript::new_p2wpkh(&pubkey_hash))
}
}
chan_handler: channelmanager.clone(),
route_handler: gossip_sync.clone(),
onion_message_handler: IgnoringMessageHandler {},
- }, 0, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0], Arc::clone(&logger), IgnoringMessageHandler{}, keys_manager.clone()));
+ custom_message_handler: IgnoringMessageHandler {},
+ }, 0, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0], Arc::clone(&logger), keys_manager.clone()));
let mut should_forward = false;
let mut payments_received: Vec<PaymentHash> = Vec::new();
// 00 030000000000000000000000000000000000000000000000000000000000000002 03000000000000000000000000000000 - noise act two (0||pubkey||mac)
//
// 030012 - inbound read from peer id 0 of len 18
- // 000a 03000000000000000000000000000000 - message header indicating message length 10
- // 03001a - inbound read from peer id 0 of len 26
- // 0010 00022000 00022000 03000000000000000000000000000000 - init message (type 16) with static_remotekey (0x2000) and mac
+ // 0010 03000000000000000000000000000000 - message header indicating message length 16
+ // 030020 - inbound read from peer id 0 of len 32
+ // 0010 00021aaa 0008aaaaaaaaaaaa9aaa 03000000000000000000000000000000 - init message (type 16) with static_remotekey required and other bits optional and mac
//
// 030012 - inbound read from peer id 0 of len 18
- // 0141 03000000000000000000000000000000 - message header indicating message length 321
+ // 0147 03000000000000000000000000000000 - message header indicating message length 327
// 0300fe - inbound read from peer id 0 of len 254
// 0020 7500000000000000000000000000000000000000000000000000000000000000 ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679 000000000000c350 0000000000000000 0000000000000162 ffffffffffffffff 0000000000000222 0000000000000000 000000fd 0006 01e3 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 030000000000000000000000000000000000000000000000000000000000000004 - beginning of open_channel message
- // 030053 - inbound read from peer id 0 of len 83
- // 030000000000000000000000000000000000000000000000000000000000000005 020900000000000000000000000000000000000000000000000000000000000000 01 03000000000000000000000000000000 - rest of open_channel and mac
+ // 030059 - inbound read from peer id 0 of len 89
+ // 030000000000000000000000000000000000000000000000000000000000000005 020900000000000000000000000000000000000000000000000000000000000000 01 0000 01021000 03000000000000000000000000000000 - rest of open_channel and mac
//
// 00fd00fd - Two feerate requests (all returning min feerate, which our open_channel also uses) (gonna be ingested by FuzzEstimator)
// - client should now respond with accept_channel (CHECK 1: type 33 to peer 03000000)
// 000302000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003000000000000000000000000000000 - inbound noise act 3
//
// 030112 - inbound read from peer id 1 of len 18
- // 000a 01000000000000000000000000000000 - message header indicating message length 10
- // 03011a - inbound read from peer id 1 of len 26
- // 0010 00022000 00022000 01000000000000000000000000000000 - init message (type 16) with static_remotekey (0x2000) and mac
+ // 0010 01000000000000000000000000000000 - message header indicating message length 16
+ // 030120 - inbound read from peer id 1 of len 32
+ // 0010 00021aaa 0008aaaaaaaaaaaa9aaa 01000000000000000000000000000000 - init message (type 16) with static_remotekey required and other bits optional and mac
//
// 05 01 030200000000000000000000000000000000000000000000000000000000000000 00c350 0003e8 - create outbound channel to peer 1 for 50k sat
// 00fd - One feerate requests (all returning min feerate) (gonna be ingested by FuzzEstimator)
//
// 030112 - inbound read from peer id 1 of len 18
- // 0110 01000000000000000000000000000000 - message header indicating message length 272
+ // 0112 01000000000000000000000000000000 - message header indicating message length 274
// 0301ff - inbound read from peer id 1 of len 255
// 0021 0000000000000000000000000000000000000000000000000000000000000e05 0000000000000162 00000000004c4b40 00000000000003e8 00000000000003e8 00000002 03f0 0005 030000000000000000000000000000000000000000000000000000000000000100 030000000000000000000000000000000000000000000000000000000000000200 030000000000000000000000000000000000000000000000000000000000000300 030000000000000000000000000000000000000000000000000000000000000400 030000000000000000000000000000000000000000000000000000000000000500 02660000000000000000000000000000 - beginning of accept_channel
- // 030121 - inbound read from peer id 1 of len 33
- // 0000000000000000000000000000000000 01000000000000000000000000000000 - rest of accept_channel and mac
+ // 030123 - inbound read from peer id 1 of len 35
+ // 0000000000000000000000000000000000 0000 01000000000000000000000000000000 - rest of accept_channel and mac
//
// 0a - create the funding transaction (client should send funding_created now)
//
// - client now fails the HTLC backwards as it was unable to extract the payment preimage (CHECK 9 duplicate and CHECK 10)
let logger = Arc::new(TrackingLogger { lines: Mutex::new(HashMap::new()) });
- super::do_test(&::hex::decode("01000000000000000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000000020300320003000000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000030012000a0300000000000000000000000000000003001a00100002200000022000030000000000000000000000000000000300120141030000000000000000000000000000000300fe00207500000000000000000000000000000000000000000000000000000000000000ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679000000000000c35000000000000000000000000000000162ffffffffffffffff00000000000002220000000000000000000000fd000601e3030000000000000000000000000000000000000000000000000000000000000001030000000000000000000000000000000000000000000000000000000000000002030000000000000000000000000000000000000000000000000000000000000003030000000000000000000000000000000000000000000000000000000000000004030053030000000000000000000000000000000000000000000000000000000000000005020900000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000fd00fd0300120084030000000000000000000000000000000300940022ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb1819096793d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000210100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000c005e020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0150c3000000000000220020ae00000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c000003001200430300000000000000000000000000000003005300243d0000000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000010301320003000000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000030142000302000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003000000000000000000000000000000030112000a0100000000000000000000000000000003011a0010000220000002200001000000000000000000000000000000050103020000000000000000000000000000000000000000000000000000000000000000c3500003e800fd0301120110010000000000000000000000000000000301ff00210000000000000000000000000000000000000000000000000000000000000e05000000000000016200000000004c4b4000000000000003e800000000000003e80000000203f00005030000000000000000000000000000000000000000000000000000000000000100030000000000000000000000000000000000000000000000000000000000000200030000000000000000000000000000000000000000000000000000000000000300030000000000000000000000000000000000000000000000000000000000000400030000000000000000000000000000000000000000000000000000000000000500026600000000000000000000000000000301210000000000000000000000000000000000010000000000000000000000000000000a03011200620100000000000000000000000000000003017200233a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c0001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000b03011200430100000000000000000000000000000003015300243a000000000000000000000000000000000000000000000000000000000000000267000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000020b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000703011200640100000000000000000000000000000003017400843a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000002640000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112004a0100000000000000000000000000000003015a00823a000000000000000000000000000000000000000000000000000000000000000000000000000000ff008888888888888888888888888888888888888888888888888888888888880100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a0000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000265000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000010000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000020d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000703011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000002700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112002c0100000000000000000000000000000003013c00833a00000000000000000000000000000000000000000000000000000000000000000000000000000100000100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000703001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d00000000000000000000000000000000000000000000000000000000000000000000000000000200000000000b0838ff00000000000000000000000000000000000000000000000000000000000000000003f0000300000000000000000000000000000000000000000000000000000000000005551202030927c00401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff53000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200a4030000000000000000000000000000000300b400843d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007501000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006705000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000070c007d02000000013a000000000000000000000000000000000000000000000000000000000000000000000000000000800258020000000000002200204b0000000000000000000000000000000000000000000000000000000000000014c00000000000001600142800000000000000000000000000000000000000050000200c005e0200000001730000000000000000000000000000000000000000000000000000000000000000000000000000000001a701000000000000220020b200000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c000007").unwrap(), &(Arc::clone(&logger) as Arc<dyn Logger>));
+ super::do_test(&::hex::decode("01000000000000000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000000020300320003000000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000030012001003000000000000000000000000000000030020001000021aaa0008aaaaaaaaaaaa9aaa030000000000000000000000000000000300120147030000000000000000000000000000000300fe00207500000000000000000000000000000000000000000000000000000000000000ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679000000000000c35000000000000000000000000000000162ffffffffffffffff00000000000002220000000000000000000000fd000601e3030000000000000000000000000000000000000000000000000000000000000001030000000000000000000000000000000000000000000000000000000000000002030000000000000000000000000000000000000000000000000000000000000003030000000000000000000000000000000000000000000000000000000000000004030059030000000000000000000000000000000000000000000000000000000000000005020900000000000000000000000000000000000000000000000000000000000000010000010210000300000000000000000000000000000000fd00fd0300120084030000000000000000000000000000000300940022ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb1819096793d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000210100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000c005e020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0150c3000000000000220020ae00000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c000003001200430300000000000000000000000000000003005300243d0000000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000010301320003000000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000030142000302000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003000000000000000000000000000000030112001001000000000000000000000000000000030120001000021aaa0008aaaaaaaaaaaa9aaa01000000000000000000000000000000050103020000000000000000000000000000000000000000000000000000000000000000c3500003e800fd0301120112010000000000000000000000000000000301ff00210000000000000000000000000000000000000000000000000000000000000e05000000000000016200000000004c4b4000000000000003e800000000000003e80000000203f000050300000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000002000300000000000000000000000000000000000000000000000000000000000003000300000000000000000000000000000000000000000000000000000000000004000300000000000000000000000000000000000000000000000000000000000005000266000000000000000000000000000003012300000000000000000000000000000000000000010000000000000000000000000000000a03011200620100000000000000000000000000000003017200233a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c0001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000b03011200430100000000000000000000000000000003015300243a000000000000000000000000000000000000000000000000000000000000000267000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000020b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000703011200640100000000000000000000000000000003017400843a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000002640000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112004a0100000000000000000000000000000003015a00823a000000000000000000000000000000000000000000000000000000000000000000000000000000ff008888888888888888888888888888888888888888888888888888888888880100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a0000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000265000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000010000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000020d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000703011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000002700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112002c0100000000000000000000000000000003013c00833a00000000000000000000000000000000000000000000000000000000000000000000000000000100000100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000703001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d00000000000000000000000000000000000000000000000000000000000000000000000000000200000000000b0838ff00000000000000000000000000000000000000000000000000000000000000000003f0000300000000000000000000000000000000000000000000000000000000000005551202030927c00401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff53000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200a4030000000000000000000000000000000300b400843d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007501000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006705000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000070c007d02000000013a000000000000000000000000000000000000000000000000000000000000000000000000000000800258020000000000002200204b0000000000000000000000000000000000000000000000000000000000000014c00000000000001600142800000000000000000000000000000000000000050000200c005e0200000001730000000000000000000000000000000000000000000000000000000000000000000000000000000001a701000000000000220020b200000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c000007").unwrap(), &(Arc::clone(&logger) as Arc<dyn Logger>));
let log_entries = logger.lines.lock().unwrap();
assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling SendAcceptChannel event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000002 for channel ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679".to_string())), Some(&1)); // 1
use crate::utils::test_logger;
use core::convert::{Infallible, TryFrom};
use lightning::blinded_path::BlindedPath;
-use lightning::chain::keysinterface::EntropySource;
+use lightning::sign::EntropySource;
use lightning::ln::PaymentHash;
use lightning::ln::features::BlindedHopFeatures;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedInvoice};
GEN_TEST lightning::ln::msgs::UpdateFailMalformedHTLC test_msg_simple ""
GEN_TEST lightning::ln::msgs::UpdateFee test_msg_simple ""
GEN_TEST lightning::ln::msgs::UpdateFulfillHTLC test_msg_simple ""
+GEN_TEST lightning::ln::msgs::ChannelReestablish test_msg_simple ""
-GEN_TEST lightning::ln::msgs::ChannelReestablish test_msg ""
GEN_TEST lightning::ln::msgs::DecodedOnionErrorPacket test_msg ""
GEN_TEST lightning::ln::msgs::ChannelAnnouncement test_msg_exact ""
GEN_TEST lightning::ln::msgs::ChannelUpdate test_msg_hole ", 108, 1"
GEN_TEST lightning::ln::channelmanager::ChannelDetails test_msg_simple ""
+
+GEN_TEST lightning::ln::msgs::OpenChannelV2 test_msg_simple ""
+GEN_TEST lightning::ln::msgs::AcceptChannelV2 test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxAddInput test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxAddOutput test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxRemoveInput test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxRemoveOutput test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxComplete test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxSignatures test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxInitRbf test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxAckRbf test_msg_simple ""
+GEN_TEST lightning::ln::msgs::TxAbort test_msg_simple ""
pub mod msg_warning_message;
pub mod msg_channel_update;
pub mod msg_channel_details;
+pub mod msg_open_channel_v2;
+pub mod msg_accept_channel_v2;
+pub mod msg_tx_add_input;
+pub mod msg_tx_add_output;
+pub mod msg_tx_remove_input;
+pub mod msg_tx_remove_output;
+pub mod msg_tx_complete;
+pub mod msg_tx_signatures;
+pub mod msg_tx_init_rbf;
+pub mod msg_tx_ack_rbf;
+pub mod msg_tx_abort;
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_accept_channel_v2_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::AcceptChannelV2, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_accept_channel_v2_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::AcceptChannelV2, data);
+}
#[inline]
pub fn msg_channel_reestablish_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(lightning::ln::msgs::ChannelReestablish, data);
+ test_msg_simple!(lightning::ln::msgs::ChannelReestablish, data);
}
#[no_mangle]
pub extern "C" fn msg_channel_reestablish_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg!(lightning::ln::msgs::ChannelReestablish, data);
+ test_msg_simple!(lightning::ln::msgs::ChannelReestablish, data);
}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_open_channel_v2_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::OpenChannelV2, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_open_channel_v2_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::OpenChannelV2, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_abort_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxAbort, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_abort_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxAbort, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_ack_rbf_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxAckRbf, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_ack_rbf_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxAckRbf, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_add_input_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxAddInput, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_add_input_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxAddInput, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_add_output_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxAddOutput, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_add_output_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxAddOutput, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_complete_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxComplete, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_complete_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxComplete, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_init_rbf_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxInitRbf, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_init_rbf_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxInitRbf, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_remove_input_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxRemoveInput, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_remove_input_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxRemoveInput, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_remove_output_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxRemoveOutput, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_remove_output_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxRemoveOutput, data);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::msg_targets::utils::VecWriter;
+use crate::utils::test_logger;
+
+#[inline]
+pub fn msg_tx_signatures_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::msgs::TxSignatures, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_tx_signatures_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::msgs::TxSignatures, data);
+}
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
-use lightning::chain::keysinterface::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
+use lightning::sign::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerProvider};
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
use lightning::ln::script::ShutdownScript;
use lightning::util::enforcing_trait_impls::EnforcingSigner;
fn read_chan_signer(&self, _data: &[u8]) -> Result<EnforcingSigner, DecodeError> { unreachable!() }
- fn get_destination_script(&self) -> Script { unreachable!() }
+ fn get_destination_script(&self) -> Result<Script, ()> { unreachable!() }
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!() }
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> { unreachable!() }
}
#[cfg(test)]
use crate::utils::test_logger;
use core::convert::{Infallible, TryFrom};
use lightning::blinded_path::BlindedPath;
-use lightning::chain::keysinterface::EntropySource;
+use lightning::sign::EntropySource;
use lightning::ln::PaymentHash;
use lightning::ln::features::BlindedHopFeatures;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedInvoice};
use lightning::routing::gossip::{NetworkGraph, RoutingFees};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};
use lightning::routing::router::{find_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters};
-use lightning::routing::scoring::ProbabilisticScorer;
+use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
use lightning::util::config::UserConfig;
use lightning::util::ser::Readable;
}]));
}
}
- let scorer = ProbabilisticScorer::new(Default::default(), &net_graph, &logger);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &net_graph, &logger);
let random_seed_bytes: [u8; 32] = [get_slice!(1)[0]; 32];
for target in node_pks.iter() {
let final_value_msat = slice_to_be64(get_slice!(8));
let final_cltv_expiry_delta = slice_to_be32(get_slice!(4));
let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(*target, final_cltv_expiry_delta)
- .with_route_hints(last_hops.clone()),
+ .with_route_hints(last_hops.clone()).unwrap(),
final_value_msat,
};
let _ = find_route(&our_pubkey, &route_params, &net_graph,
first_hops.map(|c| c.iter().collect::<Vec<_>>()).as_ref().map(|a| a.as_slice()),
- &logger, &scorer, &random_seed_bytes);
+ &logger, &scorer, &ProbabilisticScoringFeeParameters::default(), &random_seed_bytes);
}
},
}
void msg_ping_run(const unsigned char* data, size_t data_len);
void msg_pong_run(const unsigned char* data, size_t data_len);
void msg_channel_details_run(const unsigned char* data, size_t data_len);
+void msg_open_channel_v2_run(const unsigned char* data, size_t data_len);
+void msg_accept_channel_v2_run(const unsigned char* data, size_t data_len);
+void msg_tx_add_input_run(const unsigned char* data, size_t data_len);
+void msg_tx_add_output_run(const unsigned char* data, size_t data_len);
+void msg_tx_remove_input_run(const unsigned char* data, size_t data_len);
+void msg_tx_remove_output_run(const unsigned char* data, size_t data_len);
+void msg_tx_complete_run(const unsigned char* data, size_t data_len);
+void msg_tx_signatures_run(const unsigned char* data, size_t data_len);
+void msg_tx_init_rbf_run(const unsigned char* data, size_t data_len);
+void msg_tx_ack_rbf_run(const unsigned char* data, size_t data_len);
+void msg_tx_abort_run(const unsigned char* data, size_t data_len);
[package]
name = "lightning-background-processor"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Valentine Wallace <vwallace@protonmail.com>"]
license = "MIT OR Apache-2.0"
repository = "http://github.com/lightningdevkit/rust-lightning"
[dependencies]
bitcoin = { version = "0.29.0", default-features = false }
-lightning = { version = "0.0.114", path = "../lightning", default-features = false }
-lightning-rapid-gossip-sync = { version = "0.0.114", path = "../lightning-rapid-gossip-sync", default-features = false }
+lightning = { version = "0.0.115", path = "../lightning", default-features = false }
+lightning-rapid-gossip-sync = { version = "0.0.115", path = "../lightning-rapid-gossip-sync", default-features = false }
[dev-dependencies]
tokio = { version = "1.14", features = [ "macros", "rt", "rt-multi-thread", "sync", "time" ] }
-lightning = { version = "0.0.114", path = "../lightning", features = ["_test_utils"] }
-lightning-invoice = { version = "0.22.0", path = "../lightning-invoice" }
-lightning-persister = { version = "0.0.114", path = "../lightning-persister" }
+lightning = { version = "0.0.115", path = "../lightning", features = ["_test_utils"] }
+lightning-invoice = { version = "0.23.0", path = "../lightning-invoice" }
+lightning-persister = { version = "0.0.115", path = "../lightning-persister" }
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use lightning::chain::chainmonitor::{ChainMonitor, Persist};
-use lightning::chain::keysinterface::{EntropySource, NodeSigner, SignerProvider};
+use lightning::sign::{EntropySource, NodeSigner, SignerProvider};
use lightning::events::{Event, PathFailure};
#[cfg(feature = "std")]
use lightning::events::{EventHandler, EventsProvider};
use lightning::ln::channelmanager::ChannelManager;
-use lightning::ln::msgs::{ChannelMessageHandler, OnionMessageHandler, RoutingMessageHandler};
-use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor};
+use lightning::ln::peer_handler::APeerManager;
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::UtxoLookup;
use lightning::routing::router::Router;
///
/// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
/// [`Event`]: lightning::events::Event
+/// [`PeerManager::timer_tick_occurred`]: lightning::ln::peer_handler::PeerManager::timer_tick_occurred
+/// [`PeerManager::process_events`]: lightning::ln::peer_handler::PeerManager::process_events
#[cfg(feature = "std")]
#[must_use = "BackgroundProcessor will immediately stop on drop. It should be stored until shutdown."]
pub struct BackgroundProcessor {
// ChannelManager, we want to minimize methods blocking on a ChannelManager
// generally, and as a fallback place such blocking only immediately before
// persistence.
- $peer_manager.process_events();
+ $peer_manager.as_ref().process_events();
// Exit the loop if the background processor was requested to stop.
if $loop_exit_check {
// more than a handful of seconds to complete, and shouldn't disconnect all our
// peers.
log_trace!($logger, "100ms sleep took more than a second, disconnecting peers.");
- $peer_manager.disconnect_all_peers();
+ $peer_manager.as_ref().disconnect_all_peers();
last_ping_call = $get_timer(PING_TIMER);
} else if $timer_elapsed(&mut last_ping_call, PING_TIMER) {
log_trace!($logger, "Calling PeerManager's timer_tick_occurred");
- $peer_manager.timer_tick_occurred();
+ $peer_manager.as_ref().timer_tick_occurred();
last_ping_call = $get_timer(PING_TIMER);
}
/// # use lightning_background_processor::{process_events_async, GossipSync};
/// # type MyBroadcaster = dyn lightning::chain::chaininterface::BroadcasterInterface + Send + Sync;
/// # type MyFeeEstimator = dyn lightning::chain::chaininterface::FeeEstimator + Send + Sync;
-/// # type MyNodeSigner = dyn lightning::chain::keysinterface::NodeSigner + Send + Sync;
+/// # type MyNodeSigner = dyn lightning::sign::NodeSigner + Send + Sync;
/// # type MyUtxoLookup = dyn lightning::routing::utxo::UtxoLookup + Send + Sync;
/// # type MyFilter = dyn lightning::chain::Filter + Send + Sync;
/// # type MyLogger = dyn lightning::util::logger::Logger + Send + Sync;
-/// # type MyChainMonitor = lightning::chain::chainmonitor::ChainMonitor<lightning::chain::keysinterface::InMemorySigner, Arc<MyFilter>, Arc<MyBroadcaster>, Arc<MyFeeEstimator>, Arc<MyLogger>, Arc<MyPersister>>;
+/// # type MyChainMonitor = lightning::chain::chainmonitor::ChainMonitor<lightning::sign::InMemorySigner, Arc<MyFilter>, Arc<MyBroadcaster>, Arc<MyFeeEstimator>, Arc<MyLogger>, Arc<MyPersister>>;
/// # type MyPeerManager = lightning::ln::peer_handler::SimpleArcPeerManager<MySocketDescriptor, MyChainMonitor, MyBroadcaster, MyFeeEstimator, MyUtxoLookup, MyLogger>;
/// # type MyNetworkGraph = lightning::routing::gossip::NetworkGraph<Arc<MyLogger>>;
/// # type MyGossipSync = lightning::routing::gossip::P2PGossipSync<Arc<MyNetworkGraph>, Arc<MyUtxoLookup>, Arc<MyLogger>>;
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
- Descriptor: 'static + SocketDescriptor + Send + Sync,
- CMH: 'static + Deref + Send + Sync,
- RMH: 'static + Deref + Send + Sync,
- OMH: 'static + Deref + Send + Sync,
EventHandlerFuture: core::future::Future<Output = ()>,
EventHandler: Fn(Event) -> EventHandlerFuture,
PS: 'static + Deref + Send,
CM: 'static + Deref<Target = ChannelManager<CW, T, ES, NS, SP, F, R, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, UL, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
- UMH: 'static + Deref + Send + Sync,
- PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, OMH, L, UMH, NS>> + Send + Sync,
+ APM: APeerManager + Send + Sync,
+ PM: 'static + Deref<Target = APM> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: for<'b> WriteableScore<'b>,
SleepFuture: core::future::Future<Output = bool> + core::marker::Unpin,
R::Target: 'static + Router,
L::Target: 'static + Logger,
P::Target: 'static + Persist<<SP::Target as SignerProvider>::Signer>,
- CMH::Target: 'static + ChannelMessageHandler,
- OMH::Target: 'static + OnionMessageHandler,
- RMH::Target: 'static + RoutingMessageHandler,
- UMH::Target: 'static + CustomMessageHandler,
PS::Target: 'static + Persister<'a, CW, T, ES, NS, SP, F, R, L, SC>,
{
let mut should_break = false;
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
- Descriptor: 'static + SocketDescriptor + Send + Sync,
- CMH: 'static + Deref + Send + Sync,
- OMH: 'static + Deref + Send + Sync,
- RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<<SP::Target as SignerProvider>::Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<CW, T, ES, NS, SP, F, R, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, UL, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
- UMH: 'static + Deref + Send + Sync,
- PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, OMH, L, UMH, NS>> + Send + Sync,
+ APM: APeerManager + Send + Sync,
+ PM: 'static + Deref<Target = APM> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: for <'b> WriteableScore<'b>,
>(
R::Target: 'static + Router,
L::Target: 'static + Logger,
P::Target: 'static + Persist<<SP::Target as SignerProvider>::Signer>,
- CMH::Target: 'static + ChannelMessageHandler,
- OMH::Target: 'static + OnionMessageHandler,
- RMH::Target: 'static + RoutingMessageHandler,
- UMH::Target: 'static + CustomMessageHandler,
PS::Target: 'static + Persister<'a, CW, T, ES, NS, SP, F, R, L, SC>,
{
let stop_thread = Arc::new(AtomicBool::new(false));
#[cfg(all(feature = "std", test))]
mod tests {
- use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::locktime::PackedLockTime;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1};
use lightning::chain::{BestBlock, Confirm, chainmonitor};
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
- use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
+ use lightning::sign::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::{get_event_msg, get_event};
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
use lightning::ln::features::{ChannelFeatures, NodeFeatures};
+ use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs::{ChannelMessageHandler, Init};
use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
use lightning::routing::gossip::{NetworkGraph, NodeId, P2PGossipSync};
use lightning::util::persist::KVStorePersister;
use lightning_persister::FilesystemPersister;
use std::collections::VecDeque;
- use std::fs;
+ use std::{fs, env};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::SyncSender;
use std::time::Duration;
- use bitcoin::hashes::Hash;
- use bitcoin::TxMerkleNode;
use lightning_rapid_gossip_sync::RapidGossipSync;
use super::{BackgroundProcessor, GossipSync, FRESHNESS_TIMER};
fn disconnect_socket(&mut self) {}
}
- type ChannelManager = channelmanager::ChannelManager<Arc<ChainMonitor>, Arc<test_utils::TestBroadcaster>, Arc<KeysManager>, Arc<KeysManager>, Arc<KeysManager>, Arc<test_utils::TestFeeEstimator>, Arc<DefaultRouter< Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>, Arc<Mutex<TestScorer>>>>, Arc<test_utils::TestLogger>>;
+ type ChannelManager = channelmanager::ChannelManager<Arc<ChainMonitor>, Arc<test_utils::TestBroadcaster>, Arc<KeysManager>, Arc<KeysManager>, Arc<KeysManager>, Arc<test_utils::TestFeeEstimator>, Arc<DefaultRouter<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>, Arc<Mutex<TestScorer>>, (), TestScorer>>, Arc<test_utils::TestLogger>>;
type ChainMonitor = chainmonitor::ChainMonitor<InMemorySigner, Arc<test_utils::TestChainSource>, Arc<test_utils::TestBroadcaster>, Arc<test_utils::TestFeeEstimator>, Arc<test_utils::TestLogger>, Arc<FilesystemPersister>>;
}
impl Score for TestScorer {
+ type ScoreParams = ();
fn channel_penalty_msat(
- &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage
+ &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage, _score_params: &Self::ScoreParams
) -> u64 { unimplemented!(); }
fn payment_path_failed(&mut self, actual_path: &Path, actual_short_channel_id: u64) {
path.to_str().unwrap().to_string()
}
- fn create_nodes(num_nodes: usize, persist_dir: String) -> Vec<Node> {
+ fn create_nodes(num_nodes: usize, persist_dir: &str) -> (String, Vec<Node>) {
+ let persist_temp_path = env::temp_dir().join(persist_dir);
+ let persist_dir = persist_temp_path.to_string_lossy().to_string();
let network = Network::Testnet;
let mut nodes = Vec::new();
for i in 0..num_nodes {
let network_graph = Arc::new(NetworkGraph::new(network, logger.clone()));
let scorer = Arc::new(Mutex::new(TestScorer::new()));
let seed = [i as u8; 32];
- let router = Arc::new(DefaultRouter::new(network_graph.clone(), logger.clone(), seed, scorer.clone()));
+ let router = Arc::new(DefaultRouter::new(network_graph.clone(), logger.clone(), seed, scorer.clone(), ()));
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
- let persister = Arc::new(FilesystemPersister::new(format!("{}_persister_{}", persist_dir, i)));
+ let persister = Arc::new(FilesystemPersister::new(format!("{}_persister_{}", &persist_dir, i)));
let now = Duration::from_secs(genesis_block.header.time as u64);
let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos()));
let chain_monitor = Arc::new(chainmonitor::ChainMonitor::new(Some(chain_source.clone()), tx_broadcaster.clone(), logger.clone(), fee_estimator.clone(), persister.clone()));
let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster.clone(), router.clone(), logger.clone(), keys_manager.clone(), keys_manager.clone(), keys_manager.clone(), UserConfig::default(), params));
let p2p_gossip_sync = Arc::new(P2PGossipSync::new(network_graph.clone(), Some(chain_source.clone()), logger.clone()));
let rapid_gossip_sync = Arc::new(RapidGossipSync::new(network_graph.clone(), logger.clone()));
- let msg_handler = MessageHandler { chan_handler: Arc::new(test_utils::TestChannelMessageHandler::new()), route_handler: Arc::new(test_utils::TestRoutingMessageHandler::new()), onion_message_handler: IgnoringMessageHandler{}};
- let peer_manager = Arc::new(PeerManager::new(msg_handler, 0, &seed, logger.clone(), IgnoringMessageHandler{}, keys_manager.clone()));
+ let msg_handler = MessageHandler {
+ chan_handler: Arc::new(test_utils::TestChannelMessageHandler::new()),
+ route_handler: Arc::new(test_utils::TestRoutingMessageHandler::new()),
+ onion_message_handler: IgnoringMessageHandler{}, custom_message_handler: IgnoringMessageHandler{}
+ };
+ let peer_manager = Arc::new(PeerManager::new(msg_handler, 0, &seed, logger.clone(), keys_manager.clone()));
let node = Node { node: manager, p2p_gossip_sync, rapid_gossip_sync, peer_manager, chain_monitor, persister, tx_broadcaster, network_graph, logger, best_block, scorer };
nodes.push(node);
}
}
}
- nodes
+ (persist_dir, nodes)
}
macro_rules! open_channel {
for i in 1..=depth {
let prev_blockhash = node.best_block.block_hash();
let height = node.best_block.height() + 1;
- let header = BlockHeader { version: 0x20000000, prev_blockhash, merkle_root: TxMerkleNode::all_zeros(), time: height, bits: 42, nonce: 42 };
+ let header = create_dummy_header(prev_blockhash, height);
let txdata = vec![(0, tx)];
node.best_block = BestBlock::new(header.block_hash(), height);
match i {
// Test that when a new channel is created, the ChannelManager needs to be re-persisted with
// updates. Also test that when new updates are available, the manager signals that it needs
// re-persistence and is successfully re-persisted.
- let nodes = create_nodes(2, "test_background_processor".to_string());
+ let (persist_dir, nodes) = create_nodes(2, "test_background_processor");
// Go through the channel creation process so that each node has something to persist. Since
// open_channel consumes events, it must complete before starting BackgroundProcessor to
}
// Check that the initial channel manager data is persisted as expected.
- let filepath = get_full_filepath("test_background_processor_persister_0".to_string(), "manager".to_string());
+ let filepath = get_full_filepath(format!("{}_persister_0", &persist_dir), "manager".to_string());
check_persisted_data!(nodes[0].node, filepath.clone());
loop {
}
// Check network graph is persisted
- let filepath = get_full_filepath("test_background_processor_persister_0".to_string(), "network_graph".to_string());
+ let filepath = get_full_filepath(format!("{}_persister_0", &persist_dir), "network_graph".to_string());
check_persisted_data!(nodes[0].network_graph, filepath.clone());
// Check scorer is persisted
- let filepath = get_full_filepath("test_background_processor_persister_0".to_string(), "scorer".to_string());
+ let filepath = get_full_filepath(format!("{}_persister_0", &persist_dir), "scorer".to_string());
check_persisted_data!(nodes[0].scorer, filepath.clone());
if !std::thread::panicking() {
// Test that `ChannelManager::timer_tick_occurred` is called every `FRESHNESS_TIMER`,
// `ChainMonitor::rebroadcast_pending_claims` is called every `REBROADCAST_TIMER`, and
// `PeerManager::timer_tick_occurred` every `PING_TIMER`.
- let nodes = create_nodes(1, "test_timer_tick_called".to_string());
+ let (_, nodes) = create_nodes(1, "test_timer_tick_called");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir));
let event_handler = |_: _| {};
#[test]
fn test_channel_manager_persist_error() {
// Test that if we encounter an error during manager persistence, the thread panics.
- let nodes = create_nodes(2, "test_persist_error".to_string());
+ let (_, nodes) = create_nodes(2, "test_persist_error");
open_channel!(nodes[0], nodes[1], 100000);
let data_dir = nodes[0].persister.get_data_dir();
#[cfg(feature = "futures")]
async fn test_channel_manager_persist_error_async() {
// Test that if we encounter an error during manager persistence, the thread panics.
- let nodes = create_nodes(2, "test_persist_error_sync".to_string());
+ let (_, nodes) = create_nodes(2, "test_persist_error_sync");
open_channel!(nodes[0], nodes[1], 100000);
let data_dir = nodes[0].persister.get_data_dir();
#[test]
fn test_network_graph_persist_error() {
// Test that if we encounter an error during network graph persistence, an error gets returned.
- let nodes = create_nodes(2, "test_persist_network_graph_error".to_string());
+ let (_, nodes) = create_nodes(2, "test_persist_network_graph_error");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir).with_graph_error(std::io::ErrorKind::Other, "test"));
let event_handler = |_: _| {};
#[test]
fn test_scorer_persist_error() {
// Test that if we encounter an error during scorer persistence, an error gets returned.
- let nodes = create_nodes(2, "test_persist_scorer_error".to_string());
+ let (_, nodes) = create_nodes(2, "test_persist_scorer_error");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir).with_scorer_error(std::io::ErrorKind::Other, "test"));
let event_handler = |_: _| {};
#[test]
fn test_background_event_handling() {
- let mut nodes = create_nodes(2, "test_background_event_handling".to_string());
+ let (_, mut nodes) = create_nodes(2, "test_background_event_handling");
let channel_value = 100000;
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir.clone()));
#[test]
fn test_scorer_persistence() {
- let nodes = create_nodes(2, "test_scorer_persistence".to_string());
+ let (_, nodes) = create_nodes(2, "test_scorer_persistence");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir));
let event_handler = |_: _| {};
fn test_not_pruning_network_graph_until_graph_sync_completion() {
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
- let nodes = create_nodes(2, "test_not_pruning_network_graph_until_graph_sync_completion".to_string());
+ let (_, nodes) = create_nodes(2, "test_not_pruning_network_graph_until_graph_sync_completion");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir).with_graph_persistence_notifier(sender));
async fn test_not_pruning_network_graph_until_graph_sync_completion_async() {
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
- let nodes = create_nodes(2, "test_not_pruning_network_graph_until_graph_sync_completion_async".to_string());
+ let (_, nodes) = create_nodes(2, "test_not_pruning_network_graph_until_graph_sync_completion_async");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir).with_graph_persistence_notifier(sender));
_ => panic!("Unexpected event: {:?}", event),
};
- let nodes = create_nodes(1, "test_payment_path_scoring".to_string());
+ let (_, nodes) = create_nodes(1, "test_payment_path_scoring");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir));
let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].no_gossip_sync(), nodes[0].peer_manager.clone(), nodes[0].logger.clone(), Some(nodes[0].scorer.clone()));
}
};
- let nodes = create_nodes(1, "test_payment_path_scoring_async".to_string());
+ let (_, nodes) = create_nodes(1, "test_payment_path_scoring_async");
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir));
[package]
name = "lightning-block-sync"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Jeffrey Czyz", "Matt Corallo"]
license = "MIT OR Apache-2.0"
repository = "http://github.com/lightningdevkit/rust-lightning"
[dependencies]
bitcoin = "0.29.0"
-lightning = { version = "0.0.114", path = "../lightning" }
+lightning = { version = "0.0.115", path = "../lightning" }
tokio = { version = "1.0", features = [ "io-util", "net", "time" ], optional = true }
serde_json = { version = "1.0", optional = true }
chunked_transfer = { version = "1.4", optional = true }
[dev-dependencies]
-lightning = { version = "0.0.114", path = "../lightning", features = ["_test_utils"] }
+lightning = { version = "0.0.115", path = "../lightning", features = ["_test_utils"] }
tokio = { version = "1.14", features = [ "macros", "rt" ] }
/// use lightning::chain::channelmonitor::ChannelMonitor;
/// use lightning::chain::chaininterface::BroadcasterInterface;
/// use lightning::chain::chaininterface::FeeEstimator;
-/// use lightning::chain::keysinterface;
-/// use lightning::chain::keysinterface::{EntropySource, NodeSigner, SignerProvider};
+/// use lightning::sign;
+/// use lightning::sign::{EntropySource, NodeSigner, SignerProvider};
/// use lightning::ln::channelmanager::{ChannelManager, ChannelManagerReadArgs};
/// use lightning::routing::router::Router;
/// use lightning::util::config::UserConfig;
if let Network::Bitcoin = network {
if self.height % 2016 == 0 {
- let previous_work = previous_header.header.work();
- if work > (previous_work << 2) || work < (previous_work >> 2) {
+ let target = self.header.target();
+ let previous_target = previous_header.header.target();
+ let min_target = previous_target >> 2;
+ let max_target = previous_target << 2;
+ if target > max_target || target < min_target {
return Err(BlockSourceError::persistent("invalid difficulty transition"))
}
} else if self.header.bits != previous_header.header.bits {
[package]
name = "lightning-custom-message"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Jeffrey Czyz"]
license = "MIT OR Apache-2.0"
repository = "http://github.com/lightningdevkit/rust-lightning"
[dependencies]
bitcoin = "0.29.0"
-lightning = { version = "0.0.114", path = "../lightning" }
+lightning = { version = "0.0.115", path = "../lightning" }
//! # use bitcoin::secp256k1::PublicKey;
//! # use lightning::io;
//! # use lightning::ln::msgs::{DecodeError, LightningError};
+//! # use lightning::ln::features::{InitFeatures, NodeFeatures};
//! use lightning::ln::peer_handler::CustomMessageHandler;
//! use lightning::ln::wire::{CustomMessageReader, self};
//! use lightning::util::ser::Writeable;
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
//! # unimplemented!()
//! # }
+//! # fn provided_node_features(&self) -> NodeFeatures {
+//! # unimplemented!()
+//! # }
+//! # fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
+//! # unimplemented!()
+//! # }
//! }
//!
//! #[derive(Debug)]
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
//! # unimplemented!()
//! # }
+//! # fn provided_node_features(&self) -> NodeFeatures {
+//! # unimplemented!()
+//! # }
+//! # fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
+//! # unimplemented!()
+//! # }
//! }
//!
//! #[derive(Debug)]
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
//! # unimplemented!()
//! # }
+//! # fn provided_node_features(&self) -> NodeFeatures {
+//! # unimplemented!()
+//! # }
+//! # fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
+//! # unimplemented!()
+//! # }
//! }
//!
//! # fn main() {
)*
.collect()
}
+
+ fn provided_node_features(&self) -> $crate::lightning::ln::features::NodeFeatures {
+ $crate::lightning::ln::features::NodeFeatures::empty()
+ $(
+ | self.$field.provided_node_features()
+ )*
+ }
+
+ fn provided_init_features(
+ &self, their_node_id: &$crate::bitcoin::secp256k1::PublicKey
+ ) -> $crate::lightning::ln::features::InitFeatures {
+ $crate::lightning::ln::features::InitFeatures::empty()
+ $(
+ | self.$field.provided_init_features(their_node_id)
+ )*
+ }
}
impl $crate::lightning::ln::wire::CustomMessageReader for $handler {
[package]
name = "lightning-invoice"
description = "Data structures to parse and serialize BOLT11 lightning invoices"
-version = "0.22.0"
+version = "0.23.0"
authors = ["Sebastian Geisler <sgeisler@wh2.tu-dresden.de>"]
documentation = "https://docs.rs/lightning-invoice/"
license = "MIT OR Apache-2.0"
[dependencies]
bech32 = { version = "0.9.0", default-features = false }
-lightning = { version = "0.0.114", path = "../lightning", default-features = false }
+lightning = { version = "0.0.115", path = "../lightning", default-features = false }
secp256k1 = { version = "0.24.0", default-features = false, features = ["recovery", "alloc"] }
num-traits = { version = "0.2.8", default-features = false }
bitcoin_hashes = { version = "0.11", default-features = false }
bitcoin = { version = "0.29.0", default-features = false }
[dev-dependencies]
-lightning = { version = "0.0.114", path = "../lightning", default-features = false, features = ["_test_utils"] }
+lightning = { version = "0.0.115", path = "../lightning", default-features = false, features = ["_test_utils"] }
hex = "0.4"
serde_json = { version = "1"}
use secp256k1::{Message, Secp256k1};
use secp256k1::ecdsa::RecoverableSignature;
+use core::cmp::Ordering;
use core::fmt::{Display, Formatter, self};
use core::iter::FilterMap;
use core::num::ParseIntError;
/// 3. using `str::parse::<Invoice>(&str)` (see [`Invoice::from_str`])
///
/// [`Invoice::from_str`]: crate::Invoice#impl-FromStr
-#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
pub struct Invoice {
signed_invoice: SignedRawInvoice,
}
///
/// This is not exported to bindings users as we don't have a good way to map the reference lifetimes making this
/// practically impossible to use safely in languages like C.
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Eq, PartialEq, Debug, Clone, Ord, PartialOrd)]
pub enum InvoiceDescription<'f> {
/// Reference to the directly supplied description in the invoice
Direct(&'f Description),
///
/// # Invariants
/// The hash has to be either from the deserialized invoice or from the serialized [`RawInvoice`].
-#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
pub struct SignedRawInvoice {
/// The rawInvoice that the signature belongs to
raw_invoice: RawInvoice,
/// Decoding and encoding should not lead to information loss but may lead to different hashes.
///
/// For methods without docs see the corresponding methods in [`Invoice`].
-#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
pub struct RawInvoice {
/// human readable part
pub hrp: RawHrp,
/// Data of the [`RawInvoice`] that is encoded in the human readable part.
///
/// This is not exported to bindings users as we don't yet support `Option<Enum>`
-#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
pub struct RawHrp {
/// The currency deferred from the 3rd and 4th character of the bech32 transaction
pub currency: Currency,
}
/// Data of the [`RawInvoice`] that is encoded in the data part
-#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
pub struct RawDataPart {
/// generation time of the invoice
pub timestamp: PositiveTimestamp,
///
/// The Unix timestamp representing the stored time has to be positive and no greater than
/// [`MAX_TIMESTAMP`].
-#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
pub struct PositiveTimestamp(Duration);
/// SI prefixes for the human readable part
-#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
+#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Ord, PartialOrd)]
pub enum SiPrefix {
/// 10^-3
Milli,
}
/// Enum representing the crypto currencies (or networks) supported by this library
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Currency {
/// Bitcoin mainnet
Bitcoin,
/// Tagged field which may have an unknown tag
///
/// This is not exported to bindings users as we don't currently support TaggedField
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum RawTaggedField {
/// Parsed tagged field with known tag
KnownSemantics(TaggedField),
/// This is not exported to bindings users as we don't yet support enum variants with the same name the struct contained
/// in the variant.
#[allow(missing_docs)]
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum TaggedField {
PaymentHash(Sha256),
Description(Description),
}
/// SHA-256 hash
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Sha256(/// This is not exported to bindings users as the native hash types are not currently mapped
pub sha256::Hash);
+impl Sha256 {
+ /// Constructs a new [`Sha256`] from the given bytes, which are assumed to be the output of a
+ /// single sha256 hash.
+ #[cfg(c_bindings)]
+ pub fn from_bytes(bytes: &[u8; 32]) -> Self {
+ Self(sha256::Hash::from_slice(bytes).expect("from_slice only fails if len is not 32"))
+ }
+}
+
/// Description string
///
/// # Invariants
/// The description can be at most 639 __bytes__ long
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Description(String);
/// Payee public key
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct PayeePubKey(pub PublicKey);
/// Positive duration that defines when (relatively to the timestamp) in the future the invoice
/// expires
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct ExpiryTime(Duration);
/// `min_final_cltv_expiry_delta` to use for the last HTLC in the route
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct MinFinalCltvExpiryDelta(pub u64);
/// Fallback address in case no LN payment is possible
#[allow(missing_docs)]
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Fallback {
SegWitProgram {
version: WitnessVersion,
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct InvoiceSignature(pub RecoverableSignature);
+impl PartialOrd for InvoiceSignature {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.0.serialize_compact().1.partial_cmp(&other.0.serialize_compact().1)
+ }
+}
+
+impl Ord for InvoiceSignature {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.0.serialize_compact().1.cmp(&other.0.serialize_compact().1)
+ }
+}
+
/// Private routing information
///
/// # Invariants
/// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
///
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct PrivateRoute(RouteHint);
/// Tag constants as specified in BOLT11
fn deserialize<D>(deserializer: D) -> Result<Invoice, D::Error> where D: Deserializer<'de> {
let bolt11 = String::deserialize(deserializer)?
.parse::<Invoice>()
- .map_err(|e| D::Error::custom(alloc::format!("{:?}", e)))?;
+ .map_err(|e| D::Error::custom(format_args!("{:?}", e)))?;
Ok(bolt11)
}
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
-use lightning::chain::keysinterface::{NodeSigner, SignerProvider, EntropySource};
+use lightning::sign::{NodeSigner, SignerProvider, EntropySource};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager::{ChannelManager, PaymentId, Retry, RetryableSendFailure, RecipientOnionFields};
use lightning::routing::router::{PaymentParameters, RouteParameters, Router};
let mut payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
invoice.min_final_cltv_expiry_delta() as u32)
.with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs())
- .with_route_hints(invoice.route_hints());
+ .with_route_hints(invoice.route_hints()).unwrap();
if let Some(features) = invoice.features() {
- payment_params = payment_params.with_features(features.clone());
+ payment_params = payment_params.with_bolt11_features(features.clone()).unwrap();
}
let route_params = RouteParameters {
payment_params,
use bitcoin_hashes::Hash;
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
-use lightning::chain::keysinterface::{Recipient, NodeSigner, SignerProvider, EntropySource};
+use lightning::sign::{Recipient, NodeSigner, SignerProvider, EntropySource};
use lightning::ln::{PaymentHash, PaymentSecret};
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, MIN_FINAL_CLTV_EXPIRY_DELTA};
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
/// requirement).
///
-/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
+/// [`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
/// participating node
/// * It is fine to cache `phantom_route_hints` and reuse it across invoices, as long as the data is
/// updated when a channel becomes disabled or closes
-/// * Note that if too many channels are included in [`PhantomRouteHints::channels`], the invoice
-/// may be too long for QR code scanning. To fix this, `PhantomRouteHints::channels` may be pared
-/// down
+/// * Note that the route hints generated from `phantom_route_hints` will be limited to a maximum
+/// of 3 hints to ensure that the invoice can be scanned in a QR code. These hints are selected
+/// in the order that the nodes in `PhantomRouteHints` are specified, selecting one hint per node
+/// until the maximum is hit. Callers may provide as many `PhantomRouteHints::channels` as
+/// desired, but note that some nodes will be trimmed if more than 3 nodes are provided.
///
/// `description_hash` is a SHA-256 hash of the description text
///
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
/// requirement).
///
-/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
+/// [`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
invoice = invoice.amount_milli_satoshis(amt);
}
+ for route_hint in select_phantom_hints(amt_msat, phantom_route_hints, logger) {
+ invoice = invoice.private_route(route_hint);
+ }
+
+ let raw_invoice = match invoice.build_raw() {
+ 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));
+ match signed_raw_invoice {
+ Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
+ Err(e) => Err(SignOrCreationError::SignError(e))
+ }
+}
+
+/// Utility to select route hints for phantom invoices.
+/// See [`PhantomKeysManager`] for more information on phantom node payments.
+///
+/// To ensure that the phantom invoice is still readable by QR code, we limit to 3 hints per invoice:
+/// * 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
+fn select_phantom_hints<L: Deref>(amt_msat: Option<u64>, phantom_route_hints: Vec<PhantomRouteHints>,
+ logger: L) -> Vec<RouteHint>
+where
+ L::Target: Logger,
+{
+ let mut phantom_hints: Vec<Vec<RouteHint>> = Vec::new();
+
for PhantomRouteHints { channels, phantom_scid, real_node_pubkey } in phantom_route_hints {
log_trace!(logger, "Generating phantom route hints for node {}",
log_pubkey!(real_node_pubkey));
- let mut route_hints = filter_channels(channels, amt_msat, &logger);
+ let mut route_hints = sort_and_filter_channels(channels, amt_msat, &logger);
- // If we have any public channel, the route hints from `filter_channels` will be empty.
- // In that case we create a RouteHint on which we will push a single hop with the phantom
- // route into the invoice, and let the sender find the path to the `real_node_pubkey`
+ // If we have any public channel, the route hints from `sort_and_filter_channels` will be
+ // empty. In that case we create a RouteHint on which we will push a single hop with the
+ // phantom route into the invoice, and let the sender find the path to the `real_node_pubkey`
// node by looking at our public channels.
if route_hints.is_empty() {
route_hints.push(RouteHint(vec![]))
}
- for mut route_hint in route_hints {
+ for route_hint in &mut route_hints {
route_hint.0.push(RouteHintHop {
src_node_id: real_node_pubkey,
short_channel_id: phantom_scid,
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
htlc_minimum_msat: None,
htlc_maximum_msat: None,});
- invoice = invoice.private_route(route_hint.clone());
}
+
+ phantom_hints.push(route_hints);
}
- let raw_invoice = match invoice.build_raw() {
- 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));
- match signed_raw_invoice {
- Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
- Err(e) => Err(SignOrCreationError::SignError(e))
+ // We have one vector per real node involved in creating the phantom invoice. To distribute
+ // the hints across our real nodes we add one hint from each in turn until no node has any hints
+ // left (if one node has more hints than any other, these will accumulate at the end of the
+ // vector).
+ let mut invoice_hints: Vec<RouteHint> = Vec::new();
+ let mut hint_idx = 0;
+
+ loop {
+ let mut remaining_hints = false;
+
+ for hints in phantom_hints.iter() {
+ if invoice_hints.len() == 3 {
+ return invoice_hints
+ }
+
+ if hint_idx < hints.len() {
+ invoice_hints.push(hints[hint_idx].clone());
+ remaining_hints = true
+ }
+ }
+
+ if !remaining_hints {
+ return invoice_hints
+ }
+
+ hint_idx +=1;
}
}
invoice = invoice.amount_milli_satoshis(amt);
}
- let route_hints = filter_channels(channels, amt_msat, &logger);
+ let route_hints = sort_and_filter_channels(channels, amt_msat, &logger);
for hint in route_hints {
invoice = invoice.private_route(hint);
}
}
}
-/// Filters the `channels` for an invoice, and returns the corresponding `RouteHint`s to include
+/// Sorts and filters the `channels` for an invoice, and returns the corresponding `RouteHint`s to include
/// in the invoice.
///
/// The filtering is based on the following criteria:
/// * Only one channel per counterparty node
-/// * Always select the channel with the highest inbound capacity per counterparty node
+/// * If the counterparty has a channel that is above the `min_inbound_capacity_msat` + 10% scaling
+/// factor (to allow some margin for change in inbound), select the channel with the lowest
+/// inbound capacity that is above this threshold.
+/// * If no `min_inbound_capacity_msat` is specified, or the counterparty has no channels above the
+/// minimum + 10% scaling factor, select the channel with the highest inbound capacity per counterparty.
/// * Prefer channels with capacity at least `min_inbound_capacity_msat` and where the channel
/// `is_usable` (i.e. the peer is connected).
/// * If any public channel exists, only public [`RouteHint`]s will be returned.
/// * If any public, announced, channel exists (i.e. a channel with 7+ confs, to ensure the
/// announcement has had a chance to propagate), no [`RouteHint`]s will be returned, as the
/// sender is expected to find the path by looking at the public channels instead.
-fn filter_channels<L: Deref>(
+/// * Limited to a total of 3 channels.
+/// * Sorted by lowest inbound capacity if an online channel with the minimum amount requested exists,
+/// otherwise sort by highest inbound capacity to give the payment the best chance of succeeding.
+fn sort_and_filter_channels<L: Deref>(
channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>, logger: &L
) -> Vec<RouteHint> where L::Target: Logger {
let mut filtered_channels: HashMap<PublicKey, ChannelDetails> = HashMap::new();
// If this channel is public and the previous channel is not, ensure we replace the
// previous channel to avoid announcing non-public channels.
let new_now_public = channel.is_public && !entry.get().is_public;
+ // Decide whether we prefer the currently selected channel with the node to the new one,
+ // based on their inbound capacity.
+ let prefer_current = prefer_current_channel(min_inbound_capacity_msat, current_max_capacity,
+ channel.inbound_capacity_msat);
// If the public-ness of the channel has not changed (in which case simply defer to
- // `new_now_public), and this channel has a greater capacity, prefer to announce
- // this channel.
- let new_higher_capacity = channel.is_public == entry.get().is_public &&
- channel.inbound_capacity_msat > current_max_capacity;
- if new_now_public || new_higher_capacity {
+ // `new_now_public), and this channel has more desirable inbound than the incumbent,
+ // prefer to include this channel.
+ let new_channel_preferable = channel.is_public == entry.get().is_public && !prefer_current;
+
+ if new_now_public || new_channel_preferable {
log_trace!(logger,
"Preferring counterparty {} channel {} (SCID {:?}, {} msats) over {} (SCID {:?}, {} msats) for invoice route hints",
log_pubkey!(channel.counterparty.node_id),
// the payment value and where we're currently connected to the channel counterparty.
// Even if we cannot satisfy both goals, always ensure we include *some* hints, preferring
// those which meet at least one criteria.
- filtered_channels
+ let mut eligible_channels = filtered_channels
.into_iter()
.map(|(_, channel)| channel)
.filter(|channel| {
include_channel
})
- .map(route_hint_from_channel)
- .collect::<Vec<RouteHint>>()
+ .collect::<Vec<ChannelDetails>>();
+
+ eligible_channels.sort_unstable_by(|a, b| {
+ if online_min_capacity_channel_exists {
+ a.inbound_capacity_msat.cmp(&b.inbound_capacity_msat)
+ } else {
+ b.inbound_capacity_msat.cmp(&a.inbound_capacity_msat)
+ }});
+ eligible_channels.into_iter().take(3).map(route_hint_from_channel).collect::<Vec<RouteHint>>()
+}
+
+/// prefer_current_channel chooses a channel to use for route hints between a currently selected and candidate
+/// channel based on the inbound capacity of each channel and the minimum inbound capacity requested for the hints,
+/// returning true if the current channel should be preferred over the candidate channel.
+/// * If no minimum amount is requested, the channel with the most inbound is chosen to maximize the chances that a
+/// payment of any size will succeed.
+/// * If we have channels with inbound above our minimum requested inbound (plus a 10% scaling factor, expressed as a
+/// percentage) then we choose the lowest inbound channel with above this amount. If we have sufficient inbound
+/// channels, we don't want to deplete our larger channels with small payments (the off-chain version of "grinding
+/// our change").
+/// * If no channel above our minimum amount exists, then we just prefer the channel with the most inbound to give
+/// payments the best chance of succeeding in multiple parts.
+fn prefer_current_channel(min_inbound_capacity_msat: Option<u64>, current_channel: u64,
+ candidate_channel: u64) -> bool {
+
+ // If no min amount is given for the hints, err of the side of caution and choose the largest channel inbound to
+ // maximize chances of any payment succeeding.
+ if min_inbound_capacity_msat.is_none() {
+ return current_channel > candidate_channel
+ }
+
+ let scaled_min_inbound = min_inbound_capacity_msat.unwrap() * 110;
+ let current_sufficient = current_channel * 100 >= scaled_min_inbound;
+ let candidate_sufficient = candidate_channel * 100 >= scaled_min_inbound;
+
+ if current_sufficient && candidate_sufficient {
+ return current_channel < candidate_channel
+ } else if current_sufficient {
+ return true
+ } else if candidate_sufficient {
+ return false
+ }
+
+ current_channel > candidate_channel
}
#[cfg(test)]
use crate::{Currency, Description, InvoiceDescription, SignOrCreationError, CreationError};
use bitcoin_hashes::{Hash, sha256};
use bitcoin_hashes::sha256::Hash as Sha256;
- use lightning::chain::keysinterface::PhantomKeysManager;
+ use lightning::sign::PhantomKeysManager;
use lightning::events::{MessageSendEvent, MessageSendEventsProvider, Event};
use lightning::ln::{PaymentPreimage, PaymentHash};
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry};
use crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch;
use std::collections::HashSet;
+ #[test]
+ fn test_prefer_current_channel() {
+ // No minimum, prefer larger candidate channel.
+ assert_eq!(crate::utils::prefer_current_channel(None, 100, 200), false);
+
+ // No minimum, prefer larger current channel.
+ assert_eq!(crate::utils::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);
+
+ // Minimum set, prefer candidate channel over minimum + buffer.
+ assert_eq!(crate::utils::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);
+
+ // Minimum set, both channels sufficient, prefer smaller candidate channel.
+ assert_eq!(crate::utils::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);
+
+ // Minimum set, neither sufficient, prefer larger candidate channel.
+ assert_eq!(crate::utils::prefer_current_channel(Some(200), 100, 150), false);
+ }
+
+
#[test]
fn test_from_channelmanager() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
invoice.min_final_cltv_expiry_delta() as u32)
- .with_features(invoice.features().unwrap().clone())
- .with_route_hints(invoice.route_hints());
+ .with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
+ .with_route_hints(invoice.route_hints()).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: invoice.amount_milli_satoshis().unwrap(),
}
#[test]
- fn test_hints_has_only_highest_inbound_capacity_channel() {
+ fn test_hints_has_only_lowest_inbound_capacity_channel_above_minimum() {
let chanmon_cfgs = create_chanmon_cfgs(2);
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 _chan_1_0_low_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 100_000, 0);
- let chan_1_0_high_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 10_000_000, 0);
- let _chan_1_0_medium_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 1_000_000, 0);
+
+ let _chan_1_0_inbound_below_amt = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 10_000, 0);
+ let _chan_1_0_large_inbound_above_amt = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 500_000, 0);
+ let chan_1_0_low_inbound_above_amt = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 200_000, 0);
+
let mut scid_aliases = HashSet::new();
- scid_aliases.insert(chan_1_0_high_inbound_capacity.0.short_channel_id_alias.unwrap());
- match_invoice_routes(Some(5000), &nodes[0], scid_aliases);
+ scid_aliases.insert(chan_1_0_low_inbound_above_amt.0.short_channel_id_alias.unwrap());
+ match_invoice_routes(Some(100_000_000), &nodes[0], scid_aliases);
}
#[test]
match_invoice_routes(Some(1_000_000_000), &nodes[0], scid_aliases);
}
+ #[test]
+ fn test_insufficient_inbound_sort_by_highest_capacity() {
+ let chanmon_cfgs = create_chanmon_cfgs(5);
+ let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]);
+ let nodes = create_network(5, &node_cfgs, &node_chanmgrs);
+ let _chan_1_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 100_000, 0);
+ let chan_2_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 2, 0, 200_000, 0);
+ let chan_3_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 3, 0, 300_000, 0);
+ let chan_4_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 4, 0, 400_000, 0);
+
+ // When no single channel has enough inbound capacity for the payment, we expect the three
+ // highest inbound channels to be chosen.
+ let mut scid_aliases = HashSet::new();
+ scid_aliases.insert(chan_2_0.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_3_0.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_4_0.0.short_channel_id_alias.unwrap());
+
+ match_invoice_routes(Some(1_000_000_000), &nodes[0], scid_aliases.clone());
+ }
+
+ #[test]
+ fn test_sufficient_inbound_sort_by_lowest_capacity() {
+ let chanmon_cfgs = create_chanmon_cfgs(5);
+ let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]);
+ let nodes = create_network(5, &node_cfgs, &node_chanmgrs);
+ let chan_1_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 100_000, 0);
+ let chan_2_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 2, 0, 200_000, 0);
+ let chan_3_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 3, 0, 300_000, 0);
+ let _chan_4_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 4, 0, 400_000, 0);
+
+ // When we have channels that have sufficient inbound for the payment, test that we sort
+ // by lowest inbound capacity.
+ let mut scid_aliases = HashSet::new();
+ scid_aliases.insert(chan_1_0.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_2_0.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_3_0.0.short_channel_id_alias.unwrap());
+
+ match_invoice_routes(Some(50_000_000), &nodes[0], scid_aliases.clone());
+ }
+
#[test]
fn test_forwarding_info_not_assigned_channel_excluded_from_hints() {
let chanmon_cfgs = create_chanmon_cfgs(3);
let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
invoice.min_final_cltv_expiry_delta() as u32)
- .with_features(invoice.features().unwrap().clone())
- .with_route_hints(invoice.route_hints());
+ .with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
+ .with_route_hints(invoice.route_hints()).unwrap();
let params = RouteParameters {
payment_params,
final_value_msat: invoice.amount_milli_satoshis().unwrap(),
#[test]
#[cfg(feature = "std")]
- fn test_multi_node_hints_has_only_highest_inbound_capacity_channel() {
+ fn test_multi_node_hints_has_only_lowest_inbound_channel_above_minimum() {
let mut chanmon_cfgs = create_chanmon_cfgs(3);
let seed_1 = [42u8; 32];
let seed_2 = [43u8; 32];
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
- let _chan_0_1_low_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0);
- let chan_0_1_high_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 0);
- let _chan_0_1_medium_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
+ let _chan_0_1_below_amt = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0);
+ let _chan_0_1_above_amt_high_inbound = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 500_000, 0);
+ let chan_0_1_above_amt_low_inbound = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 180_000, 0);
let chan_0_2 = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001);
let mut scid_aliases = HashSet::new();
- scid_aliases.insert(chan_0_1_high_inbound_capacity.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_0_1_above_amt_low_inbound.0.short_channel_id_alias.unwrap());
scid_aliases.insert(chan_0_2.0.short_channel_id_alias.unwrap());
match_multi_node_invoice_routes(
- Some(10_000),
+ Some(100_000_000),
&nodes[1],
vec![&nodes[1], &nodes[2],],
scid_aliases,
);
}
- #[cfg(feature = "std")]
+ #[test]
+ fn test_multi_node_hints_limited_to_3() {
+ let mut chanmon_cfgs = create_chanmon_cfgs(6);
+ let seed_1 = [42 as u8; 32];
+ let seed_2 = [43 as u8; 32];
+ let seed_3 = [44 as u8; 32];
+ let seed_4 = [45 as u8; 32];
+ let cross_node_seed = [44 as u8; 32];
+ chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
+ chanmon_cfgs[3].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
+ chanmon_cfgs[4].keys_manager.backing = PhantomKeysManager::new(&seed_3, 43, 44, &cross_node_seed);
+ chanmon_cfgs[5].keys_manager.backing = PhantomKeysManager::new(&seed_4, 43, 44, &cross_node_seed);
+ let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(6, &node_cfgs, &[None, None, None, None, None, None]);
+ let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
+
+ // Setup each phantom node with two channels from distinct peers.
+ let chan_0_2 = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 2, 10_000, 0);
+ let chan_1_2 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 20_000, 0);
+ let chan_0_3 = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 3, 20_000, 0);
+ let _chan_1_3 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 3, 10_000, 0);
+ let chan_0_4 = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 4, 20_000, 0);
+ let _chan_1_4 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000, 0);
+ let _chan_0_5 = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 5, 20_000, 0);
+ let _chan_1_5 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000, 0);
+
+ // Set invoice amount > all channels inbound so that every one is eligible for inclusion
+ // and hints will be sorted by largest inbound capacity.
+ let invoice_amt = Some(100_000_000);
+
+ // With 4 phantom nodes, assert that we include 1 hint per node, up to 3 nodes.
+ let mut scid_aliases = HashSet::new();
+ scid_aliases.insert(chan_1_2.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_0_3.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_0_4.0.short_channel_id_alias.unwrap());
+
+ match_multi_node_invoice_routes(
+ invoice_amt,
+ &nodes[3],
+ vec![&nodes[2], &nodes[3], &nodes[4], &nodes[5]],
+ scid_aliases,
+ false,
+ );
+
+ // With 2 phantom nodes, assert that we include no more than 3 hints.
+ let mut scid_aliases = HashSet::new();
+ scid_aliases.insert(chan_1_2.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_0_3.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_0_2.0.short_channel_id_alias.unwrap());
+
+ match_multi_node_invoice_routes(
+ invoice_amt,
+ &nodes[3],
+ vec![&nodes[2], &nodes[3]],
+ scid_aliases,
+ false,
+ );
+ }
+
+ #[test]
+ fn test_multi_node_hints_at_least_3() {
+ let mut chanmon_cfgs = create_chanmon_cfgs(5);
+ let seed_1 = [42 as u8; 32];
+ let seed_2 = [43 as u8; 32];
+ let cross_node_seed = [44 as u8; 32];
+ chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
+ chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
+ let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]);
+ let nodes = create_network(5, &node_cfgs, &node_chanmgrs);
+
+ let _chan_0_3 = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 3, 10_000, 0);
+ let chan_1_3 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 3, 20_000, 0);
+ let chan_2_3 = create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 30_000, 0);
+ let chan_0_4 = create_unannounced_chan_between_nodes_with_value(&nodes, 0, 4, 10_000, 0);
+
+ // Since the invoice amount is above all channels inbound, all four are eligible. Test that
+ // we still include 3 hints from 2 distinct nodes sorted by inbound.
+ let mut scid_aliases = HashSet::new();
+ scid_aliases.insert(chan_1_3.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_2_3.0.short_channel_id_alias.unwrap());
+ scid_aliases.insert(chan_0_4.0.short_channel_id_alias.unwrap());
+
+ match_multi_node_invoice_routes(
+ Some(100_000_000),
+ &nodes[3],
+ vec![&nodes[3], &nodes[4],],
+ scid_aliases,
+ false,
+ );
+ }
+
fn match_multi_node_invoice_routes<'a, 'b: 'a, 'c: 'b>(
invoice_amt: Option<u64>,
invoice_node: &Node<'a, 'b, 'c>,
[package]
name = "lightning-net-tokio"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Matt Corallo"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/lightningdevkit/rust-lightning/"
[dependencies]
bitcoin = "0.29.0"
-lightning = { version = "0.0.114", path = "../lightning" }
+lightning = { version = "0.0.115", path = "../lightning" }
tokio = { version = "1.0", features = [ "io-util", "macros", "rt", "sync", "net", "time" ] }
[dev-dependencies]
tokio = { version = "1.14", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] }
-lightning = { version = "0.0.114", path = "../lightning", features = ["_test_utils"] }
+lightning = { version = "0.0.115", path = "../lightning", features = ["_test_utils"] }
use tokio::sync::mpsc;
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt};
-use lightning::chain::keysinterface::NodeSigner;
use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;
-use lightning::ln::peer_handler::CustomMessageHandler;
-use lightning::ln::msgs::{ChannelMessageHandler, NetAddress, OnionMessageHandler, RoutingMessageHandler};
-use lightning::util::logger::Logger;
+use lightning::ln::peer_handler::APeerManager;
+use lightning::ln::msgs::NetAddress;
use std::ops::Deref;
use std::task;
id: u64,
}
impl Connection {
- async fn poll_event_process<PM, CMH, RMH, OMH, L, UMH, NS>(
+ async fn poll_event_process<PM: Deref + 'static + Send + Sync>(
peer_manager: PM,
mut event_receiver: mpsc::Receiver<()>,
- ) where
- PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH, NS>> + 'static + Send + Sync,
- CMH: Deref + 'static + Send + Sync,
- RMH: Deref + 'static + Send + Sync,
- OMH: Deref + 'static + Send + Sync,
- L: Deref + 'static + Send + Sync,
- UMH: Deref + 'static + Send + Sync,
- NS: Deref + 'static + Send + Sync,
- CMH::Target: ChannelMessageHandler + Send + Sync,
- RMH::Target: RoutingMessageHandler + Send + Sync,
- OMH::Target: OnionMessageHandler + Send + Sync,
- L::Target: Logger + Send + Sync,
- UMH::Target: CustomMessageHandler + Send + Sync,
- NS::Target: NodeSigner + Send + Sync,
- {
+ ) where PM::Target: APeerManager<Descriptor = SocketDescriptor> {
loop {
if event_receiver.recv().await.is_none() {
return;
}
- peer_manager.process_events();
+ peer_manager.as_ref().process_events();
}
}
- async fn schedule_read<PM, CMH, RMH, OMH, L, UMH, NS>(
+ async fn schedule_read<PM: Deref + 'static + Send + Sync + Clone>(
peer_manager: PM,
us: Arc<Mutex<Self>>,
mut reader: io::ReadHalf<TcpStream>,
mut read_wake_receiver: mpsc::Receiver<()>,
mut write_avail_receiver: mpsc::Receiver<()>,
- ) where
- PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH, NS>> + 'static + Send + Sync + Clone,
- CMH: Deref + 'static + Send + Sync,
- RMH: Deref + 'static + Send + Sync,
- OMH: Deref + 'static + Send + Sync,
- L: Deref + 'static + Send + Sync,
- UMH: Deref + 'static + Send + Sync,
- NS: Deref + 'static + Send + Sync,
- CMH::Target: ChannelMessageHandler + 'static + Send + Sync,
- RMH::Target: RoutingMessageHandler + 'static + Send + Sync,
- OMH::Target: OnionMessageHandler + 'static + Send + Sync,
- L::Target: Logger + 'static + Send + Sync,
- UMH::Target: CustomMessageHandler + 'static + Send + Sync,
- NS::Target: NodeSigner + 'static + Send + Sync,
- {
+ ) where PM::Target: APeerManager<Descriptor = SocketDescriptor> {
// Create a waker to wake up poll_event_process, above
let (event_waker, event_receiver) = mpsc::channel(1);
tokio::spawn(Self::poll_event_process(peer_manager.clone(), event_receiver));
tokio::select! {
v = write_avail_receiver.recv() => {
assert!(v.is_some()); // We can't have dropped the sending end, its in the us Arc!
- if peer_manager.write_buffer_space_avail(&mut our_descriptor).is_err() {
+ if peer_manager.as_ref().write_buffer_space_avail(&mut our_descriptor).is_err() {
break Disconnect::CloseConnection;
}
},
read = reader.read(&mut buf), if !read_paused => match read {
Ok(0) => break Disconnect::PeerDisconnected,
Ok(len) => {
- let read_res = peer_manager.read_event(&mut our_descriptor, &buf[0..len]);
+ let read_res = peer_manager.as_ref().read_event(&mut our_descriptor, &buf[0..len]);
let mut us_lock = us.lock().unwrap();
match read_res {
Ok(pause_read) => {
let _ = writer.shutdown().await;
}
if let Disconnect::PeerDisconnected = disconnect_type {
- peer_manager.socket_disconnected(&our_descriptor);
- peer_manager.process_events();
+ peer_manager.as_ref().socket_disconnected(&our_descriptor);
+ peer_manager.as_ref().process_events();
}
}
/// The returned future will complete when the peer is disconnected and associated handling
/// futures are freed, though, because all processing futures are spawned with tokio::spawn, you do
/// not need to poll the provided future in order to make progress.
-pub fn setup_inbound<PM, CMH, RMH, OMH, L, UMH, NS>(
+pub fn setup_inbound<PM: Deref + 'static + Send + Sync + Clone>(
peer_manager: PM,
stream: StdTcpStream,
-) -> impl std::future::Future<Output=()> where
- PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH, NS>> + 'static + Send + Sync + Clone,
- CMH: Deref + 'static + Send + Sync,
- RMH: Deref + 'static + Send + Sync,
- OMH: Deref + 'static + Send + Sync,
- L: Deref + 'static + Send + Sync,
- UMH: Deref + 'static + Send + Sync,
- NS: Deref + 'static + Send + Sync,
- CMH::Target: ChannelMessageHandler + Send + Sync,
- RMH::Target: RoutingMessageHandler + Send + Sync,
- OMH::Target: OnionMessageHandler + Send + Sync,
- L::Target: Logger + Send + Sync,
- UMH::Target: CustomMessageHandler + Send + Sync,
- NS::Target: NodeSigner + Send + Sync,
-{
+) -> impl std::future::Future<Output=()>
+where PM::Target: APeerManager<Descriptor = SocketDescriptor> {
let remote_addr = get_addr_from_stream(&stream);
let (reader, write_receiver, read_receiver, us) = Connection::new(stream);
#[cfg(test)]
let last_us = Arc::clone(&us);
- let handle_opt = if peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone()), remote_addr).is_ok() {
+ let handle_opt = if peer_manager.as_ref().new_inbound_connection(SocketDescriptor::new(us.clone()), remote_addr).is_ok() {
Some(tokio::spawn(Connection::schedule_read(peer_manager, us, reader, read_receiver, write_receiver)))
} else {
// Note that we will skip socket_disconnected here, in accordance with the PeerManager
/// The returned future will complete when the peer is disconnected and associated handling
/// futures are freed, though, because all processing futures are spawned with tokio::spawn, you do
/// not need to poll the provided future in order to make progress.
-pub fn setup_outbound<PM, CMH, RMH, OMH, L, UMH, NS>(
+pub fn setup_outbound<PM: Deref + 'static + Send + Sync + Clone>(
peer_manager: PM,
their_node_id: PublicKey,
stream: StdTcpStream,
-) -> impl std::future::Future<Output=()> where
- PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH, NS>> + 'static + Send + Sync + Clone,
- CMH: Deref + 'static + Send + Sync,
- RMH: Deref + 'static + Send + Sync,
- OMH: Deref + 'static + Send + Sync,
- L: Deref + 'static + Send + Sync,
- UMH: Deref + 'static + Send + Sync,
- NS: Deref + 'static + Send + Sync,
- CMH::Target: ChannelMessageHandler + Send + Sync,
- RMH::Target: RoutingMessageHandler + Send + Sync,
- OMH::Target: OnionMessageHandler + Send + Sync,
- L::Target: Logger + Send + Sync,
- UMH::Target: CustomMessageHandler + Send + Sync,
- NS::Target: NodeSigner + Send + Sync,
-{
+) -> impl std::future::Future<Output=()>
+where PM::Target: APeerManager<Descriptor = SocketDescriptor> {
let remote_addr = get_addr_from_stream(&stream);
let (reader, mut write_receiver, read_receiver, us) = Connection::new(stream);
#[cfg(test)]
let last_us = Arc::clone(&us);
- let handle_opt = if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone()), remote_addr) {
+ let handle_opt = if let Ok(initial_send) = peer_manager.as_ref().new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone()), remote_addr) {
Some(tokio::spawn(async move {
// We should essentially always have enough room in a TCP socket buffer to send the
// initial 10s of bytes. However, tokio running in single-threaded mode will always
},
_ => {
eprintln!("Failed to write first full message to socket!");
- peer_manager.socket_disconnected(&SocketDescriptor::new(Arc::clone(&us)));
+ peer_manager.as_ref().socket_disconnected(&SocketDescriptor::new(Arc::clone(&us)));
break Err(());
}
}
/// disconnected and associated handling futures are freed, though, because all processing in said
/// futures are spawned with tokio::spawn, you do not need to poll the second future in order to
/// make progress.
-pub async fn connect_outbound<PM, CMH, RMH, OMH, L, UMH, NS>(
+pub async fn connect_outbound<PM: Deref + 'static + Send + Sync + Clone>(
peer_manager: PM,
their_node_id: PublicKey,
addr: SocketAddr,
-) -> Option<impl std::future::Future<Output=()>> where
- PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH, NS>> + 'static + Send + Sync + Clone,
- CMH: Deref + 'static + Send + Sync,
- RMH: Deref + 'static + Send + Sync,
- OMH: Deref + 'static + Send + Sync,
- L: Deref + 'static + Send + Sync,
- UMH: Deref + 'static + Send + Sync,
- NS: Deref + 'static + Send + Sync,
- CMH::Target: ChannelMessageHandler + Send + Sync,
- RMH::Target: RoutingMessageHandler + Send + Sync,
- OMH::Target: OnionMessageHandler + Send + Sync,
- L::Target: Logger + Send + Sync,
- UMH::Target: CustomMessageHandler + Send + Sync,
- NS::Target: NodeSigner + Send + Sync,
-{
+) -> Option<impl std::future::Future<Output=()>>
+where PM::Target: APeerManager<Descriptor = SocketDescriptor> {
if let Ok(Ok(stream)) = time::timeout(Duration::from_secs(10), async { TcpStream::connect(&addr).await.map(|s| s.into_std().unwrap()) }).await {
Some(setup_outbound(peer_manager, their_node_id, stream))
} else { None }
fn handle_update_fee(&self, _their_node_id: &PublicKey, _msg: &UpdateFee) {}
fn handle_announcement_signatures(&self, _their_node_id: &PublicKey, _msg: &AnnouncementSignatures) {}
fn handle_channel_update(&self, _their_node_id: &PublicKey, _msg: &ChannelUpdate) {}
+ fn handle_open_channel_v2(&self, _their_node_id: &PublicKey, _msg: &OpenChannelV2) {}
+ fn handle_accept_channel_v2(&self, _their_node_id: &PublicKey, _msg: &AcceptChannelV2) {}
+ fn handle_tx_add_input(&self, _their_node_id: &PublicKey, _msg: &TxAddInput) {}
+ fn handle_tx_add_output(&self, _their_node_id: &PublicKey, _msg: &TxAddOutput) {}
+ fn handle_tx_remove_input(&self, _their_node_id: &PublicKey, _msg: &TxRemoveInput) {}
+ fn handle_tx_remove_output(&self, _their_node_id: &PublicKey, _msg: &TxRemoveOutput) {}
+ fn handle_tx_complete(&self, _their_node_id: &PublicKey, _msg: &TxComplete) {}
+ fn handle_tx_signatures(&self, _their_node_id: &PublicKey, _msg: &TxSignatures) {}
+ fn handle_tx_init_rbf(&self, _their_node_id: &PublicKey, _msg: &TxInitRbf) {}
+ fn handle_tx_ack_rbf(&self, _their_node_id: &PublicKey, _msg: &TxAckRbf) {}
+ fn handle_tx_abort(&self, _their_node_id: &PublicKey, _msg: &TxAbort) {}
fn peer_disconnected(&self, their_node_id: &PublicKey) {
if *their_node_id == self.expected_pubkey {
self.disconnected_flag.store(true, Ordering::SeqCst);
chan_handler: Arc::clone(&a_handler),
route_handler: Arc::clone(&a_handler),
onion_message_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}),
- }, 0, &[1; 32], Arc::new(TestLogger()), Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}), Arc::new(TestNodeSigner::new(a_key))));
+ custom_message_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}),
+ }, 0, &[1; 32], Arc::new(TestLogger()), Arc::new(TestNodeSigner::new(a_key))));
let (b_connected_sender, mut b_connected) = mpsc::channel(1);
let (b_disconnected_sender, mut b_disconnected) = mpsc::channel(1);
chan_handler: Arc::clone(&b_handler),
route_handler: Arc::clone(&b_handler),
onion_message_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}),
- }, 0, &[2; 32], Arc::new(TestLogger()), Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}), Arc::new(TestNodeSigner::new(b_key))));
+ custom_message_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}),
+ }, 0, &[2; 32], Arc::new(TestLogger()), Arc::new(TestNodeSigner::new(b_key))));
// We bind on localhost, hoping the environment is properly configured with a local
// address. This may not always be the case in containers and the like, so if this test is
chan_handler: Arc::new(lightning::ln::peer_handler::ErroringMessageHandler::new()),
onion_message_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}),
route_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}),
- }, 0, &[1; 32], Arc::new(TestLogger()), Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}), Arc::new(TestNodeSigner::new(a_key))));
+ custom_message_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler{}),
+ }, 0, &[1; 32], Arc::new(TestLogger()), Arc::new(TestNodeSigner::new(a_key))));
// Make two connections, one for an inbound and one for an outbound connection
let conn_a = {
[package]
name = "lightning-persister"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Valentine Wallace", "Matt Corallo"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/lightningdevkit/rust-lightning/"
[dependencies]
bitcoin = "0.29.0"
-lightning = { version = "0.0.114", path = "../lightning" }
+lightning = { version = "0.0.115", path = "../lightning" }
libc = "0.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }
[dev-dependencies]
-lightning = { version = "0.0.114", path = "../lightning", features = ["_test_utils"] }
+lightning = { version = "0.0.115", path = "../lightning", features = ["_test_utils"] }
use bitcoin::hash_types::{BlockHash, Txid};
use bitcoin::hashes::hex::FromHex;
use lightning::chain::channelmonitor::ChannelMonitor;
-use lightning::chain::keysinterface::{EntropySource, SignerProvider};
+use lightning::sign::{EntropySource, SignerProvider};
use lightning::util::ser::{ReadableArgs, Writeable};
use lightning::util::persist::KVStorePersister;
use std::fs;
extern crate lightning;
extern crate bitcoin;
use crate::FilesystemPersister;
- use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::hashes::hex::FromHex;
- use bitcoin::{Txid, TxMerkleNode};
+ use bitcoin::Txid;
use lightning::chain::ChannelMonitorUpdateStatus;
use lightning::chain::chainmonitor::Persist;
use lightning::chain::channelmonitor::CLOSED_CHANNEL_UPDATE_ID;
use lightning::ln::functional_test_utils::*;
use lightning::util::test_utils;
use std::fs;
- use bitcoin::hashes::Hash;
#[cfg(target_os = "windows")]
use {
lightning::get_event_msg,
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[0].clone()]});
+ connect_block(&nodes[1], &create_dummy_block(nodes[0].best_block_hash(), 42, vec![node_txn[0].clone(), node_txn[0].clone()]));
check_closed_broadcast!(nodes[1], true);
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
check_added_monitors!(nodes[1], 1);
[package]
name = "lightning-rapid-gossip-sync"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Arik Sosman <git@arik.io>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/lightningdevkit/rust-lightning"
_bench_unstable = []
[dependencies]
-lightning = { version = "0.0.114", path = "../lightning", default-features = false }
+lightning = { version = "0.0.115", path = "../lightning", default-features = false }
bitcoin = { version = "0.29.0", default-features = false }
[dev-dependencies]
-lightning = { version = "0.0.114", path = "../lightning", features = ["_test_utils"] }
+lightning = { version = "0.0.115", path = "../lightning", features = ["_test_utils"] }
[package]
name = "lightning-transaction-sync"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Elias Rohrer"]
license = "MIT OR Apache-2.0"
repository = "http://github.com/lightningdevkit/rust-lightning"
async-interface = []
[dependencies]
-lightning = { version = "0.0.114", path = "../lightning", default-features = false }
+lightning = { version = "0.0.115", path = "../lightning", default-features = false }
bitcoin = { version = "0.29.0", default-features = false }
bdk-macros = "0.6"
futures = { version = "0.3", optional = true }
reqwest = { version = "0.11", optional = true, default-features = false, features = ["json"] }
[dev-dependencies]
-lightning = { version = "0.0.114", path = "../lightning", features = ["std"] }
+lightning = { version = "0.0.115", path = "../lightning", features = ["std"] }
electrsd = { version = "0.22.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_23_0"] }
electrum-client = "0.12.0"
tokio = { version = "1.14.0", features = ["full"] }
//!
//! ## Version Compatibility
//!
-//! Currently this crate is compatible with nodes that were created with LDK version 0.0.113 and above.
+//! Currently this crate is compatible with LDK version 0.0.114 and above using channels which were
+//! created on LDK version 0.0.113 and above.
//!
//! ## Usage Example:
//!
[package]
name = "lightning"
-version = "0.0.114"
+version = "0.0.115"
authors = ["Matt Corallo"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/lightningdevkit/rust-lightning/"
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
-use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
+use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::onion_message::ControlTlvs;
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils;
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::chain::keysinterface::WriteableEcdsaChannelSigner;
+use crate::sign::WriteableEcdsaChannelSigner;
use crate::events;
use crate::events::{Event, EventHandler};
use crate::util::atomic_counter::AtomicCounter;
#[cfg(test)]
mod tests {
- use bitcoin::{BlockHeader, TxMerkleNode};
- use bitcoin::hashes::Hash;
use crate::{check_added_monitors, check_closed_broadcast, check_closed_event};
use crate::{expect_payment_sent, expect_payment_claimed, expect_payment_sent_without_paths, expect_payment_path_successful, get_event_msg};
use crate::{get_htlc_update_msgs, get_local_commitment_txn, get_revoke_commit_msgs, get_route_and_payment_hash, unwrap_send_err};
// Connect B's commitment transaction, but only to the ChainMonitor/ChannelMonitor. The
// channel is now closed, but the ChannelManager doesn't know that yet.
- let new_header = BlockHeader {
- version: 2, time: 0, bits: 0, nonce: 0,
- prev_blockhash: nodes[0].best_block_info().0,
- merkle_root: TxMerkleNode::all_zeros() };
+ let new_header = create_dummy_header(nodes[0].best_block_info().0, 0);
nodes[0].chain_monitor.chain_monitor.transactions_confirmed(&new_header,
&[(0, &remote_txn[0]), (1, &remote_txn[1])], nodes[0].best_block_info().1 + 1);
assert!(nodes[0].chain_monitor.release_pending_monitor_events().is_empty());
if block_timeout {
// After three blocks, pending MontiorEvents should be released either way.
- let latest_header = BlockHeader {
- version: 2, time: 0, bits: 0, nonce: 0,
- prev_blockhash: nodes[0].best_block_info().0,
- merkle_root: TxMerkleNode::all_zeros() };
+ let latest_header = create_dummy_header(nodes[0].best_block_info().0, 0);
nodes[0].chain_monitor.chain_monitor.best_block_updated(&latest_header, nodes[0].best_block_info().1 + LATENCY_GRACE_PERIOD_BLOCKS);
} else {
let persistences = chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap().clone();
use crate::chain::{BestBlock, WatchedOutput};
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, WriteableEcdsaChannelSigner, SignerProvider, EntropySource};
+use crate::sign::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, WriteableEcdsaChannelSigner, SignerProvider, EntropySource};
#[cfg(anchors)]
use crate::chain::onchaintx::ClaimEvent;
use crate::chain::onchaintx::OnchainTxHandler;
/// The height at which the counterparty may be able to claim the balance if we have not
/// done so.
timeout_height: u32,
+ /// The payment hash that locks this HTLC.
+ payment_hash: PaymentHash,
+ /// The preimage that can be used to claim this HTLC.
+ payment_preimage: PaymentPreimage,
},
/// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain
/// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat
/// The height at which we will be able to claim the balance if our counterparty has not
/// done so.
claimable_height: u32,
+ /// The payment hash whose preimage our counterparty needs to claim this HTLC.
+ payment_hash: PaymentHash,
},
/// HTLCs which we received from our counterparty which are claimable with a preimage which we
/// do not currently have. This will only be claimable if we receive the preimage from the node
/// The height at which our counterparty will be able to claim the balance if we have not
/// yet received the preimage and claimed it ourselves.
expiry_height: u32,
+ /// The payment hash whose preimage we need to claim this HTLC.
+ payment_hash: PaymentHash,
},
/// The channel has been closed, and our counterparty broadcasted a revoked commitment
/// transaction.
return Some(Balance::MaybeTimeoutClaimableHTLC {
claimable_amount_satoshis: htlc.amount_msat / 1000,
claimable_height: htlc.cltv_expiry,
+ payment_hash: htlc.payment_hash,
});
}
- } else if self.payment_preimages.get(&htlc.payment_hash).is_some() {
+ } else if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
// Otherwise (the payment was inbound), only expose it as claimable if
// we know the preimage.
// Note that if there is a pending claim, but it did not use the
return Some(Balance::ContentiousClaimable {
claimable_amount_satoshis: htlc.amount_msat / 1000,
timeout_height: htlc.cltv_expiry,
+ payment_hash: htlc.payment_hash,
+ payment_preimage: *payment_preimage,
});
}
} else if htlc_resolved.is_none() {
return Some(Balance::MaybePreimageClaimableHTLC {
claimable_amount_satoshis: htlc.amount_msat / 1000,
expiry_height: htlc.cltv_expiry,
+ payment_hash: htlc.payment_hash,
});
}
None
res.push(Balance::MaybeTimeoutClaimableHTLC {
claimable_amount_satoshis: htlc.amount_msat / 1000,
claimable_height: htlc.cltv_expiry,
+ payment_hash: htlc.payment_hash,
});
} else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
res.push(Balance::MaybePreimageClaimableHTLC {
claimable_amount_satoshis: htlc.amount_msat / 1000,
expiry_height: htlc.cltv_expiry,
+ payment_hash: htlc.payment_hash,
});
}
}
F::Target: FeeEstimator,
L::Target: Logger,
{
- log_info!(logger, "Applying update to monitor {}, bringing update_id from {} to {} with {} changes.",
- log_funding_info!(self), self.latest_update_id, updates.update_id, updates.updates.len());
+ if self.latest_update_id == CLOSED_CHANNEL_UPDATE_ID && updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
+ log_info!(logger, "Applying post-force-closed update to monitor {} with {} change(s).",
+ log_funding_info!(self), updates.updates.len());
+ } else if updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
+ log_info!(logger, "Applying force close update to monitor {} with {} change(s).",
+ log_funding_info!(self), updates.updates.len());
+ } else {
+ log_info!(logger, "Applying update to monitor {}, bringing update_id from {} to {} with {} change(s).",
+ log_funding_info!(self), self.latest_update_id, updates.update_id, updates.updates.len());
+ }
// ChannelMonitor updates may be applied after force close if we receive a preimage for a
// broadcasted commitment transaction HTLC output that we'd like to claim on-chain. If this
// is the case, we no longer have guaranteed access to the monitor's update ID, so we use a
_ => false,
}).is_some();
if detected_funding_spend {
+ log_trace!(logger, "Avoiding commitment broadcast, already detected confirmed spend onchain");
continue;
}
self.broadcast_latest_holder_commitment_txn(broadcaster, logger);
let commitment_package = PackageTemplate::build_package(
self.funding_info.0.txid.clone(), self.funding_info.0.index as u32,
PackageSolvingData::HolderFundingOutput(funding_output),
- best_block_height, false, best_block_height,
+ best_block_height, best_block_height
);
self.onchain_tx_handler.update_claims_view_from_requests(
vec![commitment_package], best_block_height, best_block_height,
self.latest_update_id = updates.update_id;
- if ret.is_ok() && self.funding_spend_seen {
+ // Refuse updates after we've detected a spend onchain, but only if we haven't processed a
+ // force closed monitor update yet.
+ if ret.is_ok() && self.funding_spend_seen && self.latest_update_id != CLOSED_CHANNEL_UPDATE_ID {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
- let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv);
- let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv, self.onchain_tx_handler.opt_anchors());
+ let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, height);
claimable_outpoints.push(justice_package);
to_counterparty_output_info =
Some((idx.try_into().expect("Txn can't have more than 2^32 outputs"), outp.value));
to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), self.onchain_tx_handler.channel_transaction_parameters.opt_anchors.is_some());
- let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
+ let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, height);
claimable_outpoints.push(justice_package);
}
}
self.counterparty_commitment_params.counterparty_htlc_base_key,
htlc.clone(), self.onchain_tx_handler.opt_anchors()))
};
- let aggregation = if !htlc.offered { false } else { true };
- let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry,aggregation, 0);
+ let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry, 0);
claimable_outpoints.push(counterparty_package);
}
}
let revk_outp = RevokedOutput::build(
per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key,
- tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv
+ tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv,
+ false
);
let justice_package = PackageTemplate::build_package(
htlc_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp),
- height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height
+ height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, height
);
claimable_outpoints.push(justice_package);
if outputs_to_watch.is_none() {
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
- let (htlc_output, aggregable) = if htlc.offered {
+ let htlc_output = if htlc.offered {
let htlc_output = HolderHTLCOutput::build_offered(
htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.opt_anchors()
);
- (htlc_output, false)
+ htlc_output
} else {
let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
preimage.clone()
let htlc_output = HolderHTLCOutput::build_accepted(
payment_preimage, htlc.amount_msat, self.onchain_tx_handler.opt_anchors()
);
- (htlc_output, self.onchain_tx_handler.opt_anchors())
+ htlc_output
};
let htlc_package = PackageTemplate::build_package(
holder_tx.txid, transaction_output_index,
PackageSolvingData::HolderHTLCOutput(htlc_output),
- htlc.cltv_expiry, aggregable, conf_height
+ htlc.cltv_expiry, conf_height
);
claim_requests.push(htlc_package);
}
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
if should_broadcast {
let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone(), self.channel_value_satoshis, self.onchain_tx_handler.opt_anchors());
- let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), false, self.best_block.height());
+ let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), self.best_block.height());
claimable_outpoints.push(commitment_package);
self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(self.funding_info.0));
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
#[cfg(test)]
mod tests {
- use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, EcdsaSighashType};
use crate::chain::channelmonitor::ChannelMonitor;
use crate::chain::package::{weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT};
use crate::chain::transaction::OutPoint;
- use crate::chain::keysinterface::InMemorySigner;
+ use crate::sign::InMemorySigner;
use crate::events::ClosureReason;
use crate::ln::{PaymentPreimage, PaymentHash};
use crate::ln::chan_utils;
use crate::util::ser::{ReadableArgs, Writeable};
use crate::sync::{Arc, Mutex};
use crate::io;
- use bitcoin::{PackedLockTime, Sequence, TxMerkleNode, Witness};
+ use bitcoin::{PackedLockTime, Sequence, Witness};
use crate::prelude::*;
fn do_test_funding_spend_refuses_updates(use_local_txn: bool) {
// Connect a commitment transaction, but only to the ChainMonitor/ChannelMonitor. The
// channel is now closed, but the ChannelManager doesn't know that yet.
- let new_header = BlockHeader {
- version: 2, time: 0, bits: 0, nonce: 0,
- prev_blockhash: nodes[0].best_block_info().0,
- merkle_root: TxMerkleNode::all_zeros() };
+ let new_header = create_dummy_header(nodes[0].best_block_info().0, 0);
let conf_height = nodes[0].best_block_info().1 + 1;
nodes[1].chain_monitor.chain_monitor.transactions_confirmed(&new_header,
&[(0, broadcast_tx)], conf_height);
+++ /dev/null
-// This file is Copyright its original authors, visible in version control
-// history.
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Provides keys to LDK and defines some useful objects describing spendable on-chain outputs.
-//!
-//! The provided output descriptors follow a custom LDK data format and are currently not fully
-//! compatible with Bitcoin Core output descriptors.
-
-use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, EcdsaSighashType};
-use bitcoin::blockdata::script::{Script, Builder};
-use bitcoin::blockdata::opcodes;
-use bitcoin::network::constants::Network;
-use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber};
-use bitcoin::util::sighash;
-
-use bitcoin::bech32::u5;
-use bitcoin::hashes::{Hash, HashEngine};
-use bitcoin::hashes::sha256::Hash as Sha256;
-use bitcoin::hashes::sha256d::Hash as Sha256dHash;
-use bitcoin::hash_types::WPubkeyHash;
-
-use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
-use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Signing};
-use bitcoin::secp256k1::ecdh::SharedSecret;
-use bitcoin::secp256k1::ecdsa::RecoverableSignature;
-use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
-
-use crate::util::transaction_utils;
-use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
-use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
-use crate::chain::transaction::OutPoint;
-#[cfg(anchors)]
-use crate::events::bump_transaction::HTLCDescriptor;
-use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
-use crate::ln::{chan_utils, PaymentPreimage};
-use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
-use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
-use crate::ln::script::ShutdownScript;
-
-use crate::prelude::*;
-use core::convert::TryInto;
-use core::ops::Deref;
-use core::sync::atomic::{AtomicUsize, Ordering};
-use crate::io::{self, Error};
-use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
-use crate::util::atomic_counter::AtomicCounter;
-use crate::util::chacha20::ChaCha20;
-use crate::util::invoice::construct_invoice_preimage;
-
-/// Used as initial key material, to be expanded into multiple secret keys (but not to be used
-/// directly). This is used within LDK to encrypt/decrypt inbound payment data.
-///
-/// This is not exported to bindings users as we just use `[u8; 32]` directly
-#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
-pub struct KeyMaterial(pub [u8; 32]);
-
-/// Information about a spendable output to a P2WSH script.
-///
-/// See [`SpendableOutputDescriptor::DelayedPaymentOutput`] for more details on how to spend this.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct DelayedPaymentOutputDescriptor {
- /// The outpoint which is spendable.
- pub outpoint: OutPoint,
- /// Per commitment point to derive the delayed payment key by key holder.
- pub per_commitment_point: PublicKey,
- /// The `nSequence` value which must be set in the spending input to satisfy the `OP_CSV` in
- /// the witness_script.
- pub to_self_delay: u16,
- /// The output which is referenced by the given outpoint.
- pub output: TxOut,
- /// The revocation point specific to the commitment transaction which was broadcast. Used to
- /// derive the witnessScript for this output.
- pub revocation_pubkey: PublicKey,
- /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`].
- /// This may be useful in re-deriving keys used in the channel to spend the output.
- pub channel_keys_id: [u8; 32],
- /// The value of the channel which this output originated from, possibly indirectly.
- pub channel_value_satoshis: u64,
-}
-impl DelayedPaymentOutputDescriptor {
- /// The maximum length a well-formed witness spending one of these should have.
- // Calculated as 1 byte length + 73 byte signature, 1 byte empty vec push, 1 byte length plus
- // redeemscript push length.
- pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1;
-}
-
-impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
- (0, outpoint, required),
- (2, per_commitment_point, required),
- (4, to_self_delay, required),
- (6, output, required),
- (8, revocation_pubkey, required),
- (10, channel_keys_id, required),
- (12, channel_value_satoshis, required),
-});
-
-/// Information about a spendable output to our "payment key".
-///
-/// See [`SpendableOutputDescriptor::StaticPaymentOutput`] for more details on how to spend this.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct StaticPaymentOutputDescriptor {
- /// The outpoint which is spendable.
- pub outpoint: OutPoint,
- /// The output which is referenced by the given outpoint.
- pub output: TxOut,
- /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`].
- /// This may be useful in re-deriving keys used in the channel to spend the output.
- pub channel_keys_id: [u8; 32],
- /// The value of the channel which this transactions spends.
- pub channel_value_satoshis: u64,
-}
-impl StaticPaymentOutputDescriptor {
- /// The maximum length a well-formed witness spending one of these should have.
- // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
- // redeemscript push length.
- pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34;
-}
-impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, {
- (0, outpoint, required),
- (2, output, required),
- (4, channel_keys_id, required),
- (6, channel_value_satoshis, required),
-});
-
-/// Describes the necessary information to spend a spendable output.
-///
-/// When on-chain outputs are created by LDK (which our counterparty is not able to claim at any
-/// point in the future) a [`SpendableOutputs`] event is generated which you must track and be able
-/// to spend on-chain. The information needed to do this is provided in this enum, including the
-/// outpoint describing which `txid` and output `index` is available, the full output which exists
-/// at that `txid`/`index`, and any keys or other information required to sign.
-///
-/// [`SpendableOutputs`]: crate::events::Event::SpendableOutputs
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum SpendableOutputDescriptor {
- /// An output to a script which was provided via [`SignerProvider`] directly, either from
- /// [`get_destination_script`] or [`get_shutdown_scriptpubkey`], thus you should already
- /// know how to spend it. No secret keys are provided as LDK was never given any key.
- /// These may include outputs from a transaction punishing our counterparty or claiming an HTLC
- /// on-chain using the payment preimage or after it has timed out.
- ///
- /// [`get_shutdown_scriptpubkey`]: SignerProvider::get_shutdown_scriptpubkey
- /// [`get_destination_script`]: SignerProvider::get_shutdown_scriptpubkey
- StaticOutput {
- /// The outpoint which is spendable.
- outpoint: OutPoint,
- /// The output which is referenced by the given outpoint.
- output: TxOut,
- },
- /// An output to a P2WSH script which can be spent with a single signature after an `OP_CSV`
- /// delay.
- ///
- /// The witness in the spending input should be:
- /// ```bitcoin
- /// <BIP 143 signature> <empty vector> (MINIMALIF standard rule) <provided witnessScript>
- /// ```
- ///
- /// Note that the `nSequence` field in the spending input must be set to
- /// [`DelayedPaymentOutputDescriptor::to_self_delay`] (which means the transaction is not
- /// broadcastable until at least [`DelayedPaymentOutputDescriptor::to_self_delay`] blocks after
- /// the outpoint confirms, see [BIP
- /// 68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki)). Also note that LDK
- /// won't generate a [`SpendableOutputDescriptor`] until the corresponding block height
- /// is reached.
- ///
- /// These are generally the result of a "revocable" output to us, spendable only by us unless
- /// it is an output from an old state which we broadcast (which should never happen).
- ///
- /// To derive the delayed payment key which is used to sign this input, you must pass the
- /// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which corresponds to the
- /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`ChannelSigner::pubkeys`]) and the provided
- /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The public key can be
- /// generated without the secret key using [`chan_utils::derive_public_key`] and only the
- /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`ChannelSigner::pubkeys`].
- ///
- /// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is
- /// used in the witness script generation), you must pass the counterparty
- /// [`ChannelPublicKeys::revocation_basepoint`] (which appears in the call to
- /// [`ChannelSigner::provide_channel_parameters`]) and the provided
- /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to
- /// [`chan_utils::derive_public_revocation_key`].
- ///
- /// The witness script which is hashed and included in the output `script_pubkey` may be
- /// regenerated by passing the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] (derived
- /// as explained above), our delayed payment pubkey (derived as explained above), and the
- /// [`DelayedPaymentOutputDescriptor::to_self_delay`] contained here to
- /// [`chan_utils::get_revokeable_redeemscript`].
- DelayedPaymentOutput(DelayedPaymentOutputDescriptor),
- /// An output to a P2WPKH, spendable exclusively by our payment key (i.e., the private key
- /// which corresponds to the `payment_point` in [`ChannelSigner::pubkeys`]). The witness
- /// in the spending input is, thus, simply:
- /// ```bitcoin
- /// <BIP 143 signature> <payment key>
- /// ```
- ///
- /// These are generally the result of our counterparty having broadcast the current state,
- /// allowing us to claim the non-HTLC-encumbered outputs immediately.
- StaticPaymentOutput(StaticPaymentOutputDescriptor),
-}
-
-impl_writeable_tlv_based_enum!(SpendableOutputDescriptor,
- (0, StaticOutput) => {
- (0, outpoint, required),
- (2, output, required),
- },
-;
- (1, DelayedPaymentOutput),
- (2, StaticPaymentOutput),
-);
-
-/// A trait to handle Lightning channel key material without concretizing the channel type or
-/// the signature mechanism.
-pub trait ChannelSigner {
- /// Gets the per-commitment point for a specific commitment number
- ///
- /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
- fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey;
-
- /// Gets the commitment secret for a specific commitment number as part of the revocation process
- ///
- /// An external signer implementation should error here if the commitment was already signed
- /// and should refuse to sign it in the future.
- ///
- /// May be called more than once for the same index.
- ///
- /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
- // TODO: return a Result so we can signal a validation error
- fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
-
- /// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
- ///
- /// This is required in order for the signer to make sure that releasing a commitment
- /// secret won't leave us without a broadcastable holder transaction.
- /// Policy checks should be implemented in this function, including checking the amount
- /// sent to us and checking the HTLCs.
- ///
- /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
- /// A validating signer should ensure that an HTLC output is removed only when the matching
- /// preimage is provided, or when the value to holder is restored.
- ///
- /// Note that all the relevant preimages will be provided, but there may also be additional
- /// irrelevant or duplicate preimages.
- fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction,
- preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
-
- /// Returns the holder's channel public keys and basepoints.
- fn pubkeys(&self) -> &ChannelPublicKeys;
-
- /// Returns an arbitrary identifier describing the set of keys which are provided back to you in
- /// some [`SpendableOutputDescriptor`] types. This should be sufficient to identify this
- /// [`EcdsaChannelSigner`] object uniquely and lookup or re-derive its keys.
- fn channel_keys_id(&self) -> [u8; 32];
-
- /// Set the counterparty static channel data, including basepoints,
- /// `counterparty_selected`/`holder_selected_contest_delay` and funding outpoint.
- ///
- /// This data is static, and will never change for a channel once set. For a given [`ChannelSigner`]
- /// instance, LDK will call this method exactly once - either immediately after construction
- /// (not including if done via [`SignerProvider::read_chan_signer`]) or when the funding
- /// information has been generated.
- ///
- /// channel_parameters.is_populated() MUST be true.
- fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters);
-}
-
-/// A trait to sign Lightning channel transactions as described in
-/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md).
-///
-/// Signing services could be implemented on a hardware wallet and should implement signing
-/// policies in order to be secure. Please refer to the [VLS Policy
-/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md)
-/// for an example of such policies.
-pub trait EcdsaChannelSigner: ChannelSigner {
- /// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
- ///
- /// Note that if signing fails or is rejected, the channel will be force-closed.
- ///
- /// Policy checks should be implemented in this function, including checking the amount
- /// sent to us and checking the HTLCs.
- ///
- /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
- /// A validating signer should ensure that an HTLC output is removed only when the matching
- /// preimage is provided, or when the value to holder is restored.
- ///
- /// Note that all the relevant preimages will be provided, but there may also be additional
- /// irrelevant or duplicate preimages.
- //
- // TODO: Document the things someone using this interface should enforce before signing.
- fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction,
- preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>
- ) -> Result<(Signature, Vec<Signature>), ()>;
- /// Validate the counterparty's revocation.
- ///
- /// This is required in order for the signer to make sure that the state has moved
- /// forward and it is safe to sign the next counterparty commitment.
- fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>;
- /// Creates a signature for a holder's commitment transaction and its claiming HTLC transactions.
- ///
- /// This will be called
- /// - with a non-revoked `commitment_tx`.
- /// - with the latest `commitment_tx` when we initiate a force-close.
- /// - with the previous `commitment_tx`, just to get claiming HTLC
- /// signatures, if we are reacting to a [`ChannelMonitor`]
- /// [replica](https://github.com/lightningdevkit/rust-lightning/blob/main/GLOSSARY.md#monitor-replicas)
- /// that decided to broadcast before it had been updated to the latest `commitment_tx`.
- ///
- /// This may be called multiple times for the same transaction.
- ///
- /// An external signer implementation should check that the commitment has not been revoked.
- ///
- /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
- // TODO: Document the things someone using this interface should enforce before signing.
- fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction,
- secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
- /// Same as [`sign_holder_commitment_and_htlcs`], but exists only for tests to get access to
- /// holder commitment transactions which will be broadcasted later, after the channel has moved
- /// on to a newer state. Thus, needs its own method as [`sign_holder_commitment_and_htlcs`] may
- /// enforce that we only ever get called once.
- #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
- fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction,
- secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
- /// Create a signature for the given input in a transaction spending an HTLC transaction output
- /// or a commitment transaction `to_local` output when our counterparty broadcasts an old state.
- ///
- /// A justice transaction may claim multiple outputs at the same time if timelocks are
- /// similar, but only a signature for the input at index `input` should be signed for here.
- /// It may be called multiple times for same output(s) if a fee-bump is needed with regards
- /// to an upcoming timelock expiration.
- ///
- /// Amount is value of the output spent by this input, committed to in the BIP 143 signature.
- ///
- /// `per_commitment_key` is revocation secret which was provided by our counterparty when they
- /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does
- /// not allow the spending of any funds by itself (you need our holder `revocation_secret` to do
- /// so).
- fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64,
- per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>
- ) -> Result<Signature, ()>;
- /// Create a signature for the given input in a transaction spending a commitment transaction
- /// HTLC output when our counterparty broadcasts an old state.
- ///
- /// A justice transaction may claim multiple outputs at the same time if timelocks are
- /// similar, but only a signature for the input at index `input` should be signed for here.
- /// It may be called multiple times for same output(s) if a fee-bump is needed with regards
- /// to an upcoming timelock expiration.
- ///
- /// `amount` is the value of the output spent by this input, committed to in the BIP 143
- /// signature.
- ///
- /// `per_commitment_key` is revocation secret which was provided by our counterparty when they
- /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does
- /// not allow the spending of any funds by itself (you need our holder revocation_secret to do
- /// so).
- ///
- /// `htlc` holds HTLC elements (hash, timelock), thus changing the format of the witness script
- /// (which is committed to in the BIP 143 signatures).
- fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64,
- per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment,
- secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
- #[cfg(anchors)]
- /// Computes the signature for a commitment transaction's HTLC output used as an input within
- /// `htlc_tx`, which spends the commitment transaction at index `input`. The signature returned
- /// must be be computed using [`EcdsaSighashType::All`]. Note that this should only be used to
- /// sign HTLC transactions from channels supporting anchor outputs after all additional
- /// inputs/outputs have been added to the transaction.
- ///
- /// [`EcdsaSighashType::All`]: bitcoin::blockdata::transaction::EcdsaSighashType::All
- fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize,
- htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1<secp256k1::All>
- ) -> Result<Signature, ()>;
- /// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment
- /// transaction, either offered or received.
- ///
- /// Such a transaction may claim multiples offered outputs at same time if we know the
- /// preimage for each when we create it, but only the input at index `input` should be
- /// signed for here. It may be called multiple times for same output(s) if a fee-bump is
- /// needed with regards to an upcoming timelock expiration.
- ///
- /// `witness_script` is either an offered or received script as defined in BOLT3 for HTLC
- /// outputs.
- ///
- /// `amount` is value of the output spent by this input, committed to in the BIP 143 signature.
- ///
- /// `per_commitment_point` is the dynamic point corresponding to the channel state
- /// detected onchain. It has been generated by our counterparty and is used to derive
- /// channel state keys, which are then included in the witness script and committed to in the
- /// BIP 143 signature.
- fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64,
- per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment,
- secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
- /// Create a signature for a (proposed) closing transaction.
- ///
- /// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
- /// chosen to forgo their output as dust.
- fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction,
- secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
- /// Computes the signature for a commitment transaction's anchor output used as an
- /// input within `anchor_tx`, which spends the commitment transaction, at index `input`.
- fn sign_holder_anchor_input(
- &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
- ) -> Result<Signature, ()>;
- /// Signs a channel announcement message with our funding key proving it comes from one of the
- /// channel participants.
- ///
- /// Channel announcements also require a signature from each node's network key. Our node
- /// signature is computed through [`NodeSigner::sign_gossip_message`].
- ///
- /// Note that if this fails or is rejected, the channel will not be publicly announced and
- /// our counterparty may (though likely will not) close the channel on us for violating the
- /// protocol.
- fn sign_channel_announcement_with_funding_key(
- &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
- ) -> Result<Signature, ()>;
-}
-
-/// A writeable signer.
-///
-/// There will always be two instances of a signer per channel, one occupied by the
-/// [`ChannelManager`] and another by the channel's [`ChannelMonitor`].
-///
-/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
-/// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
-pub trait WriteableEcdsaChannelSigner: EcdsaChannelSigner + Writeable {}
-
-/// Specifies the recipient of an invoice.
-///
-/// This indicates to [`NodeSigner::sign_invoice`] what node secret key should be used to sign
-/// the invoice.
-pub enum Recipient {
- /// The invoice should be signed with the local node secret key.
- Node,
- /// The invoice should be signed with the phantom node secret key. This secret key must be the
- /// same for all nodes participating in the [phantom node payment].
- ///
- /// [phantom node payment]: PhantomKeysManager
- PhantomNode,
-}
-
-/// A trait that describes a source of entropy.
-pub trait EntropySource {
- /// Gets a unique, cryptographically-secure, random 32-byte value. This method must return a
- /// different value each time it is called.
- fn get_secure_random_bytes(&self) -> [u8; 32];
-}
-
-/// A trait that can handle cryptographic operations at the scope level of a node.
-pub trait NodeSigner {
- /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data.
- ///
- /// If the implementor of this trait supports [phantom node payments], then every node that is
- /// intended to be included in the phantom invoice route hints must return the same value from
- /// this method.
- // This is because LDK avoids storing inbound payment data by encrypting payment data in the
- // payment hash and/or payment secret, therefore for a payment to be receivable by multiple
- // nodes, they must share the key that encrypts this payment data.
- ///
- /// This method must return the same value each time it is called.
- ///
- /// [phantom node payments]: PhantomKeysManager
- fn get_inbound_payment_key_material(&self) -> KeyMaterial;
-
- /// Get node id based on the provided [`Recipient`].
- ///
- /// This method must return the same value each time it is called with a given [`Recipient`]
- /// parameter.
- ///
- /// Errors if the [`Recipient`] variant is not supported by the implementation.
- fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()>;
-
- /// Gets the ECDH shared secret of our node secret and `other_key`, multiplying by `tweak` if
- /// one is provided. Note that this tweak can be applied to `other_key` instead of our node
- /// secret, though this is less efficient.
- ///
- /// Note that if this fails while attempting to forward an HTLC, LDK will panic. The error
- /// should be resolved to allow LDK to resume forwarding HTLCs.
- ///
- /// Errors if the [`Recipient`] variant is not supported by the implementation.
- fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()>;
-
- /// Sign an invoice.
- ///
- /// By parameterizing by the raw invoice bytes instead of the hash, we allow implementors of
- /// this trait to parse the invoice and make sure they're signing what they expect, rather than
- /// blindly signing the hash.
- ///
- /// The `hrp_bytes` are ASCII bytes, while the `invoice_data` is base32.
- ///
- /// The secret key used to sign the invoice is dependent on the [`Recipient`].
- ///
- /// Errors if the [`Recipient`] variant is not supported by the implementation.
- fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()>;
-
- /// Sign a gossip message.
- ///
- /// Note that if this fails, LDK may panic and the message will not be broadcast to the network
- /// or a possible channel counterparty. If LDK panics, the error should be resolved to allow the
- /// message to be broadcast, as otherwise it may prevent one from receiving funds over the
- /// corresponding channel.
- fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()>;
-}
-
-/// A trait that can return signer instances for individual channels.
-pub trait SignerProvider {
- /// A type which implements [`WriteableEcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`].
- type Signer : WriteableEcdsaChannelSigner;
-
- /// Generates a unique `channel_keys_id` that can be used to obtain a [`Self::Signer`] through
- /// [`SignerProvider::derive_channel_signer`]. The `user_channel_id` is provided to allow
- /// implementations of [`SignerProvider`] to maintain a mapping between itself and the generated
- /// `channel_keys_id`.
- ///
- /// This method must return a different value each time it is called.
- fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32];
-
- /// Derives the private key material backing a `Signer`.
- ///
- /// To derive a new `Signer`, a fresh `channel_keys_id` should be obtained through
- /// [`SignerProvider::generate_channel_keys_id`]. Otherwise, an existing `Signer` can be
- /// re-derived from its `channel_keys_id`, which can be obtained through its trait method
- /// [`ChannelSigner::channel_keys_id`].
- fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer;
-
- /// Reads a [`Signer`] for this [`SignerProvider`] from the given input stream.
- /// This is only called during deserialization of other objects which contain
- /// [`WriteableEcdsaChannelSigner`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s).
- /// The bytes are exactly those which `<Self::Signer as Writeable>::write()` writes, and
- /// contain no versioning scheme. You may wish to include your own version prefix and ensure
- /// you've read all of the provided bytes to ensure no corruption occurred.
- ///
- /// This method is slowly being phased out -- it will only be called when reading objects
- /// written by LDK versions prior to 0.0.113.
- ///
- /// [`Signer`]: Self::Signer
- /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
- /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
- fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError>;
-
- /// Get a script pubkey which we send funds to when claiming on-chain contestable outputs.
- ///
- /// This method should return a different value each time it is called, to avoid linking
- /// on-chain funds across channels as controlled to the same user.
- fn get_destination_script(&self) -> Script;
-
- /// Get a script pubkey which we will send funds to when closing a channel.
- ///
- /// This method should return a different value each time it is called, to avoid linking
- /// on-chain funds across channels as controlled to the same user.
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript;
-}
-
-/// A simple implementation of [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
-///
-/// This implementation performs no policy checks and is insufficient by itself as
-/// a secure external signer.
-pub struct InMemorySigner {
- /// Holder secret key in the 2-of-2 multisig script of a channel. This key also backs the
- /// holder's anchor output in a commitment transaction, if one is present.
- pub funding_key: SecretKey,
- /// Holder secret key for blinded revocation pubkey.
- pub revocation_base_key: SecretKey,
- /// Holder secret key used for our balance in counterparty-broadcasted commitment transactions.
- pub payment_key: SecretKey,
- /// Holder secret key used in an HTLC transaction.
- pub delayed_payment_base_key: SecretKey,
- /// Holder HTLC secret key used in commitment transaction HTLC outputs.
- pub htlc_base_key: SecretKey,
- /// Commitment seed.
- pub commitment_seed: [u8; 32],
- /// Holder public keys and basepoints.
- pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
- /// Counterparty public keys and counterparty/holder `selected_contest_delay`, populated on channel acceptance.
- channel_parameters: Option<ChannelTransactionParameters>,
- /// The total value of this channel.
- channel_value_satoshis: u64,
- /// Key derivation parameters.
- channel_keys_id: [u8; 32],
- /// Seed from which all randomness produced is derived from.
- rand_bytes_unique_start: [u8; 32],
- /// Tracks the number of times we've produced randomness to ensure we don't return the same
- /// bytes twice.
- rand_bytes_index: AtomicCounter,
-}
-
-impl Clone for InMemorySigner {
- fn clone(&self) -> Self {
- Self {
- funding_key: self.funding_key.clone(),
- revocation_base_key: self.revocation_base_key.clone(),
- payment_key: self.payment_key.clone(),
- delayed_payment_base_key: self.delayed_payment_base_key.clone(),
- htlc_base_key: self.htlc_base_key.clone(),
- commitment_seed: self.commitment_seed.clone(),
- holder_channel_pubkeys: self.holder_channel_pubkeys.clone(),
- channel_parameters: self.channel_parameters.clone(),
- channel_value_satoshis: self.channel_value_satoshis,
- channel_keys_id: self.channel_keys_id,
- rand_bytes_unique_start: self.get_secure_random_bytes(),
- rand_bytes_index: AtomicCounter::new(),
- }
- }
-}
-
-impl InMemorySigner {
- /// Creates a new [`InMemorySigner`].
- pub fn new<C: Signing>(
- secp_ctx: &Secp256k1<C>,
- funding_key: SecretKey,
- revocation_base_key: SecretKey,
- payment_key: SecretKey,
- delayed_payment_base_key: SecretKey,
- htlc_base_key: SecretKey,
- commitment_seed: [u8; 32],
- channel_value_satoshis: u64,
- channel_keys_id: [u8; 32],
- rand_bytes_unique_start: [u8; 32],
- ) -> InMemorySigner {
- let holder_channel_pubkeys =
- InMemorySigner::make_holder_keys(secp_ctx, &funding_key, &revocation_base_key,
- &payment_key, &delayed_payment_base_key,
- &htlc_base_key);
- InMemorySigner {
- funding_key,
- revocation_base_key,
- payment_key,
- delayed_payment_base_key,
- htlc_base_key,
- commitment_seed,
- channel_value_satoshis,
- holder_channel_pubkeys,
- channel_parameters: None,
- channel_keys_id,
- rand_bytes_unique_start,
- rand_bytes_index: AtomicCounter::new(),
- }
- }
-
- fn make_holder_keys<C: Signing>(secp_ctx: &Secp256k1<C>,
- funding_key: &SecretKey,
- revocation_base_key: &SecretKey,
- payment_key: &SecretKey,
- delayed_payment_base_key: &SecretKey,
- htlc_base_key: &SecretKey) -> ChannelPublicKeys {
- let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s);
- ChannelPublicKeys {
- funding_pubkey: from_secret(&funding_key),
- revocation_basepoint: from_secret(&revocation_base_key),
- payment_point: from_secret(&payment_key),
- delayed_payment_basepoint: from_secret(&delayed_payment_base_key),
- htlc_basepoint: from_secret(&htlc_base_key),
- }
- }
-
- /// Returns the counterparty's pubkeys.
- ///
- /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
- pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().pubkeys }
- /// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable
- /// transactions, i.e., the amount of time that we have to wait to recover our funds if we
- /// broadcast a transaction.
- ///
- /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
- pub fn counterparty_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().selected_contest_delay }
- /// Returns the `contest_delay` value specified by us and applied on transactions broadcastable
- /// by our counterparty, i.e., the amount of time that they have to wait to recover their funds
- /// if they broadcast a transaction.
- ///
- /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
- pub fn holder_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().holder_selected_contest_delay }
- /// Returns whether the holder is the initiator.
- ///
- /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
- pub fn is_outbound(&self) -> bool { self.get_channel_parameters().is_outbound_from_holder }
- /// Funding outpoint
- ///
- /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
- pub fn funding_outpoint(&self) -> &OutPoint { self.get_channel_parameters().funding_outpoint.as_ref().unwrap() }
- /// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or
- /// building transactions.
- ///
- /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
- pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
- self.channel_parameters.as_ref().unwrap()
- }
- /// Returns whether anchors should be used.
- ///
- /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
- pub fn opt_anchors(&self) -> bool {
- self.get_channel_parameters().opt_anchors.is_some()
- }
- /// Sign the single input of `spend_tx` at index `input_idx`, which spends the output described
- /// by `descriptor`, returning the witness stack for the input.
- ///
- /// Returns an error if the input at `input_idx` does not exist, has a non-empty `script_sig`,
- /// is not spending the outpoint described by [`descriptor.outpoint`],
- /// or if an output descriptor `script_pubkey` does not match the one we can spend.
- ///
- /// [`descriptor.outpoint`]: StaticPaymentOutputDescriptor::outpoint
- pub fn sign_counterparty_payment_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
- // TODO: We really should be taking the SigHashCache as a parameter here instead of
- // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
- // so that we can check them. This requires upstream rust-bitcoin changes (as well as
- // bindings updates to support SigHashCache objects).
- if spend_tx.input.len() <= input_idx { return Err(()); }
- if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
- if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
-
- let remotepubkey = self.pubkeys().payment_point;
- let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Testnet).script_pubkey();
- let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
- let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
- let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
-
- if payment_script != descriptor.output.script_pubkey { return Err(()); }
-
- let mut witness = Vec::with_capacity(2);
- witness.push(remotesig.serialize_der().to_vec());
- witness[0].push(EcdsaSighashType::All as u8);
- witness.push(remotepubkey.serialize().to_vec());
- Ok(witness)
- }
-
- /// Sign the single input of `spend_tx` at index `input_idx` which spends the output
- /// described by `descriptor`, returning the witness stack for the input.
- ///
- /// Returns an error if the input at `input_idx` does not exist, has a non-empty `script_sig`,
- /// is not spending the outpoint described by [`descriptor.outpoint`], does not have a
- /// sequence set to [`descriptor.to_self_delay`], or if an output descriptor
- /// `script_pubkey` does not match the one we can spend.
- ///
- /// [`descriptor.outpoint`]: DelayedPaymentOutputDescriptor::outpoint
- /// [`descriptor.to_self_delay`]: DelayedPaymentOutputDescriptor::to_self_delay
- pub fn sign_dynamic_p2wsh_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
- // TODO: We really should be taking the SigHashCache as a parameter here instead of
- // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
- // so that we can check them. This requires upstream rust-bitcoin changes (as well as
- // bindings updates to support SigHashCache objects).
- if spend_tx.input.len() <= input_idx { return Err(()); }
- if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
- if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
- if spend_tx.input[input_idx].sequence.0 != descriptor.to_self_delay as u32 { return Err(()); }
-
- let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key);
- let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
- let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
- let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
- let local_delayedsig = sign_with_aux_rand(secp_ctx, &sighash, &delayed_payment_key, &self);
- let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
-
- if descriptor.output.script_pubkey != payment_script { return Err(()); }
-
- let mut witness = Vec::with_capacity(3);
- witness.push(local_delayedsig.serialize_der().to_vec());
- witness[0].push(EcdsaSighashType::All as u8);
- witness.push(vec!()); //MINIMALIF
- witness.push(witness_script.clone().into_bytes());
- Ok(witness)
- }
-}
-
-impl EntropySource for InMemorySigner {
- fn get_secure_random_bytes(&self) -> [u8; 32] {
- let index = self.rand_bytes_index.get_increment();
- let mut nonce = [0u8; 16];
- nonce[..8].copy_from_slice(&index.to_be_bytes());
- ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
- }
-}
-
-impl ChannelSigner for InMemorySigner {
- fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey {
- let commitment_secret = SecretKey::from_slice(&chan_utils::build_commitment_secret(&self.commitment_seed, idx)).unwrap();
- PublicKey::from_secret_key(secp_ctx, &commitment_secret)
- }
-
- fn release_commitment_secret(&self, idx: u64) -> [u8; 32] {
- chan_utils::build_commitment_secret(&self.commitment_seed, idx)
- }
-
- fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
- Ok(())
- }
-
- fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
-
- fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
-
- fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) {
- assert!(self.channel_parameters.is_none() || self.channel_parameters.as_ref().unwrap() == channel_parameters);
- if self.channel_parameters.is_some() {
- // The channel parameters were already set and they match, return early.
- return;
- }
- assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated");
- self.channel_parameters = Some(channel_parameters.clone());
- }
-}
-
-impl EcdsaChannelSigner for InMemorySigner {
- fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
- let trusted_tx = commitment_tx.trust();
- let keys = trusted_tx.keys();
-
- let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
- let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
-
- let built_tx = trusted_tx.built_transaction();
- let commitment_sig = built_tx.sign_counterparty_commitment(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
- let commitment_txid = built_tx.txid;
-
- let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
- for htlc in commitment_tx.htlcs() {
- let channel_parameters = self.get_channel_parameters();
- let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), self.holder_selected_contest_delay(), htlc, self.opt_anchors(), channel_parameters.opt_non_zero_fee_anchors.is_some(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
- let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys);
- let htlc_sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
- let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]);
- let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key);
- htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key));
- }
-
- Ok((commitment_sig, htlc_sigs))
- }
-
- fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> {
- Ok(())
- }
-
- fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
- let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
- let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
- let trusted_tx = commitment_tx.trust();
- let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
- let channel_parameters = self.get_channel_parameters();
- let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
- Ok((sig, htlc_sigs))
- }
-
- #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
- fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
- let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
- let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
- let trusted_tx = commitment_tx.trust();
- let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
- let channel_parameters = self.get_channel_parameters();
- let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
- Ok((sig, htlc_sigs))
- }
-
- fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
- let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
- let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
- let witness_script = {
- let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint);
- chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey)
- };
- let mut sighash_parts = sighash::SighashCache::new(justice_tx);
- let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
- return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
- }
-
- fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
- let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
- let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
- let witness_script = {
- let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
- let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
- chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
- };
- let mut sighash_parts = sighash::SighashCache::new(justice_tx);
- let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
- return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
- }
-
- #[cfg(anchors)]
- fn sign_holder_htlc_transaction(
- &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor,
- secp_ctx: &Secp256k1<secp256k1::All>
- ) -> Result<Signature, ()> {
- let per_commitment_point = self.get_per_commitment_point(
- htlc_descriptor.per_commitment_number, &secp_ctx
- );
- let witness_script = htlc_descriptor.witness_script(&per_commitment_point, secp_ctx);
- let sighash = &sighash::SighashCache::new(&*htlc_tx).segwit_signature_hash(
- input, &witness_script, htlc_descriptor.htlc.amount_msat / 1000, EcdsaSighashType::All
- ).map_err(|_| ())?;
- let our_htlc_private_key = chan_utils::derive_private_key(
- &secp_ctx, &per_commitment_point, &self.htlc_base_key
- );
- Ok(sign_with_aux_rand(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key, &self))
- }
-
- fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let htlc_key = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key);
- let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
- let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
- let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
- let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
- let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
- let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
- Ok(sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self))
- }
-
- fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
- let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
- Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
- }
-
- fn sign_holder_anchor_input(
- &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
- ) -> Result<Signature, ()> {
- let witness_script = chan_utils::get_anchor_redeemscript(&self.holder_channel_pubkeys.funding_pubkey);
- let sighash = sighash::SighashCache::new(&*anchor_tx).segwit_signature_hash(
- input, &witness_script, ANCHOR_OUTPUT_VALUE_SATOSHI, EcdsaSighashType::All,
- ).unwrap();
- Ok(sign_with_aux_rand(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key, &self))
- }
-
- fn sign_channel_announcement_with_funding_key(
- &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
- ) -> Result<Signature, ()> {
- let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
- Ok(secp_ctx.sign_ecdsa(&msghash, &self.funding_key))
- }
-}
-
-const SERIALIZATION_VERSION: u8 = 1;
-
-const MIN_SERIALIZATION_VERSION: u8 = 1;
-
-impl WriteableEcdsaChannelSigner for InMemorySigner {}
-
-impl Writeable for InMemorySigner {
- fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
- write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
-
- self.funding_key.write(writer)?;
- self.revocation_base_key.write(writer)?;
- self.payment_key.write(writer)?;
- self.delayed_payment_base_key.write(writer)?;
- self.htlc_base_key.write(writer)?;
- self.commitment_seed.write(writer)?;
- self.channel_parameters.write(writer)?;
- self.channel_value_satoshis.write(writer)?;
- self.channel_keys_id.write(writer)?;
-
- write_tlv_fields!(writer, {});
-
- Ok(())
- }
-}
-
-impl<ES: Deref> ReadableArgs<ES> for InMemorySigner where ES::Target: EntropySource {
- fn read<R: io::Read>(reader: &mut R, entropy_source: ES) -> Result<Self, DecodeError> {
- let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
-
- let funding_key = Readable::read(reader)?;
- let revocation_base_key = Readable::read(reader)?;
- let payment_key = Readable::read(reader)?;
- let delayed_payment_base_key = Readable::read(reader)?;
- let htlc_base_key = Readable::read(reader)?;
- let commitment_seed = Readable::read(reader)?;
- let counterparty_channel_data = Readable::read(reader)?;
- let channel_value_satoshis = Readable::read(reader)?;
- let secp_ctx = Secp256k1::signing_only();
- let holder_channel_pubkeys =
- InMemorySigner::make_holder_keys(&secp_ctx, &funding_key, &revocation_base_key,
- &payment_key, &delayed_payment_base_key, &htlc_base_key);
- let keys_id = Readable::read(reader)?;
-
- read_tlv_fields!(reader, {});
-
- Ok(InMemorySigner {
- funding_key,
- revocation_base_key,
- payment_key,
- delayed_payment_base_key,
- htlc_base_key,
- commitment_seed,
- channel_value_satoshis,
- holder_channel_pubkeys,
- channel_parameters: counterparty_channel_data,
- channel_keys_id: keys_id,
- rand_bytes_unique_start: entropy_source.get_secure_random_bytes(),
- rand_bytes_index: AtomicCounter::new(),
- })
- }
-}
-
-/// Simple implementation of [`EntropySource`], [`NodeSigner`], and [`SignerProvider`] that takes a
-/// 32-byte seed for use as a BIP 32 extended key and derives keys from that.
-///
-/// Your `node_id` is seed/0'.
-/// Unilateral closes may use seed/1'.
-/// Cooperative closes may use seed/2'.
-/// The two close keys may be needed to claim on-chain funds!
-///
-/// This struct cannot be used for nodes that wish to support receiving phantom payments;
-/// [`PhantomKeysManager`] must be used instead.
-///
-/// Note that switching between this struct and [`PhantomKeysManager`] will invalidate any
-/// previously issued invoices and attempts to pay previous invoices will fail.
-pub struct KeysManager {
- secp_ctx: Secp256k1<secp256k1::All>,
- node_secret: SecretKey,
- node_id: PublicKey,
- inbound_payment_key: KeyMaterial,
- destination_script: Script,
- shutdown_pubkey: PublicKey,
- channel_master_key: ExtendedPrivKey,
- channel_child_index: AtomicUsize,
-
- rand_bytes_unique_start: [u8; 32],
- rand_bytes_index: AtomicCounter,
-
- seed: [u8; 32],
- starting_time_secs: u64,
- starting_time_nanos: u32,
-}
-
-impl KeysManager {
- /// Constructs a [`KeysManager`] from a 32-byte seed. If the seed is in some way biased (e.g.,
- /// your CSRNG is busted) this may panic (but more importantly, you will possibly lose funds).
- /// `starting_time` isn't strictly required to actually be a time, but it must absolutely,
- /// without a doubt, be unique to this instance. ie if you start multiple times with the same
- /// `seed`, `starting_time` must be unique to each run. Thus, the easiest way to achieve this
- /// is to simply use the current time (with very high precision).
- ///
- /// The `seed` MUST be backed up safely prior to use so that the keys can be re-created, however,
- /// obviously, `starting_time` should be unique every time you reload the library - it is only
- /// used to generate new ephemeral key data (which will be stored by the individual channel if
- /// necessary).
- ///
- /// Note that the seed is required to recover certain on-chain funds independent of
- /// [`ChannelMonitor`] data, though a current copy of [`ChannelMonitor`] data is also required
- /// for any channel, and some on-chain during-closing funds.
- ///
- /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
- pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self {
- let secp_ctx = Secp256k1::new();
- // Note that when we aren't serializing the key, network doesn't matter
- match ExtendedPrivKey::new_master(Network::Testnet, seed) {
- Ok(master_key) => {
- let node_secret = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0).unwrap()).expect("Your RNG is busted").private_key;
- let node_id = PublicKey::from_secret_key(&secp_ctx, &node_secret);
- let destination_script = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(1).unwrap()) {
- Ok(destination_key) => {
- let wpubkey_hash = WPubkeyHash::hash(&ExtendedPubKey::from_priv(&secp_ctx, &destination_key).to_pub().to_bytes());
- Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0)
- .push_slice(&wpubkey_hash.into_inner())
- .into_script()
- },
- Err(_) => panic!("Your RNG is busted"),
- };
- let shutdown_pubkey = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(2).unwrap()) {
- Ok(shutdown_key) => ExtendedPubKey::from_priv(&secp_ctx, &shutdown_key).public_key,
- Err(_) => panic!("Your RNG is busted"),
- };
- let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap()).expect("Your RNG is busted");
- let inbound_payment_key: SecretKey = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted").private_key;
- let mut inbound_pmt_key_bytes = [0; 32];
- inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]);
-
- let mut rand_bytes_engine = Sha256::engine();
- rand_bytes_engine.input(&starting_time_secs.to_be_bytes());
- rand_bytes_engine.input(&starting_time_nanos.to_be_bytes());
- rand_bytes_engine.input(seed);
- rand_bytes_engine.input(b"LDK PRNG Seed");
- let rand_bytes_unique_start = Sha256::from_engine(rand_bytes_engine).into_inner();
-
- let mut res = KeysManager {
- secp_ctx,
- node_secret,
- node_id,
- inbound_payment_key: KeyMaterial(inbound_pmt_key_bytes),
-
- destination_script,
- shutdown_pubkey,
-
- channel_master_key,
- channel_child_index: AtomicUsize::new(0),
-
- rand_bytes_unique_start,
- rand_bytes_index: AtomicCounter::new(),
-
- seed: *seed,
- starting_time_secs,
- starting_time_nanos,
- };
- let secp_seed = res.get_secure_random_bytes();
- res.secp_ctx.seeded_randomize(&secp_seed);
- res
- },
- Err(_) => panic!("Your rng is busted"),
- }
- }
-
- /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
- pub fn get_node_secret_key(&self) -> SecretKey {
- self.node_secret
- }
-
- /// Derive an old [`WriteableEcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters.
- pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
- let chan_id = u64::from_be_bytes(params[0..8].try_into().unwrap());
- let mut unique_start = Sha256::engine();
- unique_start.input(params);
- unique_start.input(&self.seed);
-
- // We only seriously intend to rely on the channel_master_key for true secure
- // entropy, everything else just ensures uniqueness. We rely on the unique_start (ie
- // starting_time provided in the constructor) to be unique.
- let child_privkey = self.channel_master_key.ckd_priv(&self.secp_ctx,
- ChildNumber::from_hardened_idx((chan_id as u32) % (1 << 31)).expect("key space exhausted")
- ).expect("Your RNG is busted");
- unique_start.input(&child_privkey.private_key[..]);
-
- let seed = Sha256::from_engine(unique_start).into_inner();
-
- let commitment_seed = {
- let mut sha = Sha256::engine();
- sha.input(&seed);
- sha.input(&b"commitment seed"[..]);
- Sha256::from_engine(sha).into_inner()
- };
- macro_rules! key_step {
- ($info: expr, $prev_key: expr) => {{
- let mut sha = Sha256::engine();
- sha.input(&seed);
- sha.input(&$prev_key[..]);
- sha.input(&$info[..]);
- SecretKey::from_slice(&Sha256::from_engine(sha).into_inner()).expect("SHA-256 is busted")
- }}
- }
- let funding_key = key_step!(b"funding key", commitment_seed);
- let revocation_base_key = key_step!(b"revocation base key", funding_key);
- let payment_key = key_step!(b"payment key", revocation_base_key);
- let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key);
- let htlc_base_key = key_step!(b"HTLC base key", delayed_payment_base_key);
- let prng_seed = self.get_secure_random_bytes();
-
- InMemorySigner::new(
- &self.secp_ctx,
- funding_key,
- revocation_base_key,
- payment_key,
- delayed_payment_base_key,
- htlc_base_key,
- commitment_seed,
- channel_value_satoshis,
- params.clone(),
- prng_seed,
- )
- }
-
- /// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
- /// output to the given change destination (if sufficient change value remains). The
- /// transaction will have a feerate, at least, of the given value.
- ///
- /// Returns `Err(())` if the output value is greater than the input value minus required fee,
- /// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
- /// does not match the one we can spend.
- ///
- /// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
- ///
- /// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
- /// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
- pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
- let mut input = Vec::new();
- let mut input_value = 0;
- let mut witness_weight = 0;
- let mut output_set = HashSet::with_capacity(descriptors.len());
- for outp in descriptors {
- match outp {
- SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
- input.push(TxIn {
- previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
- script_sig: Script::new(),
- sequence: Sequence::ZERO,
- witness: Witness::new(),
- });
- witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
- input_value += descriptor.output.value;
- if !output_set.insert(descriptor.outpoint) { return Err(()); }
- },
- SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
- input.push(TxIn {
- previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
- script_sig: Script::new(),
- sequence: Sequence(descriptor.to_self_delay as u32),
- witness: Witness::new(),
- });
- witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
- input_value += descriptor.output.value;
- if !output_set.insert(descriptor.outpoint) { return Err(()); }
- },
- SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
- input.push(TxIn {
- previous_output: outpoint.into_bitcoin_outpoint(),
- script_sig: Script::new(),
- sequence: Sequence::ZERO,
- witness: Witness::new(),
- });
- witness_weight += 1 + 73 + 34;
- input_value += output.value;
- if !output_set.insert(*outpoint) { return Err(()); }
- }
- }
- if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
- }
- let mut spend_tx = Transaction {
- version: 2,
- lock_time: PackedLockTime(0),
- input,
- output: outputs,
- };
- let expected_max_weight =
- transaction_utils::maybe_add_change_output(&mut spend_tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;
-
- let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None;
- let mut input_idx = 0;
- for outp in descriptors {
- match outp {
- SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
- if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
- keys_cache = Some((
- self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
- descriptor.channel_keys_id));
- }
- spend_tx.input[input_idx].witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?);
- },
- SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
- if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
- keys_cache = Some((
- self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
- descriptor.channel_keys_id));
- }
- spend_tx.input[input_idx].witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?);
- },
- SpendableOutputDescriptor::StaticOutput { ref output, .. } => {
- let derivation_idx = if output.script_pubkey == self.destination_script {
- 1
- } else {
- 2
- };
- let secret = {
- // Note that when we aren't serializing the key, network doesn't matter
- match ExtendedPrivKey::new_master(Network::Testnet, &self.seed) {
- Ok(master_key) => {
- match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(derivation_idx).expect("key space exhausted")) {
- Ok(key) => key,
- Err(_) => panic!("Your RNG is busted"),
- }
- }
- Err(_) => panic!("Your rng is busted"),
- }
- };
- let pubkey = ExtendedPubKey::from_priv(&secp_ctx, &secret).to_pub();
- if derivation_idx == 2 {
- assert_eq!(pubkey.inner, self.shutdown_pubkey);
- }
- let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
- let payment_script = bitcoin::Address::p2wpkh(&pubkey, Network::Testnet).expect("uncompressed key found").script_pubkey();
-
- if payment_script != output.script_pubkey { return Err(()); };
-
- let sighash = hash_to_message!(&sighash::SighashCache::new(&spend_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
- let sig = sign_with_aux_rand(secp_ctx, &sighash, &secret.private_key, &self);
- let mut sig_ser = sig.serialize_der().to_vec();
- sig_ser.push(EcdsaSighashType::All as u8);
- spend_tx.input[input_idx].witness.push(sig_ser);
- spend_tx.input[input_idx].witness.push(pubkey.inner.serialize().to_vec());
- },
- }
- input_idx += 1;
- }
-
- debug_assert!(expected_max_weight >= spend_tx.weight());
- // Note that witnesses with a signature vary somewhat in size, so allow
- // `expected_max_weight` to overshoot by up to 3 bytes per input.
- debug_assert!(expected_max_weight <= spend_tx.weight() + descriptors.len() * 3);
-
- Ok(spend_tx)
- }
-}
-
-impl EntropySource for KeysManager {
- fn get_secure_random_bytes(&self) -> [u8; 32] {
- let index = self.rand_bytes_index.get_increment();
- let mut nonce = [0u8; 16];
- nonce[..8].copy_from_slice(&index.to_be_bytes());
- ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
- }
-}
-
-impl NodeSigner for KeysManager {
- fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
- match recipient {
- Recipient::Node => Ok(self.node_id.clone()),
- Recipient::PhantomNode => Err(())
- }
- }
-
- fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
- let mut node_secret = match recipient {
- Recipient::Node => Ok(self.node_secret.clone()),
- Recipient::PhantomNode => Err(())
- }?;
- if let Some(tweak) = tweak {
- node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
- }
- Ok(SharedSecret::new(other_key, &node_secret))
- }
-
- fn get_inbound_payment_key_material(&self) -> KeyMaterial {
- self.inbound_payment_key.clone()
- }
-
- fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
- let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
- 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)), secret))
- }
-
- fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
- let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
- Ok(self.secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
- }
-}
-
-impl SignerProvider for KeysManager {
- type Signer = InMemorySigner;
-
- fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
- let child_idx = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
- // `child_idx` is the only thing guaranteed to make each channel unique without a restart
- // (though `user_channel_id` should help, depending on user behavior). If it manages to
- // roll over, we may generate duplicate keys for two different channels, which could result
- // in loss of funds. Because we only support 32-bit+ systems, assert that our `AtomicUsize`
- // doesn't reach `u32::MAX`.
- assert!(child_idx < core::u32::MAX as usize, "2^32 channels opened without restart");
- let mut id = [0; 32];
- id[0..4].copy_from_slice(&(child_idx as u32).to_be_bytes());
- id[4..8].copy_from_slice(&self.starting_time_nanos.to_be_bytes());
- id[8..16].copy_from_slice(&self.starting_time_secs.to_be_bytes());
- id[16..32].copy_from_slice(&user_channel_id.to_be_bytes());
- id
- }
-
- fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
- self.derive_channel_keys(channel_value_satoshis, &channel_keys_id)
- }
-
- fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
- InMemorySigner::read(&mut io::Cursor::new(reader), self)
- }
-
- fn get_destination_script(&self) -> Script {
- self.destination_script.clone()
- }
-
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
- ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone())
- }
-}
-
-/// Similar to [`KeysManager`], but allows the node using this struct to receive phantom node
-/// payments.
-///
-/// A phantom node payment is a payment made to a phantom invoice, which is an invoice that can be
-/// paid to one of multiple nodes. This works because we encode the invoice route hints such that
-/// LDK will recognize an incoming payment as destined for a phantom node, and collect the payment
-/// itself without ever needing to forward to this fake node.
-///
-/// Phantom node payments are useful for load balancing between multiple LDK nodes. They also
-/// provide some fault tolerance, because payers will automatically retry paying other provided
-/// nodes in the case that one node goes down.
-///
-/// Note that multi-path payments are not supported in phantom invoices for security reasons.
-// In the hypothetical case that we did support MPP phantom payments, there would be no way for
-// nodes to know when the full payment has been received (and the preimage can be released) without
-// significantly compromising on our safety guarantees. I.e., if we expose the ability for the user
-// to tell LDK when the preimage can be released, we open ourselves to attacks where the preimage
-// is released too early.
-//
-/// Switching between this struct and [`KeysManager`] will invalidate any previously issued
-/// invoices and attempts to pay previous invoices will fail.
-pub struct PhantomKeysManager {
- inner: KeysManager,
- inbound_payment_key: KeyMaterial,
- phantom_secret: SecretKey,
- phantom_node_id: PublicKey,
-}
-
-impl EntropySource for PhantomKeysManager {
- fn get_secure_random_bytes(&self) -> [u8; 32] {
- self.inner.get_secure_random_bytes()
- }
-}
-
-impl NodeSigner for PhantomKeysManager {
- fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
- match recipient {
- Recipient::Node => self.inner.get_node_id(Recipient::Node),
- Recipient::PhantomNode => Ok(self.phantom_node_id.clone()),
- }
- }
-
- fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
- let mut node_secret = match recipient {
- Recipient::Node => self.inner.node_secret.clone(),
- Recipient::PhantomNode => self.phantom_secret.clone(),
- };
- if let Some(tweak) = tweak {
- node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
- }
- Ok(SharedSecret::new(other_key, &node_secret))
- }
-
- fn get_inbound_payment_key_material(&self) -> KeyMaterial {
- self.inbound_payment_key.clone()
- }
-
- fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
- let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
- 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)), secret))
- }
-
- fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
- self.inner.sign_gossip_message(msg)
- }
-}
-
-impl SignerProvider for PhantomKeysManager {
- type Signer = InMemorySigner;
-
- fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
- self.inner.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id)
- }
-
- fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
- self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id)
- }
-
- fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
- self.inner.read_chan_signer(reader)
- }
-
- fn get_destination_script(&self) -> Script {
- self.inner.get_destination_script()
- }
-
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
- self.inner.get_shutdown_scriptpubkey()
- }
-}
-
-impl PhantomKeysManager {
- /// Constructs a [`PhantomKeysManager`] given a 32-byte seed and an additional `cross_node_seed`
- /// that is shared across all nodes that intend to participate in [phantom node payments]
- /// together.
- ///
- /// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and
- /// `starting_time_nanos`.
- ///
- /// `cross_node_seed` must be the same across all phantom payment-receiving nodes and also the
- /// same across restarts, or else inbound payments may fail.
- ///
- /// [phantom node payments]: PhantomKeysManager
- pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, cross_node_seed: &[u8; 32]) -> Self {
- let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos);
- let (inbound_key, phantom_key) = hkdf_extract_expand_twice(b"LDK Inbound and Phantom Payment Key Expansion", cross_node_seed);
- let phantom_secret = SecretKey::from_slice(&phantom_key).unwrap();
- let phantom_node_id = PublicKey::from_secret_key(&inner.secp_ctx, &phantom_secret);
- Self {
- inner,
- inbound_payment_key: KeyMaterial(inbound_key),
- phantom_secret,
- phantom_node_id,
- }
- }
-
- /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method.
- pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
- self.inner.spend_spendable_outputs(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, secp_ctx)
- }
-
- /// See [`KeysManager::derive_channel_keys`] for documentation on this method.
- pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
- self.inner.derive_channel_keys(channel_value_satoshis, params)
- }
-
- /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
- pub fn get_node_secret_key(&self) -> SecretKey {
- self.inner.get_node_secret_key()
- }
-
- /// Gets the "node_id" secret key of the phantom node used to sign invoices, decode the
- /// last-hop onion data, etc.
- pub fn get_phantom_node_secret_key(&self) -> SecretKey {
- self.phantom_secret
- }
-}
-
-// Ensure that EcdsaChannelSigner can have a vtable
-#[test]
-pub fn dyn_sign() {
- let _signer: Box<dyn EcdsaChannelSigner>;
-}
-
-#[cfg(all(test, feature = "_bench_unstable", not(feature = "no-std")))]
-mod benches {
- use std::sync::{Arc, mpsc};
- use std::sync::mpsc::TryRecvError;
- use std::thread;
- use std::time::Duration;
- use bitcoin::blockdata::constants::genesis_block;
- use bitcoin::Network;
- use crate::chain::keysinterface::{EntropySource, KeysManager};
-
- use test::Bencher;
-
- #[bench]
- fn bench_get_secure_random_bytes(bench: &mut Bencher) {
- let seed = [0u8; 32];
- let now = Duration::from_secs(genesis_block(Network::Testnet).header.time as u64);
- let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_micros()));
-
- let mut handles = Vec::new();
- let mut stops = Vec::new();
- for _ in 1..5 {
- let keys_manager_clone = Arc::clone(&keys_manager);
- let (stop_sender, stop_receiver) = mpsc::channel();
- let handle = thread::spawn(move || {
- loop {
- keys_manager_clone.get_secure_random_bytes();
- match stop_receiver.try_recv() {
- Ok(_) | Err(TryRecvError::Disconnected) => {
- println!("Terminating.");
- break;
- }
- Err(TryRecvError::Empty) => {}
- }
- }
- });
- handles.push(handle);
- stops.push(stop_sender);
- }
-
- bench.iter(|| {
- for _ in 1..100 {
- keys_manager.get_secure_random_bytes();
- }
- });
-
- for stop in stops {
- let _ = stop.send(());
- }
- for handle in handles {
- handle.join().unwrap();
- }
- }
-
-}
use bitcoin::secp256k1::PublicKey;
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, MonitorEvent};
-use crate::chain::keysinterface::WriteableEcdsaChannelSigner;
+use crate::sign::WriteableEcdsaChannelSigner;
use crate::chain::transaction::{OutPoint, TransactionData};
use crate::prelude::*;
pub mod chainmonitor;
pub mod channelmonitor;
pub mod transaction;
-pub mod keysinterface;
pub(crate) mod onchaintx;
pub(crate) mod package;
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1;
-use crate::chain::keysinterface::{ChannelSigner, EntropySource, SignerProvider};
+use crate::sign::{ChannelSigner, EntropySource, SignerProvider};
use crate::ln::msgs::DecodeError;
use crate::ln::PaymentPreimage;
#[cfg(anchors)]
use crate::chain::chaininterface::ConfirmationTarget;
use crate::chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
-use crate::chain::keysinterface::WriteableEcdsaChannelSigner;
+use crate::sign::WriteableEcdsaChannelSigner;
#[cfg(anchors)]
use crate::chain::package::PackageSolvingData;
use crate::chain::package::PackageTemplate;
use crate::ln::chan_utils;
use crate::ln::msgs::DecodeError;
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
-use crate::chain::keysinterface::WriteableEcdsaChannelSigner;
+use crate::sign::WriteableEcdsaChannelSigner;
#[cfg(anchors)]
use crate::chain::onchaintx::ExternalHTLCClaim;
use crate::chain::onchaintx::OnchainTxHandler;
weight: u64,
amount: u64,
on_counterparty_tx_csv: u16,
+ is_counterparty_balance_on_anchors: Option<()>,
}
impl RevokedOutput {
- pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, per_commitment_key: SecretKey, amount: u64, on_counterparty_tx_csv: u16) -> Self {
+ pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, per_commitment_key: SecretKey, amount: u64, on_counterparty_tx_csv: u16, is_counterparty_balance_on_anchors: bool) -> Self {
RevokedOutput {
per_commitment_point,
counterparty_delayed_payment_base_key,
per_commitment_key,
weight: WEIGHT_REVOKED_OUTPUT,
amount,
- on_counterparty_tx_csv
+ on_counterparty_tx_csv,
+ is_counterparty_balance_on_anchors: if is_counterparty_balance_on_anchors { Some(()) } else { None }
}
}
}
(8, weight, required),
(10, amount, required),
(12, on_counterparty_tx_csv, required),
+ (14, is_counterparty_balance_on_anchors, option)
});
/// A struct to describe a revoked offered output and corresponding information to generate a
};
absolute_timelock
}
+
+ fn map_output_type_flags(&self) -> (PackageMalleability, bool) {
+ // Post-anchor, aggregation of outputs of different types is unsafe. See https://github.com/lightning/bolts/pull/803.
+ let (malleability, aggregable) = match self {
+ PackageSolvingData::RevokedOutput(RevokedOutput { is_counterparty_balance_on_anchors: Some(()), .. }) => { (PackageMalleability::Malleable, false) },
+ PackageSolvingData::RevokedOutput(RevokedOutput { is_counterparty_balance_on_anchors: None, .. }) => { (PackageMalleability::Malleable, true) },
+ PackageSolvingData::RevokedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { (PackageMalleability::Malleable, false) },
+ PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
+ (PackageMalleability::Malleable, outp.preimage.is_some())
+ } else {
+ (PackageMalleability::Untractable, false)
+ },
+ PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) },
+ };
+ (malleability, aggregable)
+ }
}
impl_writeable_tlv_based_enum!(PackageSolvingData, ;
);
/// A malleable package might be aggregated with other packages to save on fees.
-/// A untractable package has been counter-signed and aggregable will break cached counterparty
-/// signatures.
+/// A untractable package has been counter-signed and aggregable will break cached counterparty signatures.
#[derive(Clone, PartialEq, Eq)]
pub(crate) enum PackageMalleability {
Malleable,
}).is_some()
}
- pub (crate) fn build_package(txid: Txid, vout: u32, input_solving_data: PackageSolvingData, soonest_conf_deadline: u32, aggregable: bool, height_original: u32) -> Self {
- let malleability = match input_solving_data {
- PackageSolvingData::RevokedOutput(..) => PackageMalleability::Malleable,
- PackageSolvingData::RevokedHTLCOutput(..) => PackageMalleability::Malleable,
- PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => PackageMalleability::Malleable,
- PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => PackageMalleability::Malleable,
- PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
- PackageMalleability::Malleable
- } else {
- PackageMalleability::Untractable
- },
- PackageSolvingData::HolderFundingOutput(..) => PackageMalleability::Untractable,
- };
+ pub (crate) fn build_package(txid: Txid, vout: u32, input_solving_data: PackageSolvingData, soonest_conf_deadline: u32, height_original: u32) -> Self {
+ let (malleability, aggregable) = PackageSolvingData::map_output_type_flags(&input_solving_data);
let mut inputs = Vec::with_capacity(1);
inputs.push((BitcoinOutPoint { txid, vout }, input_solving_data));
PackageTemplate {
inputs.push((outpoint, rev_outp));
}
let (malleability, aggregable) = if let Some((_, lead_input)) = inputs.first() {
- match lead_input {
- PackageSolvingData::RevokedOutput(..) => { (PackageMalleability::Malleable, true) },
- PackageSolvingData::RevokedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
- PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
- PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { (PackageMalleability::Malleable, false) },
- PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
- (PackageMalleability::Malleable, outp.preimage.is_some())
- } else {
- (PackageMalleability::Untractable, false)
- },
- PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) },
- }
+ PackageSolvingData::map_output_type_flags(&lead_input)
} else { return Err(DecodeError::InvalidValue); };
let mut soonest_conf_deadline = 0;
let mut feerate_previous = 0;
use bitcoin::secp256k1::Secp256k1;
macro_rules! dumb_revk_output {
- ($secp_ctx: expr) => {
+ ($secp_ctx: expr, $is_counterparty_balance_on_anchors: expr) => {
{
let dumb_scalar = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
let dumb_point = PublicKey::from_secret_key(&$secp_ctx, &dumb_scalar);
- PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, dumb_point, dumb_point, dumb_scalar, 0, 0))
+ PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, dumb_point, dumb_point, dumb_scalar, 0, 0, $is_counterparty_balance_on_anchors))
}
}
}
fn test_package_differing_heights() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
- let mut package_one_hundred = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, true, 100);
- let package_two_hundred = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, true, 200);
+ let mut package_one_hundred = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, 100);
+ let package_two_hundred = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, 200);
package_one_hundred.merge_package(package_two_hundred);
}
fn test_package_untractable_merge_to() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
let htlc_outp = dumb_htlc_output!();
- let mut untractable_package = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, true, 100);
- let malleable_package = PackageTemplate::build_package(txid, 1, htlc_outp.clone(), 1000, true, 100);
+ let mut untractable_package = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, 100);
+ let malleable_package = PackageTemplate::build_package(txid, 1, htlc_outp.clone(), 1000, 100);
untractable_package.merge_package(malleable_package);
}
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
let htlc_outp = dumb_htlc_output!();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
- let mut malleable_package = PackageTemplate::build_package(txid, 0, htlc_outp.clone(), 1000, true, 100);
- let untractable_package = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, true, 100);
+ let mut malleable_package = PackageTemplate::build_package(txid, 0, htlc_outp.clone(), 1000, 100);
+ let untractable_package = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, 100);
malleable_package.merge_package(untractable_package);
}
fn test_package_noaggregation_to() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
+ let revk_outp_counterparty_balance = dumb_revk_output!(secp_ctx, true);
- let mut noaggregation_package = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, false, 100);
- let aggregation_package = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, true, 100);
+ let mut noaggregation_package = PackageTemplate::build_package(txid, 0, revk_outp_counterparty_balance.clone(), 1000, 100);
+ let aggregation_package = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, 100);
noaggregation_package.merge_package(aggregation_package);
}
fn test_package_noaggregation_from() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
+ let revk_outp_counterparty_balance = dumb_revk_output!(secp_ctx, true);
- let mut aggregation_package = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, true, 100);
- let noaggregation_package = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, false, 100);
+ let mut aggregation_package = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, 100);
+ let noaggregation_package = PackageTemplate::build_package(txid, 1, revk_outp_counterparty_balance.clone(), 1000, 100);
aggregation_package.merge_package(noaggregation_package);
}
fn test_package_empty() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
- let mut empty_package = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, true, 100);
+ let mut empty_package = PackageTemplate::build_package(txid, 0, revk_outp.clone(), 1000, 100);
empty_package.inputs = vec![];
- let package = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, true, 100);
+ let package = PackageTemplate::build_package(txid, 1, revk_outp.clone(), 1000, 100);
empty_package.merge_package(package);
}
fn test_package_differing_categories() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
let counterparty_outp = dumb_counterparty_output!(secp_ctx, 0, false);
- let mut revoked_package = PackageTemplate::build_package(txid, 0, revk_outp, 1000, true, 100);
- let counterparty_package = PackageTemplate::build_package(txid, 1, counterparty_outp, 1000, true, 100);
+ let mut revoked_package = PackageTemplate::build_package(txid, 0, revk_outp, 1000, 100);
+ let counterparty_package = PackageTemplate::build_package(txid, 1, counterparty_outp, 1000, 100);
revoked_package.merge_package(counterparty_package);
}
fn test_package_split_malleable() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp_one = dumb_revk_output!(secp_ctx);
- let revk_outp_two = dumb_revk_output!(secp_ctx);
- let revk_outp_three = dumb_revk_output!(secp_ctx);
+ let revk_outp_one = dumb_revk_output!(secp_ctx, false);
+ let revk_outp_two = dumb_revk_output!(secp_ctx, false);
+ let revk_outp_three = dumb_revk_output!(secp_ctx, false);
- let mut package_one = PackageTemplate::build_package(txid, 0, revk_outp_one, 1000, true, 100);
- let package_two = PackageTemplate::build_package(txid, 1, revk_outp_two, 1000, true, 100);
- let package_three = PackageTemplate::build_package(txid, 2, revk_outp_three, 1000, true, 100);
+ let mut package_one = PackageTemplate::build_package(txid, 0, revk_outp_one, 1000, 100);
+ let package_two = PackageTemplate::build_package(txid, 1, revk_outp_two, 1000, 100);
+ let package_three = PackageTemplate::build_package(txid, 2, revk_outp_three, 1000, 100);
package_one.merge_package(package_two);
package_one.merge_package(package_three);
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let htlc_outp_one = dumb_htlc_output!();
- let mut package_one = PackageTemplate::build_package(txid, 0, htlc_outp_one, 1000, true, 100);
+ let mut package_one = PackageTemplate::build_package(txid, 0, htlc_outp_one, 1000, 100);
let ret_split = package_one.split_package(&BitcoinOutPoint { txid, vout: 0});
assert!(ret_split.is_none());
}
fn test_package_timer() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let revk_outp = dumb_revk_output!(secp_ctx);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
- let mut package = PackageTemplate::build_package(txid, 0, revk_outp, 1000, true, 100);
+ let mut package = PackageTemplate::build_package(txid, 0, revk_outp, 1000, 100);
assert_eq!(package.timer(), 100);
package.set_timer(101);
assert_eq!(package.timer(), 101);
let secp_ctx = Secp256k1::new();
let counterparty_outp = dumb_counterparty_output!(secp_ctx, 1_000_000, false);
- let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
+ let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, 100);
assert_eq!(package.package_amount(), 1000);
}
let weight_sans_output = (4 + 4 + 1 + 36 + 4 + 1 + 1 + 8 + 1) * WITNESS_SCALE_FACTOR + 2;
{
- let revk_outp = dumb_revk_output!(secp_ctx);
- let package = PackageTemplate::build_package(txid, 0, revk_outp, 0, true, 100);
+ let revk_outp = dumb_revk_output!(secp_ctx, false);
+ let package = PackageTemplate::build_package(txid, 0, revk_outp, 0, 100);
assert_eq!(package.package_weight(&Script::new()), weight_sans_output + WEIGHT_REVOKED_OUTPUT as usize);
}
{
for &opt_anchors in [false, true].iter() {
let counterparty_outp = dumb_counterparty_output!(secp_ctx, 1_000_000, opt_anchors);
- let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
+ let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, 100);
assert_eq!(package.package_weight(&Script::new()), weight_sans_output + weight_received_htlc(opt_anchors) as usize);
}
}
{
for &opt_anchors in [false, true].iter() {
let counterparty_outp = dumb_counterparty_offered_output!(secp_ctx, 1_000_000, opt_anchors);
- let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
+ let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, 100);
assert_eq!(package.package_weight(&Script::new()), weight_sans_output + weight_offered_htlc(opt_anchors) as usize);
}
}
/// A unique identifier used along with `channel_value_satoshis` to re-derive the
/// [`InMemorySigner`] required to sign `input`.
///
- /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ /// [`InMemorySigner`]: crate::sign::InMemorySigner
pub channel_keys_id: [u8; 32],
/// The value in satoshis of the channel we're attempting to spend the anchor output of. This is
/// used along with `channel_keys_id` to re-derive the [`InMemorySigner`] required to sign
/// `input`.
///
- /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ /// [`InMemorySigner`]: crate::sign::InMemorySigner
pub channel_value_satoshis: u64,
/// The transaction input's outpoint corresponding to the commitment transaction's anchor
/// output.
/// A unique identifier used along with `channel_value_satoshis` to re-derive the
/// [`InMemorySigner`] required to sign `input`.
///
- /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ /// [`InMemorySigner`]: crate::sign::InMemorySigner
pub channel_keys_id: [u8; 32],
/// The value in satoshis of the channel we're attempting to spend the anchor output of. This is
/// used along with `channel_keys_id` to re-derive the [`InMemorySigner`] required to sign
/// `input`.
///
- /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ /// [`InMemorySigner`]: crate::sign::InMemorySigner
pub channel_value_satoshis: u64,
/// The necessary channel parameters that need to be provided to the re-derived
/// [`InMemorySigner`] through [`ChannelSigner::provide_channel_parameters`].
///
- /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
- /// [`ChannelSigner::provide_channel_parameters`]: crate::chain::keysinterface::ChannelSigner::provide_channel_parameters
+ /// [`InMemorySigner`]: crate::sign::InMemorySigner
+ /// [`ChannelSigner::provide_channel_parameters`]: crate::sign::ChannelSigner::provide_channel_parameters
pub channel_parameters: ChannelTransactionParameters,
/// The txid of the commitment transaction in which the HTLC output lives.
pub commitment_txid: Txid,
/// an empty `pending_htlcs`), confirmation of the commitment transaction can be considered to
/// be not urgent.
///
- /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
- /// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
- /// [`EcdsaChannelSigner::sign_holder_anchor_input`]: crate::chain::keysinterface::EcdsaChannelSigner::sign_holder_anchor_input
+ /// [`InMemorySigner`]: crate::sign::InMemorySigner
+ /// [`KeysManager::derive_channel_keys`]: crate::sign::KeysManager::derive_channel_keys
+ /// [`EcdsaChannelSigner::sign_holder_anchor_input`]: crate::sign::EcdsaChannelSigner::sign_holder_anchor_input
/// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness
ChannelClose {
/// The target feerate that the transaction package, which consists of the commitment
/// longer able to commit external confirmed funds to the HTLC transaction or the fee committed
/// to the HTLC transaction is greater in value than the HTLCs being claimed.
///
- /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
- /// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
- /// [`EcdsaChannelSigner::sign_holder_htlc_transaction`]: crate::chain::keysinterface::EcdsaChannelSigner::sign_holder_htlc_transaction
+ /// [`InMemorySigner`]: crate::sign::InMemorySigner
+ /// [`KeysManager::derive_channel_keys`]: crate::sign::KeysManager::derive_channel_keys
+ /// [`EcdsaChannelSigner::sign_holder_htlc_transaction`]: crate::sign::EcdsaChannelSigner::sign_holder_htlc_transaction
/// [`HTLCDescriptor::tx_input_witness`]: HTLCDescriptor::tx_input_witness
HTLCResolution {
/// The target feerate that the resulting HTLC transaction must meet.
#[cfg(anchors)]
pub use bump_transaction::BumpTransactionEvent;
-use crate::chain::keysinterface::SpendableOutputDescriptor;
+use crate::sign::SpendableOutputDescriptor;
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
use crate::ln::features::ChannelTypeFeatures;
/// Be careful about printing the peer_msg, a well-crafted message could exploit
/// a security vulnerability in the terminal emulator or the logging subsystem.
/// To be safe, use `Display` on `UntrustedString`
- ///
+ ///
/// [`UntrustedString`]: crate::util::string::UntrustedString
peer_msg: UntrustedString,
},
/// This field will always be filled in when the event was generated by LDK versions
/// 0.0.113 and above.
///
- /// [phantom nodes]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom nodes]: crate::sign::PhantomKeysManager
receiver_node_id: Option<PublicKey>,
/// The hash for which the preimage should be handed to the ChannelManager. Note that LDK will
/// not stop you from registering duplicate payment hashes for inbound payments.
/// This field will always be filled in when the event was generated by LDK versions
/// 0.0.113 and above.
///
- /// [phantom nodes]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom nodes]: crate::sign::PhantomKeysManager
receiver_node_id: Option<PublicKey>,
/// The payment hash of the claimed payment. Note that LDK will not stop you from
/// registering duplicate payment hashes for inbound payments.
payment_id: PaymentId,
/// The hash that was given to [`ChannelManager::send_payment`].
///
+ /// This will be `Some` for all payments which completed on LDK 0.0.104 or later.
+ ///
/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
payment_hash: Option<PaymentHash>,
/// The payment path that was successful.
PaymentPathFailed {
/// The `payment_id` passed to [`ChannelManager::send_payment`].
///
+ /// This will be `Some` for all payment paths which failed on LDK 0.0.103 or later.
+ ///
/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
payment_id: Option<PaymentId>,
/// The message which should be sent.
msg: msgs::AcceptChannel,
},
+ /// Used to indicate that we've accepted a V2 channel open and should send the accept_channel2
+ /// message provided to the given peer.
+ SendAcceptChannelV2 {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::AcceptChannelV2,
+ },
/// Used to indicate that we've initiated a channel open and should send the open_channel
/// message provided to the given peer.
SendOpenChannel {
/// The message which should be sent.
msg: msgs::OpenChannel,
},
+ /// Used to indicate that we've initiated a V2 channel open and should send the open_channel2
+ /// message provided to the given peer.
+ SendOpenChannelV2 {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::OpenChannelV2,
+ },
/// Used to indicate that a funding_created message should be sent to the peer with the given node_id.
SendFundingCreated {
/// The node_id of the node which should receive this message
/// The message which should be sent.
msg: msgs::FundingSigned,
},
+ /// Used to indicate that a tx_add_input message should be sent to the peer with the given node_id.
+ SendTxAddInput {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxAddInput,
+ },
+ /// Used to indicate that a tx_add_output message should be sent to the peer with the given node_id.
+ SendTxAddOutput {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxAddOutput,
+ },
+ /// Used to indicate that a tx_remove_input message should be sent to the peer with the given node_id.
+ SendTxRemoveInput {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxRemoveInput,
+ },
+ /// Used to indicate that a tx_remove_output message should be sent to the peer with the given node_id.
+ SendTxRemoveOutput {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxRemoveOutput,
+ },
+ /// Used to indicate that a tx_complete message should be sent to the peer with the given node_id.
+ SendTxComplete {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxComplete,
+ },
+ /// Used to indicate that a tx_signatures message should be sent to the peer with the given node_id.
+ SendTxSignatures {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxSignatures,
+ },
+ /// Used to indicate that a tx_init_rbf message should be sent to the peer with the given node_id.
+ SendTxInitRbf {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxInitRbf,
+ },
+ /// Used to indicate that a tx_ack_rbf message should be sent to the peer with the given node_id.
+ SendTxAckRbf {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxAckRbf,
+ },
+ /// Used to indicate that a tx_abort message should be sent to the peer with the given node_id.
+ SendTxAbort {
+ /// The node_id of the node which should receive this message
+ node_id: PublicKey,
+ /// The message which should be sent.
+ msg: msgs::TxAddInput,
+ },
/// Used to indicate that a channel_ready message should be sent to the peer with the given node_id.
SendChannelReady {
/// The node_id of the node which should receive these message(s)
pub mod ln;
pub mod offers;
pub mod routing;
+pub mod sign;
pub mod onion_message;
pub mod blinded_path;
pub mod events;
// licenses.
//! Various utilities for building scripts and deriving keys related to channels. These are
-//! largely of interest for those implementing the traits on [`chain::keysinterface`] by hand.
+//! largely of interest for those implementing the traits on [`crate::sign`] by hand.
use bitcoin::blockdata::script::{Script,Builder};
use bitcoin::blockdata::opcodes;
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
use bitcoin::hash_types::{Txid, PubkeyHash};
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use crate::ln::{PaymentHash, PaymentPreimage};
use crate::ln::msgs::DecodeError;
use crate::util::ser::{Readable, Writeable, Writer};
use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
use crate::util::test_utils;
- use crate::chain::keysinterface::{ChannelSigner, SignerProvider};
+ use crate::sign::{ChannelSigner, SignerProvider};
use bitcoin::{Network, Txid};
use bitcoin::hashes::Hash;
use crate::ln::PaymentHash;
//! There are a bunch of these as their handling is relatively error-prone so they are split out
//! here. See also the chanmon_fail_consistency fuzz test.
-use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::hash_types::BlockHash;
use bitcoin::network::constants::Network;
use crate::io;
use bitcoin::hashes::Hash;
-use bitcoin::TxMerkleNode;
use crate::prelude::*;
use crate::sync::{Arc, Mutex};
assert_eq!(chain_mon.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
chain_mon
};
- let header = BlockHeader {
- version: 0x20000000,
- prev_blockhash: BlockHash::all_zeros(),
- merkle_root: TxMerkleNode::all_zeros(),
- time: 42,
- bits: 42,
- nonce: 42
- };
- chain_mon.chain_monitor.block_connected(&Block { header, txdata: vec![] }, 200);
+ chain_mon.chain_monitor.block_connected(&create_dummy_block(BlockHash::all_zeros(), 42, Vec::new()), 200);
// Set the persister's return value to be a InProgress.
persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
let mut node_0_per_peer_lock;
let mut node_0_peer_state_lock;
let mut channel = get_channel_ref!(nodes[0], nodes[1], node_0_per_peer_lock, node_0_peer_state_lock, chan.2);
- if let Ok(update) = channel.commitment_signed(&updates.commitment_signed, &node_cfgs[0].logger) {
+ if let Ok(Some(update)) = channel.commitment_signed(&updates.commitment_signed, &node_cfgs[0].logger) {
// Check that even though the persister is returning a InProgress,
// because the update is bogus, ultimately the error that's returned
// should be a PermanentFailure.
use crate::ln::{PaymentPreimage, PaymentHash};
use crate::ln::features::{ChannelTypeFeatures, InitFeatures};
use crate::ln::msgs;
-use crate::ln::msgs::{DecodeError, OptionalField, DataLossProtect};
+use crate::ln::msgs::DecodeError;
use crate::ln::script::{self, ShutdownScript};
use crate::ln::channelmanager::{self, CounterpartyForwardingInfo, PendingHTLCStatus, HTLCSource, SentHTLCId, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
use crate::ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputInCommitment, htlc_success_tx_weight, htlc_timeout_tx_weight, make_funding_redeemscript, ChannelPublicKeys, CommitmentTransaction, HolderCommitmentTransaction, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, MAX_HTLCS, get_commitment_transaction_number_obscure_factor, ClosingTransaction};
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator};
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS, CLOSED_CHANNEL_UPDATE_ID};
use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::chain::keysinterface::{WriteableEcdsaChannelSigner, EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
+use crate::sign::{WriteableEcdsaChannelSigner, EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
use crate::events::ClosureReason;
use crate::routing::gossip::NodeId;
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter};
/// * `EXPIRE_PREV_CONFIG_TICKS` = convergence_delay / tick_interval
pub(crate) const EXPIRE_PREV_CONFIG_TICKS: usize = 5;
+struct PendingChannelMonitorUpdate {
+ update: ChannelMonitorUpdate,
+ /// In some cases we need to delay letting the [`ChannelMonitorUpdate`] go until after an
+ /// `Event` is processed by the user. This bool indicates the [`ChannelMonitorUpdate`] is
+ /// blocked on some external event and the [`ChannelManager`] will update us when we're ready.
+ ///
+ /// [`ChannelManager`]: super::channelmanager::ChannelManager
+ blocked: bool,
+}
+
+impl_writeable_tlv_based!(PendingChannelMonitorUpdate, {
+ (0, update, required),
+ (2, blocked, required),
+});
+
// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
// calling channel_id() before we're set up or things like get_outbound_funding_signed on an
/// If we then persist the [`channelmanager::ChannelManager`] and crash before the persistence
/// completes we still need to be able to complete the persistence. Thus, we have to keep a
/// copy of the [`ChannelMonitorUpdate`] here until it is complete.
- pending_monitor_updates: Vec<ChannelMonitorUpdate>,
+ pending_monitor_updates: Vec<PendingChannelMonitorUpdate>,
}
#[cfg(any(test, fuzzing))]
secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
- Some(signer_provider.get_shutdown_scriptpubkey())
+ match signer_provider.get_shutdown_scriptpubkey() {
+ Ok(scriptpubkey) => Some(scriptpubkey),
+ Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get shutdown scriptpubkey".to_owned()}),
+ }
} else { None };
if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
}
}
+ let destination_script = match signer_provider.get_destination_script() {
+ Ok(script) => script,
+ Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get destination script".to_owned()}),
+ };
+
let temporary_channel_id = entropy_source.get_secure_random_bytes();
Ok(Channel {
holder_signer,
shutdown_scriptpubkey,
- destination_script: signer_provider.get_destination_script(),
+ destination_script,
cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
match &msg.shutdown_scriptpubkey {
- &OptionalField::Present(ref script) => {
+ &Some(ref script) => {
// Peer is signaling upfront_shutdown and has opt-out with a 0-length script. We don't enforce anything
if script.len() == 0 {
None
}
},
// Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel
- &OptionalField::Absent => {
+ &None => {
return Err(ChannelError::Close("Peer is signaling upfront_shutdown but we don't get any script. Use 0-length script to opt-out".to_owned()));
}
}
} else { None };
let shutdown_scriptpubkey = if config.channel_handshake_config.commit_upfront_shutdown_pubkey {
- Some(signer_provider.get_shutdown_scriptpubkey())
+ match signer_provider.get_shutdown_scriptpubkey() {
+ Ok(scriptpubkey) => Some(scriptpubkey),
+ Err(_) => return Err(ChannelError::Close("Failed to get upfront shutdown scriptpubkey".to_owned())),
+ }
} else { None };
if let Some(shutdown_scriptpubkey) = &shutdown_scriptpubkey {
}
}
+ let destination_script = match signer_provider.get_destination_script() {
+ Ok(script) => script,
+ Err(_) => return Err(ChannelError::Close("Failed to get destination script".to_owned())),
+ };
+
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
holder_signer,
shutdown_scriptpubkey,
- destination_script: signer_provider.get_destination_script(),
+ destination_script,
cur_holder_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
cur_counterparty_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER,
}
pub fn get_update_fulfill_htlc_and_commit<L: Deref>(&mut self, htlc_id: u64, payment_preimage: PaymentPreimage, logger: &L) -> UpdateFulfillCommitFetch where L::Target: Logger {
+ let release_cs_monitor = self.pending_monitor_updates.iter().all(|upd| !upd.blocked);
match self.get_update_fulfill_htlc(htlc_id, payment_preimage, logger) {
- UpdateFulfillFetch::NewClaim { mut monitor_update, htlc_value_msat, msg: Some(_) } => {
- let mut additional_update = self.build_commitment_no_status_check(logger);
- // build_commitment_no_status_check may bump latest_monitor_id but we want them to be
- // strictly increasing by one, so decrement it here.
- self.latest_monitor_update_id = monitor_update.update_id;
- monitor_update.updates.append(&mut additional_update.updates);
- self.monitor_updating_paused(false, true, false, Vec::new(), Vec::new(), Vec::new());
- self.pending_monitor_updates.push(monitor_update);
+ UpdateFulfillFetch::NewClaim { mut monitor_update, htlc_value_msat, msg } => {
+ // Even if we aren't supposed to let new monitor updates with commitment state
+ // updates run, we still need to push the preimage ChannelMonitorUpdateStep no
+ // matter what. Sadly, to push a new monitor update which flies before others
+ // already queued, we have to insert it into the pending queue and update the
+ // update_ids of all the following monitors.
+ let unblocked_update_pos = if release_cs_monitor && msg.is_some() {
+ let mut additional_update = self.build_commitment_no_status_check(logger);
+ // build_commitment_no_status_check may bump latest_monitor_id but we want them
+ // to be strictly increasing by one, so decrement it here.
+ self.latest_monitor_update_id = monitor_update.update_id;
+ monitor_update.updates.append(&mut additional_update.updates);
+ self.pending_monitor_updates.push(PendingChannelMonitorUpdate {
+ update: monitor_update, blocked: false,
+ });
+ self.pending_monitor_updates.len() - 1
+ } else {
+ let insert_pos = self.pending_monitor_updates.iter().position(|upd| upd.blocked)
+ .unwrap_or(self.pending_monitor_updates.len());
+ let new_mon_id = self.pending_monitor_updates.get(insert_pos)
+ .map(|upd| upd.update.update_id).unwrap_or(monitor_update.update_id);
+ monitor_update.update_id = new_mon_id;
+ self.pending_monitor_updates.insert(insert_pos, PendingChannelMonitorUpdate {
+ update: monitor_update, blocked: false,
+ });
+ for held_update in self.pending_monitor_updates.iter_mut().skip(insert_pos + 1) {
+ held_update.update.update_id += 1;
+ }
+ if msg.is_some() {
+ debug_assert!(false, "If there is a pending blocked monitor we should have MonitorUpdateInProgress set");
+ let update = self.build_commitment_no_status_check(logger);
+ self.pending_monitor_updates.push(PendingChannelMonitorUpdate {
+ update, blocked: true,
+ });
+ }
+ insert_pos
+ };
+ self.monitor_updating_paused(false, msg.is_some(), false, Vec::new(), Vec::new(), Vec::new());
UpdateFulfillCommitFetch::NewClaim {
- monitor_update: self.pending_monitor_updates.last().unwrap(),
+ monitor_update: &self.pending_monitor_updates.get(unblocked_update_pos)
+ .expect("We just pushed the monitor update").update,
htlc_value_msat,
}
},
- UpdateFulfillFetch::NewClaim { monitor_update, htlc_value_msat, msg: None } => {
- self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new());
- self.pending_monitor_updates.push(monitor_update);
- UpdateFulfillCommitFetch::NewClaim {
- monitor_update: self.pending_monitor_updates.last().unwrap(),
- htlc_value_msat,
- }
- }
UpdateFulfillFetch::DuplicateClaim {} => UpdateFulfillCommitFetch::DuplicateClaim {},
}
}
let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
match &msg.shutdown_scriptpubkey {
- &OptionalField::Present(ref script) => {
+ &Some(ref script) => {
// Peer is signaling upfront_shutdown and has opt-out with a 0-length script. We don't enforce anything
if script.len() == 0 {
None
}
},
// Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel
- &OptionalField::Absent => {
+ &None => {
return Err(ChannelError::Close("Peer is signaling upfront_shutdown but we don't get any script. Use 0-length script to opt-out".to_owned()));
}
}
Ok(())
}
- pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<&ChannelMonitorUpdate, ChannelError>
+ pub fn commitment_signed<L: Deref>(&mut self, msg: &msgs::CommitmentSigned, logger: &L) -> Result<Option<&ChannelMonitorUpdate>, ChannelError>
where L::Target: Logger
{
if (self.channel_state & (ChannelState::ChannelReady as u32)) != (ChannelState::ChannelReady as u32) {
}
log_debug!(logger, "Received valid commitment_signed from peer in channel {}, updated HTLC state but awaiting a monitor update resolution to reply.",
log_bytes!(self.channel_id));
- self.pending_monitor_updates.push(monitor_update);
- return Ok(self.pending_monitor_updates.last().unwrap());
+ return Ok(self.push_ret_blockable_mon_update(monitor_update));
}
let need_commitment_signed = if need_commitment && (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == 0 {
log_debug!(logger, "Received valid commitment_signed from peer in channel {}, updating HTLC state and responding with{} a revoke_and_ack.",
log_bytes!(self.channel_id()), if need_commitment_signed { " our own commitment_signed and" } else { "" });
- self.pending_monitor_updates.push(monitor_update);
self.monitor_updating_paused(true, need_commitment_signed, false, Vec::new(), Vec::new(), Vec::new());
- return Ok(self.pending_monitor_updates.last().unwrap());
+ return Ok(self.push_ret_blockable_mon_update(monitor_update));
}
/// Public version of the below, checking relevant preconditions first.
update_add_htlcs.len(), update_fulfill_htlcs.len(), update_fail_htlcs.len());
self.monitor_updating_paused(false, true, false, Vec::new(), Vec::new(), Vec::new());
- self.pending_monitor_updates.push(monitor_update);
- (Some(self.pending_monitor_updates.last().unwrap()), htlcs_to_fail)
+ (self.push_ret_blockable_mon_update(monitor_update), htlcs_to_fail)
} else {
(None, Vec::new())
}
/// waiting on this revoke_and_ack. The generation of this new commitment_signed may also fail,
/// generating an appropriate error *after* the channel state has been updated based on the
/// revoke_and_ack message.
- pub fn revoke_and_ack<L: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L) -> Result<(Vec<(HTLCSource, PaymentHash)>, &ChannelMonitorUpdate), ChannelError>
+ pub fn revoke_and_ack<L: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L) -> Result<(Vec<(HTLCSource, PaymentHash)>, Option<&ChannelMonitorUpdate>), ChannelError>
where L::Target: Logger,
{
if (self.channel_state & (ChannelState::ChannelReady as u32)) != (ChannelState::ChannelReady as u32) {
self.monitor_pending_failures.append(&mut revoked_htlcs);
self.monitor_pending_finalized_fulfills.append(&mut finalized_claimed_htlcs);
log_debug!(logger, "Received a valid revoke_and_ack for channel {} but awaiting a monitor update resolution to reply.", log_bytes!(self.channel_id()));
- self.pending_monitor_updates.push(monitor_update);
- return Ok((Vec::new(), self.pending_monitor_updates.last().unwrap()));
+ return Ok((Vec::new(), self.push_ret_blockable_mon_update(monitor_update)));
}
match self.free_holding_cell_htlcs(logger) {
(Some(_), htlcs_to_fail) => {
- let mut additional_update = self.pending_monitor_updates.pop().unwrap();
+ let mut additional_update = self.pending_monitor_updates.pop().unwrap().update;
// free_holding_cell_htlcs may bump latest_monitor_id multiple times but we want them to be
// strictly increasing by one, so decrement it here.
self.latest_monitor_update_id = monitor_update.update_id;
monitor_update.updates.append(&mut additional_update.updates);
self.monitor_updating_paused(false, true, false, to_forward_infos, revoked_htlcs, finalized_claimed_htlcs);
- self.pending_monitor_updates.push(monitor_update);
- Ok((htlcs_to_fail, self.pending_monitor_updates.last().unwrap()))
+ Ok((htlcs_to_fail, self.push_ret_blockable_mon_update(monitor_update)))
},
(None, htlcs_to_fail) => {
if require_commitment {
log_debug!(logger, "Received a valid revoke_and_ack for channel {}. Responding with a commitment update with {} HTLCs failed.",
log_bytes!(self.channel_id()), update_fail_htlcs.len() + update_fail_malformed_htlcs.len());
self.monitor_updating_paused(false, true, false, to_forward_infos, revoked_htlcs, finalized_claimed_htlcs);
- self.pending_monitor_updates.push(monitor_update);
- Ok((htlcs_to_fail, self.pending_monitor_updates.last().unwrap()))
+ Ok((htlcs_to_fail, self.push_ret_blockable_mon_update(monitor_update)))
} else {
log_debug!(logger, "Received a valid revoke_and_ack for channel {} with no reply necessary.", log_bytes!(self.channel_id()));
self.monitor_updating_paused(false, false, false, to_forward_infos, revoked_htlcs, finalized_claimed_htlcs);
- self.pending_monitor_updates.push(monitor_update);
- Ok((htlcs_to_fail, self.pending_monitor_updates.last().unwrap()))
+ Ok((htlcs_to_fail, self.push_ret_blockable_mon_update(monitor_update)))
}
}
}
{
assert_eq!(self.channel_state & ChannelState::MonitorUpdateInProgress as u32, ChannelState::MonitorUpdateInProgress as u32);
self.channel_state &= !(ChannelState::MonitorUpdateInProgress as u32);
- self.pending_monitor_updates.clear();
+ let mut found_blocked = false;
+ self.pending_monitor_updates.retain(|upd| {
+ if found_blocked { debug_assert!(upd.blocked, "No mons may be unblocked after a blocked one"); }
+ if upd.blocked { found_blocked = true; }
+ upd.blocked
+ });
// If we're past (or at) the FundingSent stage on an outbound channel, try to
// (re-)broadcast the funding transaction as we may have declined to broadcast it when we
if msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER ||
msg.next_local_commitment_number == 0 {
- return Err(ChannelError::Close("Peer sent a garbage channel_reestablish".to_owned()));
+ return Err(ChannelError::Close("Peer sent a garbage channel_reestablish (usually an lnd node with lost state asking us to force-close for them)".to_owned()));
}
if msg.next_remote_commitment_number > 0 {
- match msg.data_loss_protect {
- OptionalField::Present(ref data_loss) => {
- let expected_point = self.holder_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.secp_ctx);
- let given_secret = SecretKey::from_slice(&data_loss.your_last_per_commitment_secret)
- .map_err(|_| ChannelError::Close("Peer sent a garbage channel_reestablish with unparseable secret key".to_owned()))?;
- if expected_point != PublicKey::from_secret_key(&self.secp_ctx, &given_secret) {
- return Err(ChannelError::Close("Peer sent a garbage channel_reestablish with secret key not matching the commitment height provided".to_owned()));
+ let expected_point = self.holder_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.secp_ctx);
+ let given_secret = SecretKey::from_slice(&msg.your_last_per_commitment_secret)
+ .map_err(|_| ChannelError::Close("Peer sent a garbage channel_reestablish with unparseable secret key".to_owned()))?;
+ if expected_point != PublicKey::from_secret_key(&self.secp_ctx, &given_secret) {
+ return Err(ChannelError::Close("Peer sent a garbage channel_reestablish with secret key not matching the commitment height provided".to_owned()));
+ }
+ if msg.next_remote_commitment_number > INITIAL_COMMITMENT_NUMBER - self.cur_holder_commitment_transaction_number {
+ macro_rules! log_and_panic {
+ ($err_msg: expr) => {
+ log_error!(logger, $err_msg, log_bytes!(self.channel_id), log_pubkey!(self.counterparty_node_id));
+ panic!($err_msg, log_bytes!(self.channel_id), log_pubkey!(self.counterparty_node_id));
}
- if msg.next_remote_commitment_number > INITIAL_COMMITMENT_NUMBER - self.cur_holder_commitment_transaction_number {
- macro_rules! log_and_panic {
- ($err_msg: expr) => {
- log_error!(logger, $err_msg, log_bytes!(self.channel_id), log_pubkey!(self.counterparty_node_id));
- panic!($err_msg, log_bytes!(self.channel_id), log_pubkey!(self.counterparty_node_id));
- }
- }
- log_and_panic!("We have fallen behind - we have received proof that if we broadcast our counterparty is going to claim all our funds.\n\
- This implies you have restarted with lost ChannelMonitor and ChannelManager state, the first of which is a violation of the LDK chain::Watch requirements.\n\
- More specifically, this means you have a bug in your implementation that can cause loss of funds, or you are running with an old backup, which is unsafe.\n\
- If you have restored from an old backup and wish to force-close channels and return to operation, you should start up, call\n\
- ChannelManager::force_close_without_broadcasting_txn on channel {} with counterparty {} or\n\
- ChannelManager::force_close_all_channels_without_broadcasting_txn, then reconnect to peer(s).\n\
- Note that due to a long-standing bug in lnd you may have to reach out to peers running lnd-based nodes to ask them to manually force-close channels\n\
- See https://github.com/lightningdevkit/rust-lightning/issues/1565 for more info.");
- }
- },
- OptionalField::Absent => {}
+ }
+ log_and_panic!("We have fallen behind - we have received proof that if we broadcast our counterparty is going to claim all our funds.\n\
+ This implies you have restarted with lost ChannelMonitor and ChannelManager state, the first of which is a violation of the LDK chain::Watch requirements.\n\
+ More specifically, this means you have a bug in your implementation that can cause loss of funds, or you are running with an old backup, which is unsafe.\n\
+ If you have restored from an old backup and wish to force-close channels and return to operation, you should start up, call\n\
+ ChannelManager::force_close_without_broadcasting_txn on channel {} with counterparty {} or\n\
+ ChannelManager::force_close_all_channels_without_broadcasting_txn, then reconnect to peer(s).\n\
+ Note that due to a long-standing bug in lnd you may have to reach out to peers running lnd-based nodes to ask them to manually force-close channels\n\
+ See https://github.com/lightningdevkit/rust-lightning/issues/1565 for more info.");
}
}
Some(_) => false,
None => {
assert!(send_shutdown);
- let shutdown_scriptpubkey = signer_provider.get_shutdown_scriptpubkey();
+ let shutdown_scriptpubkey = match signer_provider.get_shutdown_scriptpubkey() {
+ Ok(scriptpubkey) => scriptpubkey,
+ Err(_) => return Err(ChannelError::Close("Failed to get shutdown scriptpubkey".to_owned())),
+ };
if !shutdown_scriptpubkey.is_compatible(their_features) {
return Err(ChannelError::Close(format!("Provided a scriptpubkey format not accepted by peer: {}", shutdown_scriptpubkey)));
}
}],
};
self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new());
- self.pending_monitor_updates.push(monitor_update);
- Some(self.pending_monitor_updates.last().unwrap())
+ if self.push_blockable_mon_update(monitor_update) {
+ self.pending_monitor_updates.last().map(|upd| &upd.update)
+ } else { None }
} else { None };
let shutdown = if send_shutdown {
Some(msgs::Shutdown {
(self.channel_state & ChannelState::MonitorUpdateInProgress as u32) != 0
}
- pub fn get_next_monitor_update(&self) -> Option<&ChannelMonitorUpdate> {
- self.pending_monitor_updates.first()
+ pub fn get_latest_complete_monitor_update_id(&self) -> u64 {
+ if self.pending_monitor_updates.is_empty() { return self.get_latest_monitor_update_id(); }
+ self.pending_monitor_updates[0].update.update_id - 1
+ }
+
+ /// Returns the next blocked monitor update, if one exists, and a bool which indicates a
+ /// further blocked monitor update exists after the next.
+ pub fn unblock_next_blocked_monitor_update(&mut self) -> Option<(&ChannelMonitorUpdate, bool)> {
+ for i in 0..self.pending_monitor_updates.len() {
+ if self.pending_monitor_updates[i].blocked {
+ self.pending_monitor_updates[i].blocked = false;
+ return Some((&self.pending_monitor_updates[i].update,
+ self.pending_monitor_updates.len() > i + 1));
+ }
+ }
+ None
+ }
+
+ /// Pushes a new monitor update into our monitor update queue, returning whether it should be
+ /// immediately given to the user for persisting or if it should be held as blocked.
+ fn push_blockable_mon_update(&mut self, update: ChannelMonitorUpdate) -> bool {
+ let release_monitor = self.pending_monitor_updates.iter().all(|upd| !upd.blocked);
+ self.pending_monitor_updates.push(PendingChannelMonitorUpdate {
+ update, blocked: !release_monitor
+ });
+ release_monitor
+ }
+
+ /// Pushes a new monitor update into our monitor update queue, returning a reference to it if
+ /// it should be immediately given to the user for persisting or `None` if it should be held as
+ /// blocked.
+ fn push_ret_blockable_mon_update(&mut self, update: ChannelMonitorUpdate)
+ -> Option<&ChannelMonitorUpdate> {
+ let release_monitor = self.push_blockable_mon_update(update);
+ if release_monitor { self.pending_monitor_updates.last().map(|upd| &upd.update) } else { None }
+ }
+
+ pub fn no_monitor_updates_pending(&self) -> bool {
+ self.pending_monitor_updates.is_empty()
+ }
+
+ pub fn complete_one_mon_update(&mut self, update_id: u64) {
+ self.pending_monitor_updates.retain(|upd| upd.update.update_id != update_id);
}
/// Returns true if funding_created was sent/received.
htlc_basepoint: keys.htlc_basepoint,
first_per_commitment_point,
channel_flags: if self.config.announced_channel {1} else {0},
- shutdown_scriptpubkey: OptionalField::Present(match &self.shutdown_scriptpubkey {
+ shutdown_scriptpubkey: Some(match &self.shutdown_scriptpubkey {
Some(script) => script.clone().into_inner(),
None => Builder::new().into_script(),
}),
delayed_payment_basepoint: keys.delayed_payment_basepoint,
htlc_basepoint: keys.htlc_basepoint,
first_per_commitment_point,
- shutdown_scriptpubkey: OptionalField::Present(match &self.shutdown_scriptpubkey {
+ shutdown_scriptpubkey: Some(match &self.shutdown_scriptpubkey {
Some(script) => script.clone().into_inner(),
None => Builder::new().into_script(),
}),
// valid, and valid in fuzzing mode's arbitrary validity criteria:
let mut pk = [2; 33]; pk[1] = 0xff;
let dummy_pubkey = PublicKey::from_slice(&pk).unwrap();
- let data_loss_protect = if self.cur_counterparty_commitment_transaction_number + 1 < INITIAL_COMMITMENT_NUMBER {
+ let remote_last_secret = if self.cur_counterparty_commitment_transaction_number + 1 < INITIAL_COMMITMENT_NUMBER {
let remote_last_secret = self.commitment_secrets.get_secret(self.cur_counterparty_commitment_transaction_number + 2).unwrap();
log_trace!(logger, "Enough info to generate a Data Loss Protect with per_commitment_secret {} for channel {}", log_bytes!(remote_last_secret), log_bytes!(self.channel_id()));
- OptionalField::Present(DataLossProtect {
- your_last_per_commitment_secret: remote_last_secret,
- my_current_per_commitment_point: dummy_pubkey
- })
+ remote_last_secret
} else {
log_info!(logger, "Sending a data_loss_protect with no previous remote per_commitment_secret for channel {}", log_bytes!(self.channel_id()));
- OptionalField::Present(DataLossProtect {
- your_last_per_commitment_secret: [0;32],
- my_current_per_commitment_point: dummy_pubkey,
- })
+ [0;32]
};
msgs::ChannelReestablish {
channel_id: self.channel_id(),
// dropped this channel on disconnect as it hasn't yet reached FundingSent so we can't
// overflow here.
next_remote_commitment_number: INITIAL_COMMITMENT_NUMBER - self.cur_counterparty_commitment_transaction_number - 1,
- data_loss_protect,
+ your_last_per_commitment_secret: remote_last_secret,
+ my_current_per_commitment_point: dummy_pubkey,
+ // TODO(dual_funding): If we've sent `commtiment_signed` for an interactive transaction
+ // construction but have not received `tx_signatures` we MUST set `next_funding_txid` to the
+ // txid of that interactive transaction, else we MUST NOT set it.
+ next_funding_txid: None,
}
}
Some(_) => {
let monitor_update = self.build_commitment_no_status_check(logger);
self.monitor_updating_paused(false, true, false, Vec::new(), Vec::new(), Vec::new());
- self.pending_monitor_updates.push(monitor_update);
- Ok(Some(self.pending_monitor_updates.last().unwrap()))
+ Ok(self.push_ret_blockable_mon_update(monitor_update))
},
None => Ok(None)
}
/// May jump to the channel being fully shutdown (see [`Self::is_shutdown`]) in which case no
/// [`ChannelMonitorUpdate`] will be returned).
pub fn get_shutdown<SP: Deref>(&mut self, signer_provider: &SP, their_features: &InitFeatures,
- target_feerate_sats_per_kw: Option<u32>)
+ target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
-> Result<(msgs::Shutdown, Option<&ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
where SP::Target: SignerProvider {
for htlc in self.pending_outbound_htlcs.iter() {
return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote".to_owned()});
}
}
+ if self.shutdown_scriptpubkey.is_some() && override_shutdown_script.is_some() {
+ return Err(APIError::APIMisuseError{err: "Cannot override shutdown script for a channel with one already set".to_owned()});
+ }
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
if self.channel_state & (ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateInProgress as u32) != 0 {
return Err(APIError::ChannelUnavailable{err: "Cannot begin shutdown while peer is disconnected or we're waiting on a monitor update, maybe force-close instead?".to_owned()});
let update_shutdown_script = match self.shutdown_scriptpubkey {
Some(_) => false,
None if !chan_closed => {
- let shutdown_scriptpubkey = signer_provider.get_shutdown_scriptpubkey();
+ // use override shutdown script if provided
+ let shutdown_scriptpubkey = match override_shutdown_script {
+ Some(script) => script,
+ None => {
+ // otherwise, use the shutdown scriptpubkey provided by the signer
+ match signer_provider.get_shutdown_scriptpubkey() {
+ Ok(scriptpubkey) => scriptpubkey,
+ Err(_) => return Err(APIError::ChannelUnavailable{err: "Failed to get shutdown scriptpubkey".to_owned()}),
+ }
+ },
+ };
if !shutdown_scriptpubkey.is_compatible(their_features) {
return Err(APIError::IncompatibleShutdownScript { script: shutdown_scriptpubkey.clone() });
}
}],
};
self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new());
- self.pending_monitor_updates.push(monitor_update);
- Some(self.pending_monitor_updates.last().unwrap())
+ if self.push_blockable_mon_update(monitor_update) {
+ self.pending_monitor_updates.last().map(|upd| &upd.update)
+ } else { None }
} else { None };
let shutdown = msgs::Shutdown {
channel_id: self.channel_id,
(28, holder_max_accepted_htlcs, option),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
+ (33, self.pending_monitor_updates, vec_type),
});
Ok(())
let mut temporary_channel_id: Option<[u8; 32]> = None;
let mut holder_max_accepted_htlcs: Option<u16> = None;
+ let mut pending_monitor_updates = Some(Vec::new());
+
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(1, minimum_depth, option),
(28, holder_max_accepted_htlcs, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
+ (33, pending_monitor_updates, vec_type),
});
let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
channel_type: channel_type.unwrap(),
channel_keys_id,
- pending_monitor_updates: Vec::new(),
+ pending_monitor_updates: pending_monitor_updates.unwrap(),
})
}
}
use crate::ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
use crate::ln::features::ChannelTypeFeatures;
- use crate::ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate, MAX_VALUE_MSAT};
+ use crate::ln::msgs::{ChannelUpdate, DecodeError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
use crate::ln::script::ShutdownScript;
use crate::ln::chan_utils;
use crate::ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight};
use crate::chain::BestBlock;
use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator, ConfirmationTarget};
- use crate::chain::keysinterface::{ChannelSigner, InMemorySigner, EntropySource, SignerProvider};
+ use crate::sign::{ChannelSigner, InMemorySigner, EntropySource, SignerProvider};
use crate::chain::transaction::OutPoint;
use crate::routing::router::Path;
use crate::util::config::UserConfig;
fn read_chan_signer(&self, _data: &[u8]) -> Result<Self::Signer, DecodeError> { panic!(); }
- fn get_destination_script(&self) -> Script {
+ fn get_destination_script(&self) -> Result<Script, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_monitor_claim_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
let channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize());
- Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&channel_monitor_claim_key_hash[..]).into_script()
+ Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&channel_monitor_claim_key_hash[..]).into_script())
}
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
let secp_ctx = Secp256k1::signing_only();
let channel_close_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap();
- ShutdownScript::new_p2wpkh_from_pubkey(PublicKey::from_secret_key(&secp_ctx, &channel_close_key))
+ Ok(ShutdownScript::new_p2wpkh_from_pubkey(PublicKey::from_secret_key(&secp_ctx, &channel_close_key)))
}
}
let msg = node_b_chan.get_channel_reestablish(&&logger);
assert_eq!(msg.next_local_commitment_number, 1); // now called next_commitment_number
assert_eq!(msg.next_remote_commitment_number, 0); // now called next_revocation_number
- match msg.data_loss_protect {
- OptionalField::Present(DataLossProtect { your_last_per_commitment_secret, .. }) => {
- assert_eq!(your_last_per_commitment_secret, [0; 32]);
- },
- _ => panic!()
- }
+ assert_eq!(msg.your_last_per_commitment_secret, [0; 32]);
// Check that the commitment point in Node A's channel_reestablish message
// is sane.
let msg = node_a_chan.get_channel_reestablish(&&logger);
assert_eq!(msg.next_local_commitment_number, 1); // now called next_commitment_number
assert_eq!(msg.next_remote_commitment_number, 0); // now called next_revocation_number
- match msg.data_loss_protect {
- OptionalField::Present(DataLossProtect { your_last_per_commitment_secret, .. }) => {
- assert_eq!(your_last_per_commitment_secret, [0; 32]);
- },
- _ => panic!()
- }
+ assert_eq!(msg.your_last_per_commitment_secret, [0; 32]);
}
#[test]
use bitcoin::hashes::hex::FromHex;
use bitcoin::hash_types::Txid;
use bitcoin::secp256k1::Message;
- use crate::chain::keysinterface::EcdsaChannelSigner;
+ use crate::sign::EcdsaChannelSigner;
use crate::ln::PaymentPreimage;
use crate::ln::channel::{HTLCOutputInCommitment ,TxCreationKeys};
use crate::ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
#[cfg(any(feature = "_test_utils", test))]
use crate::ln::features::InvoiceFeatures;
use crate::routing::gossip::NetworkGraph;
-use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router};
-use crate::routing::scoring::ProbabilisticScorer;
+use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteHop, RouteParameters, Router};
+use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
use crate::ln::msgs;
use crate::ln::onion_utils;
use crate::ln::onion_utils::HTLCFailReason;
use crate::ln::outbound_payment;
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment};
use crate::ln::wire::Encode;
-use crate::chain::keysinterface::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, ChannelSigner, WriteableEcdsaChannelSigner};
+use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, ChannelSigner, WriteableEcdsaChannelSigner};
use crate::util::config::{UserConfig, ChannelConfig};
use crate::util::wakers::{Future, Notifier};
use crate::util::scid_utils::fake_scid;
// Re-export this for use in the public API.
pub use crate::ln::outbound_payment::{PaymentSendFailure, Retry, RetryableSendFailure, RecipientOnionFields};
+use crate::ln::script::ShutdownScript;
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
//
/// for some reason. They are handled in timer_tick_occurred, so may be processed with
/// quite some time lag.
enum BackgroundEvent {
- /// Handle a ChannelMonitorUpdate that closes a channel, broadcasting its current latest holder
- /// commitment transaction.
- ClosingMonitorUpdate((OutPoint, ChannelMonitorUpdate)),
+ /// Handle a ChannelMonitorUpdate
+ ///
+ /// Note that any such events are lost on shutdown, so in general they must be updates which
+ /// are regenerated on startup.
+ MonitorUpdateRegeneratedOnStartup((OutPoint, ChannelMonitorUpdate)),
}
#[derive(Debug)]
(2, EmitEvent) => { (0, event, upgradable_required) },
);
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(crate) enum EventCompletionAction {
+ ReleaseRAAChannelMonitorUpdate {
+ counterparty_node_id: PublicKey,
+ channel_funding_outpoint: OutPoint,
+ },
+}
+impl_writeable_tlv_based_enum!(EventCompletionAction,
+ (0, ReleaseRAAChannelMonitorUpdate) => {
+ (0, channel_funding_outpoint, required),
+ (2, counterparty_node_id, required),
+ };
+);
+
/// State we hold per-peer.
pub(super) struct PeerState<Signer: ChannelSigner> {
/// `temporary_channel_id` or `channel_id` -> `channel`.
Arc<DefaultRouter<
Arc<NetworkGraph<Arc<L>>>,
Arc<L>,
- Arc<Mutex<ProbabilisticScorer<Arc<NetworkGraph<Arc<L>>>, Arc<L>>>>
+ Arc<Mutex<ProbabilisticScorer<Arc<NetworkGraph<Arc<L>>>, Arc<L>>>>,
+ ProbabilisticScoringFeeParameters,
+ ProbabilisticScorer<Arc<NetworkGraph<Arc<L>>>, Arc<L>>,
>>,
Arc<L>
>;
/// of [`KeysManager`] and [`DefaultRouter`].
///
/// This is not exported to bindings users as Arcs don't make sense in bindings
-pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L> = ChannelManager<&'a M, &'b T, &'c KeysManager, &'c KeysManager, &'c KeysManager, &'d F, &'e DefaultRouter<&'f NetworkGraph<&'g L>, &'g L, &'h Mutex<ProbabilisticScorer<&'f NetworkGraph<&'g L>, &'g L>>>, &'g L>;
+pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L> = ChannelManager<&'a M, &'b T, &'c KeysManager, &'c KeysManager, &'c KeysManager, &'d F, &'e DefaultRouter<&'f NetworkGraph<&'g L>, &'g L, &'h Mutex<ProbabilisticScorer<&'f NetworkGraph<&'g L>, &'g L>>, ProbabilisticScoringFeeParameters, ProbabilisticScorer<&'f NetworkGraph<&'g L>, &'g L>>, &'g L>;
/// A trivial trait which describes any [`ChannelManager`] used in testing.
#[cfg(any(test, feature = "_test_utils"))]
#[cfg(any(test, feature = "_test_utils"))]
pub(super) per_peer_state: FairRwLock<HashMap<PublicKey, Mutex<PeerState<<SP::Target as SignerProvider>::Signer>>>>,
+ /// The set of events which we need to give to the user to handle. In some cases an event may
+ /// require some further action after the user handles it (currently only blocking a monitor
+ /// update from being handed to the user to ensure the included changes to the channel state
+ /// are handled by the user before they're persisted durably to disk). In that case, the second
+ /// element in the tuple is set to `Some` with further details of the action.
+ ///
+ /// Note that events MUST NOT be removed from pending_events after deserialization, as they
+ /// could be in the middle of being processed without the direct mutex held.
+ ///
/// See `ChannelManager` struct-level documentation for lock order requirements.
- pending_events: Mutex<Vec<events::Event>>,
+ pending_events: Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
/// A simple atomic flag to ensure only one task at a time can be processing events asynchronously.
pending_events_processor: AtomicBool,
/// See `ChannelManager` struct-level documentation for lock order requirements.
/// Route hints used in constructing invoices for [phantom node payents].
///
-/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+/// [phantom node payments]: crate::sign::PhantomKeysManager
#[derive(Clone)]
pub struct PhantomRouteHints {
/// The list of channels to be included in the invoice route hints.
});
}
if let Some((channel_id, user_channel_id)) = chan_id {
- $self.pending_events.lock().unwrap().push(events::Event::ChannelClosed {
+ $self.pending_events.lock().unwrap().push_back((events::Event::ChannelClosed {
channel_id, user_channel_id,
reason: ClosureReason::ProcessingError { err: err.err.clone() }
- });
+ }, None));
}
}
macro_rules! emit_channel_pending_event {
($locked_events: expr, $channel: expr) => {
if $channel.should_emit_channel_pending_event() {
- $locked_events.push(events::Event::ChannelPending {
+ $locked_events.push_back((events::Event::ChannelPending {
channel_id: $channel.channel_id(),
former_temporary_channel_id: $channel.temporary_channel_id(),
counterparty_node_id: $channel.get_counterparty_node_id(),
user_channel_id: $channel.get_user_id(),
funding_txo: $channel.get_funding_txo().unwrap().into_bitcoin_outpoint(),
- });
+ }, None));
$channel.set_channel_pending_event_emitted();
}
}
($locked_events: expr, $channel: expr) => {
if $channel.should_emit_channel_ready_event() {
debug_assert!($channel.channel_pending_event_emitted());
- $locked_events.push(events::Event::ChannelReady {
+ $locked_events.push_back((events::Event::ChannelReady {
channel_id: $channel.channel_id(),
user_channel_id: $channel.get_user_id(),
counterparty_node_id: $channel.get_counterparty_node_id(),
channel_type: $channel.get_channel_type().clone(),
- });
+ }, None));
$channel.set_channel_ready_event_emitted();
}
}
res
},
ChannelMonitorUpdateStatus::Completed => {
- if ($update_id == 0 || $chan.get_next_monitor_update()
- .expect("We can't be processing a monitor update if it isn't queued")
- .update_id == $update_id) &&
- $chan.get_latest_monitor_update_id() == $update_id
- {
+ $chan.complete_one_mon_update($update_id);
+ if $chan.no_monitor_updates_pending() {
handle_monitor_update_completion!($self, $update_id, $peer_state_lock, $peer_state, $per_peer_state_lock, $chan);
}
Ok(())
result = NotifyOption::DoPersist;
}
- for event in pending_events {
+ let mut post_event_actions = Vec::new();
+
+ for (event, action_opt) in pending_events {
$event_to_handle = event;
$handle_event;
+ if let Some(action) = action_opt {
+ post_event_actions.push(action);
+ }
}
{
$self.pending_events_processor.store(false, Ordering::Release);
}
+ if !post_event_actions.is_empty() {
+ $self.handle_post_event_actions(post_event_actions);
+ // If we had some actions, go around again as we may have more events now
+ processed_all_events = false;
+ }
+
if result == NotifyOption::DoPersist {
$self.persistence_notifier.notify();
}
per_peer_state: FairRwLock::new(HashMap::new()),
- pending_events: Mutex::new(Vec::new()),
+ pending_events: Mutex::new(VecDeque::new()),
pending_events_processor: AtomicBool::new(false),
pending_background_events: Mutex::new(Vec::new()),
total_consistency_lock: RwLock::new(()),
/// Raises [`APIError::APIMisuseError`] when `channel_value_satoshis` > 2**24 or `push_msat` is
/// greater than `channel_value_satoshis * 1k` or `channel_value_satoshis < 1000`.
///
+ /// Raises [`APIError::ChannelUnavailable`] if the channel cannot be opened due to failing to
+ /// generate a shutdown scriptpubkey or destination script set by
+ /// [`SignerProvider::get_shutdown_scriptpubkey`] or [`SignerProvider::get_destination_script`].
+ ///
/// Note that we do not check if you are currently connected to the given peer. If no
/// connection is available, the outbound `open_channel` message may fail to send, resulting in
/// the channel eventually being silently forgotten (dropped on reload).
let mut pending_events_lock = self.pending_events.lock().unwrap();
match channel.unbroadcasted_funding() {
Some(transaction) => {
- pending_events_lock.push(events::Event::DiscardFunding { channel_id: channel.channel_id(), transaction })
+ pending_events_lock.push_back((events::Event::DiscardFunding {
+ channel_id: channel.channel_id(), transaction
+ }, None));
},
None => {},
}
- pending_events_lock.push(events::Event::ChannelClosed {
+ pending_events_lock.push_back((events::Event::ChannelClosed {
channel_id: channel.channel_id(),
user_channel_id: channel.get_user_id(),
reason: closure_reason
- });
+ }, None));
}
- fn close_channel_internal(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>) -> Result<(), APIError> {
+ fn close_channel_internal(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, override_shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)>;
let funding_txo_opt = chan_entry.get().get_funding_txo();
let their_features = &peer_state.latest_features;
let (shutdown_msg, mut monitor_update_opt, htlcs) = chan_entry.get_mut()
- .get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight)?;
+ .get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight, override_shutdown_script)?;
failed_htlcs = htlcs;
// We can send the `shutdown` message before updating the `ChannelMonitor`
///
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
///
+ /// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
+ /// generate a shutdown scriptpubkey or destination script set by
+ /// [`SignerProvider::get_shutdown_scriptpubkey`]. A force-closure may be needed to close the
+ /// channel.
+ ///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
pub fn close_channel(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey) -> Result<(), APIError> {
- self.close_channel_internal(channel_id, counterparty_node_id, None)
+ self.close_channel_internal(channel_id, counterparty_node_id, None, None)
}
/// Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs
/// transaction feerate below `target_feerate_sat_per_1000_weight` (or the feerate which
/// will appear on a force-closure transaction, whichever is lower).
///
+ /// The `shutdown_script` provided will be used as the `scriptPubKey` for the closing transaction.
+ /// Will fail if a shutdown script has already been set for this channel by
+ /// ['ChannelHandshakeConfig::commit_upfront_shutdown_pubkey`]. The given shutdown script must
+ /// also be compatible with our and the counterparty's features.
+ ///
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
///
+ /// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
+ /// generate a shutdown scriptpubkey or destination script set by
+ /// [`SignerProvider::get_shutdown_scriptpubkey`]. A force-closure may be needed to close the
+ /// channel.
+ ///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
- pub fn close_channel_with_target_feerate(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: u32) -> Result<(), APIError> {
- self.close_channel_internal(channel_id, counterparty_node_id, Some(target_feerate_sats_per_1000_weight))
+ pub fn close_channel_with_feerate_and_script(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
+ self.close_channel_internal(channel_id, counterparty_node_id, target_feerate_sats_per_1000_weight, shutdown_script)
}
#[inline]
let onion_keys = onion_utils::construct_onion_keys(&self.secp_ctx, &path, &session_priv)
.map_err(|_| APIError::InvalidRoute{err: "Pubkey along hop was maliciously selected".to_owned()})?;
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(path, total_value, recipient_onion, cur_height, keysend_preimage)?;
- if onion_utils::route_size_insane(&onion_payloads) {
- return Err(APIError::InvalidRoute{err: "Route size too large considering onion data".to_owned()});
- }
- let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
+
+ let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash)
+ .map_err(|_| APIError::InvalidRoute { err: "Route size too large considering onion data".to_owned()})?;
let err: Result<(), _> = loop {
let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.hops.first().unwrap().short_channel_id) {
}
}
self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, |chan, tx| {
+ if tx.output.len() > u16::max_value() as usize {
+ return Err(APIError::APIMisuseError {
+ err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
+ });
+ }
+
let mut output_index = None;
let expected_spk = chan.get_funding_redeemscript().to_v0_p2wsh();
for (idx, outp) in tx.output.iter().enumerate() {
err: "Multiple outputs matched the expected script and value".to_owned()
});
}
- if idx > u16::max_value() as usize {
- return Err(APIError::APIMisuseError {
- err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
- });
- }
output_index = Some(idx as u16);
}
}
pub fn process_pending_htlc_forwards(&self) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
- let mut new_events = Vec::new();
+ let mut new_events = VecDeque::new();
let mut failed_forwards = Vec::new();
let mut phantom_receives: Vec<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
{
htlcs.push(claimable_htlc);
let amount_msat = htlcs.iter().map(|htlc| htlc.value).sum();
htlcs.iter_mut().for_each(|htlc| htlc.total_value_received = Some(amount_msat));
- new_events.push(events::Event::PaymentClaimable {
+ new_events.push_back((events::Event::PaymentClaimable {
receiver_node_id: Some(receiver_node_id),
payment_hash,
purpose: purpose(),
via_user_channel_id: Some(prev_user_channel_id),
claim_deadline: Some(earliest_expiry - HTLC_FAIL_BACK_BUFFER),
onion_fields: claimable_payment.onion_fields.clone(),
- });
+ }, None));
payment_claimable_generated = true;
} else {
// Nothing to do - we haven't reached the total
htlcs: vec![claimable_htlc],
});
let prev_channel_id = prev_funding_outpoint.to_channel_id();
- new_events.push(events::Event::PaymentClaimable {
+ new_events.push_back((events::Event::PaymentClaimable {
receiver_node_id: Some(receiver_node_id),
payment_hash,
amount_msat,
via_user_channel_id: Some(prev_user_channel_id),
claim_deadline,
onion_fields: Some(onion_fields),
- });
+ }, None));
},
hash_map::Entry::Occupied(_) => {
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} for a duplicative payment hash", log_bytes!(payment_hash.0));
for event in background_events.drain(..) {
match event {
- BackgroundEvent::ClosingMonitorUpdate((funding_txo, update)) => {
+ BackgroundEvent::MonitorUpdateRegeneratedOnStartup((funding_txo, update)) => {
// The channel has already been closed, so no use bothering to care about the
// monitor updating completing.
let _ = self.chain_monitor.update_channel(funding_txo, &update);
mem::drop(forward_htlcs);
if push_forward_ev { self.push_pending_forwards_ev(); }
let mut pending_events = self.pending_events.lock().unwrap();
- pending_events.push(events::Event::HTLCHandlingFailed {
+ pending_events.push_back((events::Event::HTLCHandlingFailed {
prev_channel_id: outpoint.to_channel_id(),
failed_next_destination: destination,
- });
+ }, None));
},
}
}
MonitorUpdateCompletionAction::PaymentClaimed { payment_hash } => {
let payment = self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
if let Some(ClaimingPayment { amount_msat, payment_purpose: purpose, receiver_node_id }) = payment {
- self.pending_events.lock().unwrap().push(events::Event::PaymentClaimed {
+ self.pending_events.lock().unwrap().push_back((events::Event::PaymentClaimed {
payment_hash, purpose, amount_msat, receiver_node_id: Some(receiver_node_id),
- });
+ }, None));
}
},
MonitorUpdateCompletionAction::EmitEvent { event } => {
- self.pending_events.lock().unwrap().push(event);
+ self.pending_events.lock().unwrap().push_back((event, None));
},
}
}
});
} else {
let mut pending_events = self.pending_events.lock().unwrap();
- pending_events.push(
- events::Event::OpenChannelRequest {
- temporary_channel_id: msg.temporary_channel_id.clone(),
- counterparty_node_id: counterparty_node_id.clone(),
- funding_satoshis: msg.funding_satoshis,
- push_msat: msg.push_msat,
- channel_type: channel.get_channel_type().clone(),
- }
- );
+ pending_events.push_back((events::Event::OpenChannelRequest {
+ temporary_channel_id: msg.temporary_channel_id.clone(),
+ counterparty_node_id: counterparty_node_id.clone(),
+ funding_satoshis: msg.funding_satoshis,
+ push_msat: msg.push_msat,
+ channel_type: channel.get_channel_type().clone(),
+ }, None));
}
entry.insert(channel);
}
};
let mut pending_events = self.pending_events.lock().unwrap();
- pending_events.push(events::Event::FundingGenerationReady {
+ pending_events.push_back((events::Event::FundingGenerationReady {
temporary_channel_id: msg.temporary_channel_id,
counterparty_node_id: *counterparty_node_id,
channel_value_satoshis: value,
output_script,
user_channel_id: user_id,
- });
+ }, None));
Ok(())
}
match peer_state.channel_by_id.entry(msg.channel_id) {
hash_map::Entry::Occupied(mut chan) => {
let funding_txo = chan.get().get_funding_txo();
- let monitor_update = try_chan_entry!(self, chan.get_mut().commitment_signed(&msg, &self.logger), chan);
- let update_res = self.chain_monitor.update_channel(funding_txo.unwrap(), monitor_update);
- let update_id = monitor_update.update_id;
- handle_new_monitor_update!(self, update_res, update_id, peer_state_lock,
- peer_state, per_peer_state, chan)
+ let monitor_update_opt = try_chan_entry!(self, chan.get_mut().commitment_signed(&msg, &self.logger), chan);
+ if let Some(monitor_update) = monitor_update_opt {
+ let update_res = self.chain_monitor.update_channel(funding_txo.unwrap(), monitor_update);
+ let update_id = monitor_update.update_id;
+ handle_new_monitor_update!(self, update_res, update_id, peer_state_lock,
+ peer_state, per_peer_state, chan)
+ } else { Ok(()) }
},
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id))
}
fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
let mut push_forward_event = false;
- let mut new_intercept_events = Vec::new();
+ let mut new_intercept_events = VecDeque::new();
let mut failed_intercept_forwards = Vec::new();
if !pending_forwards.is_empty() {
for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
match pending_intercepts.entry(intercept_id) {
hash_map::Entry::Vacant(entry) => {
- new_intercept_events.push(events::Event::HTLCIntercepted {
+ new_intercept_events.push_back((events::Event::HTLCIntercepted {
requested_next_hop_scid: scid,
payment_hash: forward_info.payment_hash,
inbound_amount_msat: forward_info.incoming_amt_msat.unwrap(),
expected_outbound_amount_msat: forward_info.outgoing_amt_msat,
intercept_id
- });
+ }, None));
entry.insert(PendingAddHTLCInfo {
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info });
},
fn push_pending_forwards_ev(&self) {
let mut pending_events = self.pending_events.lock().unwrap();
let forward_ev_exists = pending_events.iter()
- .find(|ev| if let events::Event::PendingHTLCsForwardable { .. } = ev { true } else { false })
+ .find(|(ev, _)| if let events::Event::PendingHTLCsForwardable { .. } = ev { true } else { false })
.is_some();
if !forward_ev_exists {
- pending_events.push(events::Event::PendingHTLCsForwardable {
+ pending_events.push_back((events::Event::PendingHTLCsForwardable {
time_forwardable:
Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS),
- });
+ }, None));
}
}
match peer_state.channel_by_id.entry(msg.channel_id) {
hash_map::Entry::Occupied(mut chan) => {
let funding_txo = chan.get().get_funding_txo();
- let (htlcs_to_fail, monitor_update) = try_chan_entry!(self, chan.get_mut().revoke_and_ack(&msg, &self.logger), chan);
- let update_res = self.chain_monitor.update_channel(funding_txo.unwrap(), monitor_update);
- let update_id = monitor_update.update_id;
- let res = handle_new_monitor_update!(self, update_res, update_id,
- peer_state_lock, peer_state, per_peer_state, chan);
+ let (htlcs_to_fail, monitor_update_opt) = try_chan_entry!(self, chan.get_mut().revoke_and_ack(&msg, &self.logger), chan);
+ let res = if let Some(monitor_update) = monitor_update_opt {
+ let update_res = self.chain_monitor.update_channel(funding_txo.unwrap(), monitor_update);
+ let update_id = monitor_update.update_id;
+ handle_new_monitor_update!(self, update_res, update_id,
+ peer_state_lock, peer_state, per_peer_state, chan)
+ } else { Ok(()) };
(htlcs_to_fail, res)
},
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id))
if let ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } = update.updates[0] {
assert!(should_broadcast);
} else { unreachable!(); }
- self.pending_background_events.lock().unwrap().push(BackgroundEvent::ClosingMonitorUpdate((funding_txo, update)));
+ self.pending_background_events.lock().unwrap().push(BackgroundEvent::MonitorUpdateRegeneratedOnStartup((funding_txo, update)));
}
self.finish_force_close_channel(failure);
}
/// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
/// are used when constructing the phantom invoice's route hints.
///
- /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom node payments]: crate::sign::PhantomKeysManager
pub fn get_phantom_scid(&self) -> u64 {
let best_block_height = self.best_block.read().unwrap().height();
let short_to_chan_info = self.short_to_chan_info.read().unwrap();
/// Gets route hints for use in receiving [phantom node payments].
///
- /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom node payments]: crate::sign::PhantomKeysManager
pub fn get_phantom_route_hints(&self) -> PhantomRouteHints {
PhantomRouteHints {
channels: self.list_usable_channels(),
#[cfg(feature = "_test_utils")]
pub fn push_pending_event(&self, event: events::Event) {
let mut events = self.pending_events.lock().unwrap();
- events.push(event);
+ events.push_back((event, None));
}
#[cfg(test)]
pub fn pop_pending_event(&self) -> Option<events::Event> {
let mut events = self.pending_events.lock().unwrap();
- if events.is_empty() { None } else { Some(events.remove(0)) }
+ events.pop_front().map(|(e, _)| e)
}
#[cfg(test)]
self.pending_outbound_payments.clear_pending_payments()
}
+ fn handle_monitor_update_release(&self, counterparty_node_id: PublicKey, channel_funding_outpoint: OutPoint) {
+ let mut errors = Vec::new();
+ loop {
+ let per_peer_state = self.per_peer_state.read().unwrap();
+ if let Some(peer_state_mtx) = per_peer_state.get(&counterparty_node_id) {
+ let mut peer_state_lck = peer_state_mtx.lock().unwrap();
+ let peer_state = &mut *peer_state_lck;
+ if self.pending_events.lock().unwrap().iter()
+ .any(|(_ev, action_opt)| action_opt == &Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
+ channel_funding_outpoint, counterparty_node_id
+ }))
+ {
+ // Check that, while holding the peer lock, we don't have another event
+ // blocking any monitor updates for this channel. If we do, let those
+ // events be the ones that ultimately release the monitor update(s).
+ log_trace!(self.logger, "Delaying monitor unlock for channel {} as another event is pending",
+ log_bytes!(&channel_funding_outpoint.to_channel_id()[..]));
+ break;
+ }
+ if let hash_map::Entry::Occupied(mut chan) = peer_state.channel_by_id.entry(channel_funding_outpoint.to_channel_id()) {
+ debug_assert_eq!(chan.get().get_funding_txo().unwrap(), channel_funding_outpoint);
+ if let Some((monitor_update, further_update_exists)) = chan.get_mut().unblock_next_blocked_monitor_update() {
+ log_debug!(self.logger, "Unlocking monitor updating for channel {} and updating monitor",
+ log_bytes!(&channel_funding_outpoint.to_channel_id()[..]));
+ let update_res = self.chain_monitor.update_channel(channel_funding_outpoint, monitor_update);
+ let update_id = monitor_update.update_id;
+ if let Err(e) = handle_new_monitor_update!(self, update_res, update_id,
+ peer_state_lck, peer_state, per_peer_state, chan)
+ {
+ errors.push((e, counterparty_node_id));
+ }
+ if further_update_exists {
+ // If there are more `ChannelMonitorUpdate`s to process, restart at the
+ // top of the loop.
+ continue;
+ }
+ } else {
+ log_trace!(self.logger, "Unlocked monitor updating for channel {} without monitors to update",
+ log_bytes!(&channel_funding_outpoint.to_channel_id()[..]));
+ }
+ }
+ } else {
+ log_debug!(self.logger,
+ "Got a release post-RAA monitor update for peer {} but the channel is gone",
+ log_pubkey!(counterparty_node_id));
+ }
+ break;
+ }
+ for (err, counterparty_node_id) in errors {
+ let res = Err::<(), _>(err);
+ let _ = handle_error!(self, res, counterparty_node_id);
+ }
+ }
+
+ fn handle_post_event_actions(&self, actions: Vec<EventCompletionAction>) {
+ for action in actions {
+ match action {
+ EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
+ channel_funding_outpoint, counterparty_node_id
+ } => {
+ self.handle_monitor_update_release(counterparty_node_id, channel_funding_outpoint);
+ }
+ }
+ }
+ }
+
/// Processes any events asynchronously in the order they were generated since the last call
/// using the given event handler.
///
let _ = handle_error!(self, self.internal_open_channel(counterparty_node_id, msg), *counterparty_node_id);
}
+ fn handle_open_channel_v2(&self, counterparty_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.temporary_channel_id.clone())), *counterparty_node_id);
+ }
+
fn handle_accept_channel(&self, counterparty_node_id: &PublicKey, msg: &msgs::AcceptChannel) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let _ = handle_error!(self, self.internal_accept_channel(counterparty_node_id, msg), *counterparty_node_id);
}
+ fn handle_accept_channel_v2(&self, counterparty_node_id: &PublicKey, msg: &msgs::AcceptChannelV2) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.temporary_channel_id.clone())), *counterparty_node_id);
+ }
+
fn handle_funding_created(&self, counterparty_node_id: &PublicKey, msg: &msgs::FundingCreated) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let _ = handle_error!(self, self.internal_funding_created(counterparty_node_id, msg), *counterparty_node_id);
});
pending_msg_events.retain(|msg| {
match msg {
+ // V1 Channel Establishment
&events::MessageSendEvent::SendAcceptChannel { .. } => false,
&events::MessageSendEvent::SendOpenChannel { .. } => false,
&events::MessageSendEvent::SendFundingCreated { .. } => false,
&events::MessageSendEvent::SendFundingSigned { .. } => false,
+ // V2 Channel Establishment
+ &events::MessageSendEvent::SendAcceptChannelV2 { .. } => false,
+ &events::MessageSendEvent::SendOpenChannelV2 { .. } => false,
+ // Common Channel Establishment
&events::MessageSendEvent::SendChannelReady { .. } => false,
&events::MessageSendEvent::SendAnnouncementSignatures { .. } => false,
+ // Interactive Transaction Construction
+ &events::MessageSendEvent::SendTxAddInput { .. } => false,
+ &events::MessageSendEvent::SendTxAddOutput { .. } => false,
+ &events::MessageSendEvent::SendTxRemoveInput { .. } => false,
+ &events::MessageSendEvent::SendTxRemoveOutput { .. } => false,
+ &events::MessageSendEvent::SendTxComplete { .. } => false,
+ &events::MessageSendEvent::SendTxSignatures { .. } => false,
+ &events::MessageSendEvent::SendTxInitRbf { .. } => false,
+ &events::MessageSendEvent::SendTxAckRbf { .. } => false,
+ &events::MessageSendEvent::SendTxAbort { .. } => false,
+ // Channel Operations
&events::MessageSendEvent::UpdateHTLCs { .. } => false,
&events::MessageSendEvent::SendRevokeAndACK { .. } => false,
&events::MessageSendEvent::SendClosingSigned { .. } => false,
&events::MessageSendEvent::SendShutdown { .. } => false,
&events::MessageSendEvent::SendChannelReestablish { .. } => false,
+ &events::MessageSendEvent::HandleError { .. } => false,
+ // Gossip
&events::MessageSendEvent::SendChannelAnnouncement { .. } => false,
&events::MessageSendEvent::BroadcastChannelAnnouncement { .. } => true,
&events::MessageSendEvent::BroadcastChannelUpdate { .. } => true,
&events::MessageSendEvent::BroadcastNodeAnnouncement { .. } => true,
&events::MessageSendEvent::SendChannelUpdate { .. } => false,
- &events::MessageSendEvent::HandleError { .. } => false,
&events::MessageSendEvent::SendChannelRangeQuery { .. } => false,
&events::MessageSendEvent::SendShortIdsQuery { .. } => false,
&events::MessageSendEvent::SendReplyChannelRange { .. } => false,
fn provided_init_features(&self, _their_init_features: &PublicKey) -> InitFeatures {
provided_init_features(&self.default_configuration)
}
+
+ fn handle_tx_add_input(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAddInput) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_add_output(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAddOutput) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_remove_input(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxRemoveInput) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_remove_output(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxRemoveOutput) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_complete(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxComplete) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_signatures(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxSignatures) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_init_rbf(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxInitRbf) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_ack_rbf(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAckRbf) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
+
+ fn handle_tx_abort(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAbort) {
+ let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
+ "Dual-funded channels not supported".to_owned(),
+ msg.channel_id.clone())), *counterparty_node_id);
+ }
}
/// Fetches the set of [`NodeFeatures`] flags which are provided by or required by
// should also add the corresponding (optional) bit to the [`ChannelMessageHandler`] impl for
// [`ErroringMessageHandler`].
let mut features = InitFeatures::empty();
- features.set_data_loss_protect_optional();
+ features.set_data_loss_protect_required();
features.set_upfront_shutdown_script_optional();
features.set_variable_length_onion_required();
features.set_static_remote_key_required();
return Err(DecodeError::InvalidValue);
}
if let Some(params) = payment_params.as_mut() {
- if params.final_cltv_expiry_delta == 0 {
- params.final_cltv_expiry_delta = path.final_cltv_expiry_delta().ok_or(DecodeError::InvalidValue)?;
+ if let Payee::Clear { ref mut final_cltv_expiry_delta, .. } = params.payee {
+ if final_cltv_expiry_delta == &0 {
+ *final_cltv_expiry_delta = path.final_cltv_expiry_delta().ok_or(DecodeError::InvalidValue)?;
+ }
}
}
Ok(HTLCSource::OutboundRoute {
}
let events = self.pending_events.lock().unwrap();
- (events.len() as u64).write(writer)?;
- for event in events.iter() {
- event.write(writer)?;
- }
-
- let background_events = self.pending_background_events.lock().unwrap();
- (background_events.len() as u64).write(writer)?;
- for event in background_events.iter() {
- match event {
- BackgroundEvent::ClosingMonitorUpdate((funding_txo, monitor_update)) => {
- 0u8.write(writer)?;
- funding_txo.write(writer)?;
- monitor_update.write(writer)?;
- },
+ // LDK versions prior to 0.0.115 don't support post-event actions, thus if there's no
+ // actions at all, skip writing the required TLV. Otherwise, pre-0.0.115 versions will
+ // refuse to read the new ChannelManager.
+ let events_not_backwards_compatible = events.iter().any(|(_, action)| action.is_some());
+ if events_not_backwards_compatible {
+ // If we're gonna write a even TLV that will overwrite our events anyway we might as
+ // well save the space and not write any events here.
+ 0u64.write(writer)?;
+ } else {
+ (events.len() as u64).write(writer)?;
+ for (event, _) in events.iter() {
+ event.write(writer)?;
}
}
+ // LDK versions prior to 0.0.116 wrote the `pending_background_events`
+ // `MonitorUpdateRegeneratedOnStartup`s here, however there was never a reason to do so -
+ // the closing monitor updates were always effectively replayed on startup (either directly
+ // by calling `broadcast_latest_holder_commitment_txn` on a `ChannelMonitor` during
+ // deserialization or, in 0.0.115, by regenerating the monitor update itself).
+ 0u64.write(writer)?;
+
// Prior to 0.0.111 we tracked node_announcement serials here, however that now happens in
// `PeerManager`, and thus we simply write the `highest_seen_timestamp` twice, which is
// likely to be identical.
(5, self.our_network_pubkey, required),
(6, monitor_update_blocked_actions_per_peer, option),
(7, self.fake_scid_rand_bytes, required),
+ (8, if events_not_backwards_compatible { Some(&*events) } else { None }, option),
(9, htlc_purposes, vec_type),
(11, self.probing_cookie_secret, required),
(13, htlc_onion_fields, optional_vec),
}
}
+impl Writeable for VecDeque<(Event, Option<EventCompletionAction>)> {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ (self.len() as u64).write(w)?;
+ for (event, action) in self.iter() {
+ event.write(w)?;
+ action.write(w)?;
+ #[cfg(debug_assertions)] {
+ // Events are MaybeReadable, in some cases indicating that they shouldn't actually
+ // be persisted and are regenerated on restart. However, if such an event has a
+ // post-event-handling action we'll write nothing for the event and would have to
+ // either forget the action or fail on deserialization (which we do below). Thus,
+ // check that the event is sane here.
+ let event_encoded = event.encode();
+ let event_read: Option<Event> =
+ MaybeReadable::read(&mut &event_encoded[..]).unwrap();
+ if action.is_some() { assert!(event_read.is_some()); }
+ }
+ }
+ Ok(())
+ }
+}
+impl Readable for VecDeque<(Event, Option<EventCompletionAction>)> {
+ fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ let len: u64 = Readable::read(reader)?;
+ const MAX_ALLOC_SIZE: u64 = 1024 * 16;
+ let mut events: Self = VecDeque::with_capacity(cmp::min(
+ MAX_ALLOC_SIZE/mem::size_of::<(events::Event, Option<EventCompletionAction>)>() as u64,
+ len) as usize);
+ for _ in 0..len {
+ let ev_opt = MaybeReadable::read(reader)?;
+ let action = Readable::read(reader)?;
+ if let Some(ev) = ev_opt {
+ events.push_back((ev, action));
+ } else if action.is_some() {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
+ Ok(events)
+ }
+}
+
/// Arguments for the creation of a ChannelManager that are not deserialized.
///
/// At a high-level, the process for deserializing a ChannelManager and resuming normal operation
let mut peer_channels: HashMap<PublicKey, HashMap<[u8; 32], Channel<<SP::Target as SignerProvider>::Signer>>> = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
let mut id_to_peer = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
let mut short_to_chan_info = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
- let mut channel_closures = Vec::new();
+ let mut channel_closures = VecDeque::new();
let mut pending_background_events = Vec::new();
for _ in 0..channel_count {
let mut channel: Channel<<SP::Target as SignerProvider>::Signer> = Channel::read(reader, (
let funding_txo = channel.get_funding_txo().ok_or(DecodeError::InvalidValue)?;
funding_txo_set.insert(funding_txo.clone());
if let Some(ref mut monitor) = args.channel_monitors.get_mut(&funding_txo) {
- if channel.get_cur_holder_commitment_transaction_number() < monitor.get_cur_holder_commitment_number() ||
- channel.get_revoked_counterparty_commitment_transaction_number() < monitor.get_min_seen_secret() ||
- channel.get_cur_counterparty_commitment_transaction_number() < monitor.get_cur_counterparty_commitment_number() ||
- channel.get_latest_monitor_update_id() > monitor.get_latest_update_id() {
+ if channel.get_latest_complete_monitor_update_id() > monitor.get_latest_update_id() {
// If the channel is ahead of the monitor, return InvalidValue:
log_error!(args.logger, "A ChannelMonitor is stale compared to the current ChannelManager! This indicates a potentially-critical violation of the chain::Watch API!");
log_error!(args.logger, " The ChannelMonitor for channel {} is at update_id {} but the ChannelManager is at update_id {}.",
- log_bytes!(channel.channel_id()), monitor.get_latest_update_id(), channel.get_latest_monitor_update_id());
+ log_bytes!(channel.channel_id()), monitor.get_latest_update_id(), channel.get_latest_complete_monitor_update_id());
log_error!(args.logger, " The chain::Watch API *requires* that monitors are persisted durably before returning,");
log_error!(args.logger, " client applications must ensure that ChannelMonitor data is always available and the latest to avoid funds loss!");
log_error!(args.logger, " Without the latest ChannelMonitor we cannot continue without risking funds.");
log_bytes!(channel.channel_id()), monitor.get_latest_update_id(), channel.get_latest_monitor_update_id());
let (monitor_update, mut new_failed_htlcs) = channel.force_shutdown(true);
if let Some(monitor_update) = monitor_update {
- pending_background_events.push(BackgroundEvent::ClosingMonitorUpdate(monitor_update));
+ pending_background_events.push(BackgroundEvent::MonitorUpdateRegeneratedOnStartup(monitor_update));
}
failed_htlcs.append(&mut new_failed_htlcs);
- channel_closures.push(events::Event::ChannelClosed {
+ channel_closures.push_back((events::Event::ChannelClosed {
channel_id: channel.channel_id(),
user_channel_id: channel.get_user_id(),
reason: ClosureReason::OutdatedChannelManager
- });
+ }, None));
for (channel_htlc_source, payment_hash) in channel.inflight_htlc_sources() {
let mut found_htlc = false;
for (monitor_htlc_source, _) in monitor.get_all_current_outbound_htlcs() {
// was in-progress, we never broadcasted the funding transaction and can still
// safely discard the channel.
let _ = channel.force_shutdown(false);
- channel_closures.push(events::Event::ChannelClosed {
+ channel_closures.push_back((events::Event::ChannelClosed {
channel_id: channel.channel_id(),
user_channel_id: channel.get_user_id(),
reason: ClosureReason::DisconnectedPeer,
- });
+ }, None));
} else {
log_error!(args.logger, "Missing ChannelMonitor for channel {} needed by ChannelManager.", log_bytes!(channel.channel_id()));
log_error!(args.logger, " The chain::Watch API *requires* that monitors are persisted durably before returning,");
for (funding_txo, _) in args.channel_monitors.iter() {
if !funding_txo_set.contains(funding_txo) {
+ log_info!(args.logger, "Queueing monitor update to ensure missing channel {} is force closed",
+ log_bytes!(funding_txo.to_channel_id()));
let monitor_update = ChannelMonitorUpdate {
update_id: CLOSED_CHANNEL_UPDATE_ID,
updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast: true }],
};
- pending_background_events.push(BackgroundEvent::ClosingMonitorUpdate((*funding_txo, monitor_update)));
+ pending_background_events.push(BackgroundEvent::MonitorUpdateRegeneratedOnStartup((*funding_txo, monitor_update)));
}
}
}
let event_count: u64 = Readable::read(reader)?;
- let mut pending_events_read: Vec<events::Event> = Vec::with_capacity(cmp::min(event_count as usize, MAX_ALLOC_SIZE/mem::size_of::<events::Event>()));
+ let mut pending_events_read: VecDeque<(events::Event, Option<EventCompletionAction>)> =
+ VecDeque::with_capacity(cmp::min(event_count as usize, MAX_ALLOC_SIZE/mem::size_of::<(events::Event, Option<EventCompletionAction>)>()));
for _ in 0..event_count {
match MaybeReadable::read(reader)? {
- Some(event) => pending_events_read.push(event),
+ Some(event) => pending_events_read.push_back((event, None)),
None => continue,
}
}
for _ in 0..background_event_count {
match <u8 as Readable>::read(reader)? {
0 => {
- let (funding_txo, monitor_update): (OutPoint, ChannelMonitorUpdate) = (Readable::read(reader)?, Readable::read(reader)?);
- if pending_background_events.iter().find(|e| {
- let BackgroundEvent::ClosingMonitorUpdate((pending_funding_txo, pending_monitor_update)) = e;
- *pending_funding_txo == funding_txo && *pending_monitor_update == monitor_update
- }).is_none() {
- pending_background_events.push(BackgroundEvent::ClosingMonitorUpdate((funding_txo, monitor_update)));
- }
+ // LDK versions prior to 0.0.116 wrote pending `MonitorUpdateRegeneratedOnStartup`s here,
+ // however we really don't (and never did) need them - we regenerate all
+ // on-startup monitor updates.
+ let _: OutPoint = Readable::read(reader)?;
+ let _: ChannelMonitorUpdate = Readable::read(reader)?;
}
_ => return Err(DecodeError::InvalidValue),
}
let mut claimable_htlc_onion_fields = None;
let mut pending_claiming_payments = Some(HashMap::new());
let mut monitor_update_blocked_actions_per_peer = Some(Vec::new());
+ let mut events_override = None;
read_tlv_fields!(reader, {
(1, pending_outbound_payments_no_retry, option),
(2, pending_intercepted_htlcs, option),
(5, received_network_pubkey, option),
(6, monitor_update_blocked_actions_per_peer, option),
(7, fake_scid_rand_bytes, option),
+ (8, events_override, option),
(9, claimable_htlc_purposes, vec_type),
(11, probing_cookie_secret, option),
(13, claimable_htlc_onion_fields, optional_vec),
probing_cookie_secret = Some(args.entropy_source.get_secure_random_bytes());
}
+ if let Some(events) = events_override {
+ pending_events_read = events;
+ }
+
if !channel_closures.is_empty() {
pending_events_read.append(&mut channel_closures);
}
if pending_forward_matches_htlc(&htlc_info) {
log_info!(args.logger, "Removing pending intercepted HTLC with hash {} as it was forwarded to the closed channel {}",
log_bytes!(htlc.payment_hash.0), log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
- pending_events_read.retain(|event| {
+ pending_events_read.retain(|(event, _)| {
if let Event::HTLCIntercepted { intercept_id: ev_id, .. } = event {
intercepted_id != ev_id
} else { true }
// shut down before the timer hit. Either way, set the time_forwardable to a small
// constant as enough time has likely passed that we should simply handle the forwards
// now, or at least after the user gets a chance to reconnect to our peers.
- pending_events_read.push(events::Event::PendingHTLCsForwardable {
+ pending_events_read.push_back((events::Event::PendingHTLCsForwardable {
time_forwardable: Duration::from_secs(2),
- });
+ }, None));
}
let inbound_pmt_key_material = args.node_signer.get_inbound_payment_key_material();
previous_hop_monitor.provide_payment_preimage(&payment_hash, &payment_preimage, &args.tx_broadcaster, &bounded_fee_estimator, &args.logger);
}
}
- pending_events_read.push(events::Event::PaymentClaimed {
+ pending_events_read.push_back((events::Event::PaymentClaimed {
receiver_node_id,
payment_hash,
purpose: payment.purpose,
amount_msat: claimable_amt_msat,
- });
+ }, None));
}
}
}
use crate::util::errors::APIError;
use crate::util::test_utils;
use crate::util::config::ChannelConfig;
- use crate::chain::keysinterface::EntropySource;
+ use crate::sign::EntropySource;
#[test]
fn test_notify_limits() {
};
let route = find_route(
&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph,
- None, nodes[0].logger, &scorer, &random_seed_bytes
+ None, nodes[0].logger, &scorer, &(), &random_seed_bytes
).unwrap();
nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage),
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_preimage.0)).unwrap();
let payment_preimage = PaymentPreimage([42; 32]);
let route = find_route(
&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph,
- None, nodes[0].logger, &scorer, &random_seed_bytes
+ None, nodes[0].logger, &scorer, &(), &random_seed_bytes
).unwrap();
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage),
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_preimage.0)).unwrap();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route = find_route(
&payer_pubkey, &route_params, &network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
- nodes[0].logger, &scorer, &random_seed_bytes
+ nodes[0].logger, &scorer, &(), &random_seed_bytes
).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route = find_route(
&payer_pubkey, &route_params, &network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
- nodes[0].logger, &scorer, &random_seed_bytes
+ nodes[0].logger, &scorer, &(), &random_seed_bytes
).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
pub mod bench {
use crate::chain::Listen;
use crate::chain::chainmonitor::{ChainMonitor, Persist};
- use crate::chain::keysinterface::{KeysManager, InMemorySigner};
+ use crate::sign::{KeysManager, InMemorySigner};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider};
use crate::ln::channelmanager::{BestBlock, ChainParameters, ChannelManager, PaymentHash, PaymentPreimage, PaymentId, RecipientOnionFields, Retry};
use crate::ln::functional_test_utils::*;
assert_eq!(&tx_broadcaster.txn_broadcasted.lock().unwrap()[..], &[tx.clone()]);
- let block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: BestBlock::from_network(network).block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![tx],
- };
+ let block = create_dummy_block(BestBlock::from_network(network).block_hash(), 42, vec![tx]);
Listen::block_connected(&node_a, &block, 1);
Listen::block_connected(&node_b, &block, 1);
macro_rules! send_payment {
($node_a: expr, $node_b: expr) => {
let payment_params = PaymentParameters::from_node_id($node_b.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features($node_b.invoice_features());
+ .with_bolt11_features($node_b.invoice_features()).unwrap();
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
mark: PhantomData<T>,
}
-impl <T: sealed::Context> Features<T> {
- pub(crate) fn or(mut self, o: Self) -> Self {
+impl<T: sealed::Context> core::ops::BitOr for Features<T> {
+ type Output = Self;
+
+ fn bitor(mut self, o: Self) -> Self {
let total_feature_len = cmp::max(self.flags.len(), o.flags.len());
self.flags.resize(total_feature_len, 0u8);
for (byte, o_byte) in self.flags.iter_mut().zip(o.flags.iter()) {
self.flags.eq(&o.flags)
}
}
+impl<T: sealed::Context> PartialOrd for Features<T> {
+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+ self.flags.partial_cmp(&other.flags)
+ }
+}
+impl<T: sealed::Context + Eq> Ord for Features<T> {
+ fn cmp(&self, other: &Self) -> cmp::Ordering {
+ self.flags.cmp(&other.flags)
+ }
+}
impl<T: sealed::Context> fmt::Debug for Features<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.flags.fmt(fmt)
}
}
+impl Bolt12InvoiceFeatures {
+ /// Converts `Bolt12InvoiceFeatures` to `Features<C>`. Only known `Bolt12InvoiceFeatures` relevant
+ /// to context `C` are included in the result.
+ pub(crate) fn to_context<C: sealed::Context>(&self) -> Features<C> {
+ self.to_context_internal()
+ }
+}
+
impl ChannelTypeFeatures {
// Maps the relevant `InitFeatures` to `ChannelTypeFeatures`. Any unknown features to
// `ChannelTypeFeatures` are not included in the result.
self.flags.iter().any(|&byte| (byte & 0b10_10_10_10) != 0)
}
+ /// Returns true if this `Features` object contains required features unknown by `other`.
+ pub fn requires_unknown_bits_from(&self, other: &Features<T>) -> 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)| {
+ const REQUIRED_FEATURES: u8 = 0b01_01_01_01;
+ const OPTIONAL_FEATURES: u8 = 0b10_10_10_10;
+ let unknown_features = if i < other.flags.len() {
+ // Form a mask similar to !T::KNOWN_FEATURE_MASK only for `other`
+ !(other.flags[i]
+ | ((other.flags[i] >> 1) & REQUIRED_FEATURES)
+ | ((other.flags[i] << 1) & OPTIONAL_FEATURES))
+ } else {
+ 0b11_11_11_11
+ };
+ (byte & (REQUIRED_FEATURES & unknown_features)) != 0
+ })
+ }
+
/// Returns true if this `Features` object contains unknown feature flags which are set as
/// "required".
pub fn requires_unknown_bits(&self) -> bool {
}
true
}
+
+ /// 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(());
+ }
+
+ let byte_offset = bit / 8;
+ let mask = 1 << (bit - 8 * byte_offset);
+ if byte_offset < T::KNOWN_FEATURE_MASK.len() {
+ 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<T: sealed::UpfrontShutdownScript> Features<T> {
impl_feature_len_prefixed_write!(ChannelFeatures);
impl_feature_len_prefixed_write!(NodeFeatures);
impl_feature_len_prefixed_write!(InvoiceFeatures);
+impl_feature_len_prefixed_write!(Bolt12InvoiceFeatures);
impl_feature_len_prefixed_write!(BlindedHopFeatures);
// Some features only appear inside of TLVs, so they don't have a length prefix when serialized.
assert!(features.supports_unknown_bits());
}
+ #[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_optional();
+ 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_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();
- init_features.set_anchors_zero_fee_htlc_tx_optional();
assert!(init_features.initial_routing_sync());
assert!(!init_features.supports_upfront_shutdown_script());
let node_features: NodeFeatures = init_features.to_context();
{
// Check that the flags are as expected:
- // - option_data_loss_protect
+ // - option_data_loss_protect (req)
// - var_onion_optin (req) | static_remote_key (req) | payment_secret(req)
- // - basic_mpp | wumbo
+ // - basic_mpp | wumbo | anchors_zero_fee_htlc_tx
// - 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], 0b00000010);
+ 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], 0b00001000);
assert!(features.supports_payment_secret());
}
+ #[test]
+ fn set_custom_bits() {
+ let mut features = InvoiceFeatures::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 = <sealed::InvoiceContext as sealed::PaymentSecret>::EVEN_BIT;
+ let byte_offset = <sealed::InvoiceContext as sealed::PaymentSecret>::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 = InvoiceFeatures::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 = InvoiceFeatures::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]);
//! A bunch of useful utilities for building networks of nodes and exchanging messages between
//! nodes for functional tests.
-use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch, keysinterface::EntropySource};
+use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
+use crate::sign::EntropySource;
use crate::chain::channelmonitor::ChannelMonitor;
use crate::chain::transaction::OutPoint;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason};
if conf_height > first_connect_height {
connect_blocks(node, conf_height - first_connect_height);
}
- let mut block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: node.best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: conf_height, bits: 42, nonce: 42 },
- txdata: Vec::new(),
- };
+ let mut txdata = Vec::new();
for _ in 0..*node.network_chan_count.borrow() { // Make sure we don't end up with channels at the same short id by offsetting by chan_count
- block.txdata.push(Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() });
+ txdata.push(Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() });
}
for tx in txn {
- block.txdata.push((*tx).clone());
+ txdata.push((*tx).clone());
}
+ let block = create_dummy_block(node.best_block_hash(), conf_height, txdata);
connect_block(node, &block);
scid_utils::scid_from_parts(conf_height as u64, block.txdata.len() as u64 - 1, 0).unwrap()
}
}
}
+pub fn create_dummy_header(prev_blockhash: BlockHash, time: u32) -> BlockHeader {
+ BlockHeader {
+ version: 0x2000_0000,
+ prev_blockhash,
+ merkle_root: TxMerkleNode::all_zeros(),
+ time,
+ bits: 42,
+ nonce: 42,
+ }
+}
+
+pub fn create_dummy_block(prev_blockhash: BlockHash, time: u32, txdata: Vec<Transaction>) -> Block {
+ Block { header: create_dummy_header(prev_blockhash, time), txdata }
+}
+
pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32) -> BlockHash {
let skip_intermediaries = node.connect_style.borrow().skips_blocks();
let height = node.best_block_info().1 + 1;
- let mut block = Block {
- header: BlockHeader { version: 0x2000000, prev_blockhash: node.best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: height, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ let mut block = create_dummy_block(node.best_block_hash(), height, Vec::new());
assert!(depth >= 1);
for i in 1..depth {
let prev_blockhash = block.header.block_hash();
do_connect_block(node, block, skip_intermediaries);
- block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash, merkle_root: TxMerkleNode::all_zeros(), time: height + i, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ block = create_dummy_block(prev_blockhash, height + i, Vec::new());
}
let hash = block.header.block_hash();
do_connect_block(node, block, false);
MessageSendEvent::SendGossipTimestampFilter { node_id, .. } => {
node_id == msg_node_id
},
+ MessageSendEvent::SendAcceptChannelV2 { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendOpenChannelV2 { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAddInput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAddOutput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxRemoveInput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxRemoveOutput { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxComplete { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxSignatures { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxInitRbf { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAckRbf { node_id, .. } => {
+ node_id == msg_node_id
+ },
+ MessageSendEvent::SendTxAbort { node_id, .. } => {
+ node_id == msg_node_id
+ },
}});
if ev_index.is_some() {
msg_events.remove(ev_index.unwrap())
}
/// Gets a route from the given sender to the node described in `payment_params`.
-pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_value: u64, final_cltv_expiry_delta: u32) -> Result<Route, msgs::LightningError> {
+pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_value: u64) -> Result<Route, msgs::LightningError> {
let scorer = TestScorer::new();
let keys_manager = TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
router::get_route(
&send_node.node.get_our_node_id(), payment_params, &send_node.network_graph.read_only(),
Some(&send_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
- recv_value, final_cltv_expiry_delta, send_node.logger, &scorer, &random_seed_bytes
+ recv_value, send_node.logger, &scorer, &(), &random_seed_bytes
)
}
/// Don't use this, use the identically-named function instead.
#[macro_export]
macro_rules! get_route {
- ($send_node: expr, $payment_params: expr, $recv_value: expr, $cltv: expr) => {
- $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value, $cltv)
+ ($send_node: expr, $payment_params: expr, $recv_value: expr) => {
+ $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value)
}
}
macro_rules! get_route_and_payment_hash {
($send_node: expr, $recv_node: expr, $recv_value: expr) => {{
let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features($recv_node.node.invoice_features());
- $crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value, TEST_FINAL_CLTV)
+ .with_bolt11_features($recv_node.node.invoice_features()).unwrap();
+ $crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value)
}};
- ($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr, $cltv: expr) => {{
+ ($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr) => {{
let (payment_preimage, payment_hash, payment_secret) =
$crate::ln::functional_test_utils::get_payment_preimage_hash(&$recv_node, Some($recv_value), None);
- let route = $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value, $cltv);
+ let route = $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value);
(route.unwrap(), payment_hash, payment_preimage, payment_secret)
}}
}
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(expected_route.last().unwrap().node.invoice_features());
- let route = get_route(origin_node, &payment_params, recv_value, TEST_FINAL_CLTV).unwrap();
+ .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
+ let route = get_route(origin_node, &payment_params, recv_value).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops.len(), expected_route.len());
for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(expected_route.last().unwrap().node.invoice_features());
+ .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
let network_graph = origin_node.network_graph.read_only();
let scorer = test_utils::TestScorer::new();
let seed = [0u8; 32];
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let route = router::get_route(
&origin_node.node.get_our_node_id(), &payment_params, &network_graph,
- None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer, &random_seed_bytes).unwrap();
+ None, recv_value, origin_node.logger, &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops.len(), expected_route.len());
for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {
use crate::chain::channelmonitor;
use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
use crate::chain::transaction::OutPoint;
-use crate::chain::keysinterface::{ChannelSigner, EcdsaChannelSigner, EntropySource};
+use crate::sign::{ChannelSigner, EcdsaChannelSigner, EntropySource};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
use crate::ln::{PaymentPreimage, PaymentSecret, PaymentHash};
use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT};
use crate::util::config::UserConfig;
use bitcoin::hash_types::BlockHash;
-use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::script::{Builder, Script};
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::network::constants::Network;
-use bitcoin::{PackedLockTime, Sequence, Transaction, TxIn, TxMerkleNode, TxOut, Witness};
+use bitcoin::{PackedLockTime, Sequence, Transaction, TxIn, TxOut, Witness};
use bitcoin::OutPoint as BitcoinOutPoint;
use bitcoin::secp256k1::Secp256k1;
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
if steps & 0b1000_0000 != 0{
- let block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ let block = create_dummy_block(nodes[0].best_block_hash(), 42, Vec::new());
connect_block(&nodes[0], &block);
connect_block(&nodes[1], &block);
}
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0],
3460001, RecipientOnionFields::secret_only(payment_secret), cur_height, &None).unwrap();
- let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
+ let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
let msg = msgs::UpdateAddHTLC {
channel_id: chan.2,
htlc_id: 0,
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0],
700_000, RecipientOnionFields::secret_only(payment_secret), cur_height, &None).unwrap();
- let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
+ let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
let msg = msgs::UpdateAddHTLC {
channel_id: chan.2,
htlc_id: MIN_AFFORDABLE_HTLC_COUNT as u64,
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv).unwrap();
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
&route_2.paths[0], recv_value_2, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
- let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash_1);
+ let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash_1).unwrap();
let msg = msgs::UpdateAddHTLC {
channel_id: chan.2,
htlc_id: 1,
// attempt to send amt_msat > their_max_htlc_value_in_flight_msat
{
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[2].node.invoice_features()).with_max_channel_saturation_power_of_half(0);
- let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, recv_value_0, TEST_FINAL_CLTV);
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
+ let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, recv_value_0);
route.paths[0].hops.last_mut().unwrap().fee_msat += 1;
assert!(route.paths[0].hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
}
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[2].node.invoice_features()).with_max_channel_saturation_power_of_half(0);
- let route = get_route!(nodes[0], payment_params, recv_value_0, TEST_FINAL_CLTV).unwrap();
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
+ let route = get_route!(nodes[0], payment_params, recv_value_0).unwrap();
let (payment_preimage, ..) = send_along_route(&nodes[0], route, &[&nodes[1], &nodes[2]], recv_value_0);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
assert_eq!(node_txn[1].lock_time.0, 0);
// Verify that B's ChannelManager is able to extract preimage from HTLC Success tx and pass it backward
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42};
- connect_block(&nodes[1], &Block { header, txdata: vec![commitment_tx[0].clone(), node_txn[0].clone(), node_txn[1].clone()]});
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, vec![commitment_tx[0].clone(), node_txn[0].clone(), node_txn[1].clone()]));
connect_blocks(&nodes[1], TEST_FINAL_CLTV); // Confirm blocks until the HTLC expires
{
let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
// we already checked the same situation with A.
// Verify that A's ChannelManager is able to extract preimage from preimage tx and generate PaymentSent
- let mut header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42};
- connect_block(&nodes[0], &Block { header, txdata: vec![node_a_commitment_tx[0].clone(), commitment_spend.clone()] });
+ connect_block(&nodes[0], &create_dummy_block(nodes[0].best_block_hash(), 42, vec![node_a_commitment_tx[0].clone(), commitment_spend.clone()]));
connect_blocks(&nodes[0], TEST_FINAL_CLTV + MIN_CLTV_EXPIRY_DELTA as u32); // Confirm blocks until the HTLC expires
check_closed_broadcast!(nodes[0], true);
check_added_monitors!(nodes[0], 1);
let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(
&route.paths[0], 50_000, RecipientOnionFields::secret_only(payment_secret), current_height, &None).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
- let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
+ let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
// Send a 0-msat update_add_htlc to fail the channel.
let update_add_htlc = msgs::UpdateAddHTLC {
assert_eq!(node_txn.len(), 3);
assert_eq!(node_txn[0].txid(), node_txn[1].txid());
- let mut header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[1].clone()]});
+ let block = create_dummy_block(nodes[1].best_block_hash(), 42, vec![node_txn[0].clone(), node_txn[1].clone()]);
+ connect_block(&nodes[1], &block);
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
// Duplicate the connect_block call since this may happen due to other listeners
// registering new transactions
- connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[2].clone()]});
+ connect_block(&nodes[1], &block);
}
#[test]
route_payment(&nodes[0], &[&nodes[1]], 100000).1
};
- let mut block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ let mut block = create_dummy_block(nodes[0].best_block_hash(), 42, Vec::new());
connect_block(&nodes[0], &block);
connect_block(&nodes[1], &block);
let block_count = TEST_FINAL_CLTV + CHAN_CONFIRM_DEPTH + 2 - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS;
match event {
Event::SpendableOutputs { mut outputs } => {
for outp in outputs.drain(..) {
- txn.push($keysinterface.backing.spend_spendable_outputs(&[&outp], Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &secp_ctx).unwrap());
+ txn.push($keysinterface.backing.spend_spendable_outputs(&[&outp], Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &secp_ctx).unwrap());
all_outputs.push(outp);
}
},
};
}
if all_outputs.len() > 1 {
- if let Ok(tx) = $keysinterface.backing.spend_spendable_outputs(&all_outputs.iter().map(|a| a).collect::<Vec<_>>(), Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &secp_ctx) {
+ if let Ok(tx) = $keysinterface.backing.spend_spendable_outputs(&all_outputs.iter().map(|a| a).collect::<Vec<_>>(), Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &secp_ctx) {
txn.push(tx);
}
}
assert_ne!(revoked_htlc_txn[0].lock_time.0, 0); // HTLC-Timeout
// B will generate justice tx from A's revoked commitment/HTLC tx
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[1], &Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] });
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()]));
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
assert_eq!(revoked_local_txn[0].output[unspent_local_txn_output].script_pubkey.len(), 2 + 20); // P2WPKH
// A will generate justice tx from B's revoked commitment/HTLC tx
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] });
+ connect_block(&nodes[0], &create_dummy_block(nodes[0].best_block_hash(), 42, vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()]));
check_closed_broadcast!(nodes[0], true);
check_added_monitors!(nodes[0], 1);
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
assert_eq!(c_txn[0].lock_time.0, 0); // Success tx
// So we broadcast C's commitment tx and HTLC-Success on B's chain, we should successfully be able to extract preimage and update downstream monitor
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42};
- connect_block(&nodes[1], &Block { header, txdata: vec![commitment_tx[0].clone(), c_txn[0].clone()]});
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, vec![commitment_tx[0].clone(), c_txn[0].clone()]));
check_added_monitors!(nodes[1], 1);
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
// script push size limit so that the below script length checks match
// ACCEPTED_HTLC_SCRIPT_WEIGHT.
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV - 40)
- .with_features(nodes[3].node.invoice_features());
- let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[3], payment_params, 800_000, TEST_FINAL_CLTV - 40);
+ .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[3], payment_params, 800_000);
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[2], &nodes[3]]], 800_000, duplicate_payment_hash, payment_secret);
let commitment_txn = get_local_commitment_txn!(nodes[2], chan_2.2);
check_added_monitors!(nodes[1], 1);
let starting_block = nodes[1].best_block_info();
- let mut block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: starting_block.0, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ let mut block = create_dummy_block(starting_block.0, 42, Vec::new());
for _ in starting_block.1 + 1..TEST_FINAL_CLTV - CLTV_CLAIM_BUFFER + starting_block.1 + 2 {
connect_block(&nodes[1], &block);
block.header.prev_blockhash = block.block_hash();
// to "time out" the HTLC.
let starting_block = nodes[1].best_block_info();
- let mut header = BlockHeader { version: 0x20000000, prev_blockhash: starting_block.0, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
+ let mut block = create_dummy_block(starting_block.0, 42, Vec::new());
for _ in starting_block.1 + 1..TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + starting_block.1 + 2 {
- connect_block(&nodes[0], &Block { header, txdata: Vec::new()});
- header.prev_blockhash = header.block_hash();
+ connect_block(&nodes[0], &block);
+ block.header.prev_blockhash = block.block_hash();
}
test_txn_broadcast(&nodes[0], &chan, None, HTLCType::NONE);
check_closed_broadcast!(nodes[0], true);
}
let starting_block = nodes[1].best_block_info();
- let mut block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: starting_block.0, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ let mut block = create_dummy_block(starting_block.0, 42, Vec::new());
for _ in starting_block.1 + 1..TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + CHAN_CONFIRM_DEPTH + 2 {
connect_block(&nodes[0], &block);
block.header.prev_blockhash = block.block_hash();
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 0)
- .with_features(nodes[1].node.invoice_features());
- let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000, 0);
+ .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000);
route.paths[0].hops.last_mut().unwrap().cltv_expiry_delta = 500000001;
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash,
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
&route.paths[0], 3999999, RecipientOnionFields::secret_only(our_payment_secret), cur_height, &None).unwrap();
- let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
+ let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap();
let mut msg = msgs::UpdateAddHTLC {
channel_id: chan.2,
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
- let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_features(nodes[1].node.invoice_features());
- let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None, 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None, 10_000, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]);
let our_payment_secret = nodes[1].node.create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, None).unwrap();
nodes[0].node.send_payment_with_route(&route, our_payment_hash,
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 30)
- .with_features(nodes[0].node.invoice_features());
- let (route,_, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_params, 3000000, 30);
+ .with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ let (route,_, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_params, 3000000);
send_along_route(&nodes[1], route, &vec!(&nodes[0])[..], 3000000);
let revoked_txn = get_local_commitment_txn!(nodes[0], chan.2);
// Actually revoke tx by claiming a HTLC
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage);
- let header = BlockHeader { version: 0x20000000, prev_blockhash: header_114, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[1], &Block { header, txdata: vec![revoked_txn[0].clone()] });
+ connect_block(&nodes[1], &create_dummy_block(header_114, 42, vec![revoked_txn[0].clone()]));
check_added_monitors!(nodes[1], 1);
// One or more justice tx should have been broadcast, check it
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000);
// Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
- let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_features(nodes[1].node.invoice_features());
+ let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None,
- 3_000_000, 50, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
+ 3_000_000, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
- let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50).with_features(nodes[0].node.invoice_features());
+ let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
let route = get_route(&nodes[1].node.get_our_node_id(), &payment_params, &nodes[1].network_graph.read_only(), None,
- 3_000_000, 50, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
+ 3_000_000, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000);
let revoked_local_txn = get_local_commitment_txn!(nodes[1], chan.2);
// Revoke local commitment tx
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage);
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
// B will generate both revoked HTLC-timeout/HTLC-preimage txn from revoked commitment tx
- connect_block(&nodes[1], &Block { header, txdata: vec![revoked_local_txn[0].clone()] });
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, vec![revoked_local_txn[0].clone()]));
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
// Broadcast set of revoked txn on A
let hash_128 = connect_blocks(&nodes[0], 40);
- let header_11 = BlockHeader { version: 0x20000000, prev_blockhash: hash_128, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header: header_11, txdata: vec![revoked_local_txn[0].clone()] });
- let header_129 = BlockHeader { version: 0x20000000, prev_blockhash: header_11.block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header: header_129, txdata: vec![revoked_htlc_txn[0].clone(), revoked_htlc_txn[1].clone()] });
+ let block_11 = create_dummy_block(hash_128, 42, vec![revoked_local_txn[0].clone()]);
+ connect_block(&nodes[0], &block_11);
+ let block_129 = create_dummy_block(block_11.block_hash(), 42, vec![revoked_htlc_txn[0].clone(), revoked_htlc_txn[1].clone()]);
+ connect_block(&nodes[0], &block_129);
let events = nodes[0].node.get_and_clear_pending_events();
expect_pending_htlcs_forwardable_from_events!(nodes[0], events[0..1], true);
match events.last().unwrap() {
}
// Connect one more block to see if bumped penalty are issued for HTLC txn
- let header_130 = BlockHeader { version: 0x20000000, prev_blockhash: header_129.block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header: header_130, txdata: penalty_txn });
- let header_131 = BlockHeader { version: 0x20000000, prev_blockhash: header_130.block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header: header_131, txdata: Vec::new() });
+ let block_130 = create_dummy_block(block_129.block_hash(), 42, penalty_txn);
+ connect_block(&nodes[0], &block_130);
+ let block_131 = create_dummy_block(block_130.block_hash(), 42, Vec::new());
+ connect_block(&nodes[0], &block_131);
// Few more blocks to confirm penalty txn
connect_blocks(&nodes[0], 4);
txn
};
// Broadcast claim txn and confirm blocks to avoid further bumps on this outputs
- let header_145 = BlockHeader { version: 0x20000000, prev_blockhash: header_144, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header: header_145, txdata: node_txn });
+ connect_block(&nodes[0], &create_dummy_block(header_144, 42, node_txn));
connect_blocks(&nodes[0], 20);
{
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
node_txn.clear();
penalty_txn
};
- let header_130 = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header: header_130, txdata: penalty_txn });
+ connect_block(&nodes[0], &create_dummy_block(nodes[0].best_block_hash(), 42, penalty_txn));
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
{
let monitor = nodes[0].chain_monitor.chain_monitor.get_monitor(OutPoint { txid: chan.3.txid(), index: 0 }).unwrap();
// Edit amt_to_forward to simulate the sender having set
// the final amount and the routing node taking less fee
onion_payloads[1].amt_to_forward = 99_000;
- let new_onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
+ let new_onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap();
payment_event.msgs[0].onion_routing_packet = new_onion_packet;
}
} else { panic!(); }
let mut block = {
let node_1_blocks = nodes[1].blocks.lock().unwrap();
- Block {
- header: BlockHeader {
- version: 0x2000000,
- prev_blockhash: node_1_blocks.last().unwrap().0.block_hash(),
- merkle_root: TxMerkleNode::all_zeros(),
- time: node_1_blocks.len() as u32 + 7200, bits: 42, nonce: 42 },
- txdata: vec![],
- }
+ create_dummy_block(node_1_blocks.last().unwrap().0.block_hash(), node_1_blocks.len() as u32 + 7200, Vec::new())
};
connect_block(&nodes[1], &block);
if let Err(APIError::APIMisuseError { err }) = nodes[1].node.create_inbound_payment_for_hash_legacy(payment_hash, Some(100_000), 2) {
assert_eq!(watchtower.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
watchtower
};
- let header = BlockHeader { version: 0x20000000, prev_blockhash: BlockHash::all_zeros(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- let block = Block { header, txdata: vec![] };
+ let block = create_dummy_block(BlockHash::all_zeros(), 42, Vec::new());
// Make the tx_broadcaster aware of enough blocks that it doesn't think we're violating
// transaction lock time requirements here.
chanmon_cfgs[0].tx_broadcaster.blocks.lock().unwrap().resize(200, (block.clone(), 200));
let mut node_0_per_peer_lock;
let mut node_0_peer_state_lock;
let mut channel = get_channel_ref!(nodes[0], nodes[1], node_0_per_peer_lock, node_0_peer_state_lock, chan_1.2);
- if let Ok(update) = channel.commitment_signed(&updates.commitment_signed, &node_cfgs[0].logger) {
+ if let Ok(Some(update)) = channel.commitment_signed(&updates.commitment_signed, &node_cfgs[0].logger) {
assert_eq!(watchtower.chain_monitor.update_channel(outpoint, &update), ChannelMonitorUpdateStatus::PermanentFailure);
assert_eq!(nodes[0].chain_monitor.update_channel(outpoint, &update), ChannelMonitorUpdateStatus::Completed);
} else { assert!(false); }
assert_eq!(watchtower.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
watchtower
};
- let header = BlockHeader { version: 0x20000000, prev_blockhash: BlockHash::all_zeros(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- let block = Block { header, txdata: vec![] };
+ let block = create_dummy_block(BlockHash::all_zeros(), 42, Vec::new());
// Make Alice aware of enough blocks that it doesn't think we're violating transaction lock time
// requirements here.
const HTLC_TIMEOUT_BROADCAST: u32 = CHAN_CONFIRM_DEPTH + 1 + TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS;
assert_eq!(watchtower.watch_channel(outpoint, new_monitor), ChannelMonitorUpdateStatus::Completed);
watchtower
};
- let header = BlockHeader { version: 0x20000000, prev_blockhash: BlockHash::all_zeros(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- watchtower_bob.chain_monitor.block_connected(&Block { header, txdata: vec![] }, HTLC_TIMEOUT_BROADCAST - 1);
+ watchtower_bob.chain_monitor.block_connected(&create_dummy_block(BlockHash::all_zeros(), 42, Vec::new()), HTLC_TIMEOUT_BROADCAST - 1);
// Route another payment to generate another update with still previous HTLC pending
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 3000000);
let mut node_0_per_peer_lock;
let mut node_0_peer_state_lock;
let mut channel = get_channel_ref!(nodes[0], nodes[1], node_0_per_peer_lock, node_0_peer_state_lock, chan_1.2);
- if let Ok(update) = channel.commitment_signed(&updates.commitment_signed, &node_cfgs[0].logger) {
+ if let Ok(Some(update)) = channel.commitment_signed(&updates.commitment_signed, &node_cfgs[0].logger) {
// Watchtower Alice should already have seen the block and reject the update
assert_eq!(watchtower_alice.chain_monitor.update_channel(outpoint, &update), ChannelMonitorUpdateStatus::PermanentFailure);
assert_eq!(watchtower_bob.chain_monitor.update_channel(outpoint, &update), ChannelMonitorUpdateStatus::Completed);
check_added_monitors!(nodes[0], 1);
//// Provide one more block to watchtower Bob, expect broadcast of commitment and HTLC-Timeout
- let header = BlockHeader { version: 0x20000000, prev_blockhash: BlockHash::all_zeros(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- watchtower_bob.chain_monitor.block_connected(&Block { header, txdata: vec![] }, HTLC_TIMEOUT_BROADCAST);
+ watchtower_bob.chain_monitor.block_connected(&create_dummy_block(BlockHash::all_zeros(), 42, Vec::new()), HTLC_TIMEOUT_BROADCAST);
// Watchtower Bob should have broadcast a commitment/HTLC-timeout
let bob_state_y;
};
// We confirm Bob's state Y on Alice, she should broadcast a HTLC-timeout
- let header = BlockHeader { version: 0x20000000, prev_blockhash: BlockHash::all_zeros(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
let height = HTLC_TIMEOUT_BROADCAST + 1;
connect_blocks(&nodes[0], height - nodes[0].best_block_info().1);
check_closed_broadcast(&nodes[0], 1, true);
check_closed_event(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed, false);
- watchtower_alice.chain_monitor.block_connected(&Block { header, txdata: vec![bob_state_y.clone()] }, height);
+ watchtower_alice.chain_monitor.block_connected(&create_dummy_block(BlockHash::all_zeros(), 42, vec![bob_state_y.clone()]), height);
check_added_monitors(&nodes[0], 1);
{
let htlc_txn = alice_broadcaster.txn_broadcast();
check_spends!(local_txn[0], chan_1.3);
// Timeout HTLC on A's chain and so it can generate a HTLC-Timeout tx
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header, txdata: vec![local_txn[0].clone()] });
+ let block = create_dummy_block(nodes[0].best_block_hash(), 42, vec![local_txn[0].clone()]);
+ connect_block(&nodes[0], &block);
// We deliberately connect the local tx twice as this should provoke a failure calling
// this test before #653 fix.
- chain::Listen::block_connected(&nodes[0].chain_monitor.chain_monitor, &Block { header, txdata: vec![local_txn[0].clone()] }, nodes[0].best_block_info().1 + 1);
+ chain::Listen::block_connected(&nodes[0].chain_monitor.chain_monitor, &block, nodes[0].best_block_info().1 + 1);
check_closed_broadcast!(nodes[0], true);
check_added_monitors!(nodes[0], 1);
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
node_txn[0].clone()
};
- let header_201 = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[0], &Block { header: header_201, txdata: vec![htlc_timeout.clone()] });
+ connect_block(&nodes[0], &create_dummy_block(nodes[0].best_block_hash(), 42, vec![htlc_timeout.clone()]));
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
expect_payment_failed!(nodes[0], our_payment_hash, false);
}
true => alice_txn.clone(),
false => get_local_commitment_txn!(nodes[1], chan_ab.2)
};
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42};
- connect_block(&nodes[1], &Block { header, txdata: vec![txn_to_broadcast[0].clone()]});
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, vec![txn_to_broadcast[0].clone()]));
if broadcast_alice {
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 1);
let mut txn_to_broadcast = alice_txn.clone();
if !broadcast_alice { txn_to_broadcast = get_local_commitment_txn!(nodes[1], chan_ab.2); }
if !go_onchain_before_fulfill {
- let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42};
- connect_block(&nodes[1], &Block { header, txdata: vec![txn_to_broadcast[0].clone()]});
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, vec![txn_to_broadcast[0].clone()]));
// If Bob was the one to force-close, he will have already passed these checks earlier.
if broadcast_alice {
check_closed_broadcast!(nodes[1], true);
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[1].node.invoice_features());
- let route = get_route!(nodes[0], payment_params, 10_000, TEST_FINAL_CLTV).unwrap();
+ .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let route = get_route!(nodes[0], payment_params, 10_000).unwrap();
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(&nodes[1]);
let chan_2_3 =create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 100_000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[3].node.invoice_features());
- let mut route = get_route!(nodes[0], payment_params, 15_000_000, TEST_FINAL_CLTV).unwrap();
+ .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ let mut route = get_route!(nodes[0], payment_params, 15_000_000).unwrap();
assert_eq!(route.paths.len(), 2);
route.paths.sort_by(|path_a, _| {
// Sort the path so that the path through nodes[1] comes first
};
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
- let route = find_route(&payer_pubkey, &route_params, &network_graph, None, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
+ let route = find_route(&payer_pubkey, &route_params, &network_graph, None, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage),
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route = find_route(
&payer_pubkey, &route_params, &network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
- nodes[0].logger, &scorer, &random_seed_bytes
+ nodes[0].logger, &scorer, &(), &random_seed_bytes
).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(recv_value), 7200, Some(min_final_cltv_expiry_delta)).unwrap();
(payment_hash, nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap(), payment_secret)
};
- let route = get_route!(nodes[0], payment_parameters, recv_value, final_cltv_expiry_delta as u32).unwrap();
+ let route = get_route!(nodes[0], payment_parameters, recv_value).unwrap();
nodes[0].node.send_payment_with_route(&route, payment_hash,
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 1);
use bitcoin::hashes::cmp::fixed_time_eq;
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
-use crate::chain::keysinterface::{KeyMaterial, EntropySource};
+use crate::sign::{KeyMaterial, EntropySource};
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use crate::ln::msgs;
use crate::ln::msgs::MAX_VALUE_MSAT;
/// A set of keys that were HKDF-expanded from an initial call to
/// [`NodeSigner::get_inbound_payment_key_material`].
///
-/// [`NodeSigner::get_inbound_payment_key_material`]: crate::chain::keysinterface::NodeSigner::get_inbound_payment_key_material
+/// [`NodeSigner::get_inbound_payment_key_material`]: crate::sign::NodeSigner::get_inbound_payment_key_material
pub struct ExpandedKey {
/// The key used to encrypt the bytes containing the payment metadata (i.e. the amount and
/// expiry, included for payment verification on decryption).
/// Note that if `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
-/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
-/// [`NodeSigner::get_inbound_payment_key_material`]: crate::chain::keysinterface::NodeSigner::get_inbound_payment_key_material
+/// [phantom node payments]: crate::sign::PhantomKeysManager
+/// [`NodeSigner::get_inbound_payment_key_material`]: crate::sign::NodeSigner::get_inbound_payment_key_material
pub fn create<ES: Deref>(keys: &ExpandedKey, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, entropy_source: &ES, current_time: u64,
min_final_cltv_expiry_delta: Option<u16>) -> Result<(PaymentHash, PaymentSecret), ()>
/// Note that if `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
-/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+/// [phantom node payments]: crate::sign::PhantomKeysManager
pub fn create_from_hash(keys: &ExpandedKey, min_value_msat: Option<u64>, payment_hash: PaymentHash,
invoice_expiry_delta_secs: u32, current_time: u64, min_final_cltv_expiry_delta: Option<u16>) -> Result<PaymentSecret, ()> {
let metadata_bytes = construct_metadata_bytes(min_value_msat, if min_final_cltv_expiry_delta.is_some() {
///
/// See [`ExpandedKey`] docs for more info on the individual keys used.
///
-/// [`NodeSigner::get_inbound_payment_key_material`]: crate::chain::keysinterface::NodeSigner::get_inbound_payment_key_material
+/// [`NodeSigner::get_inbound_payment_key_material`]: crate::sign::NodeSigner::get_inbound_payment_key_material
/// [`create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
/// [`create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
pub(super) fn verify<L: Deref>(payment_hash: PaymentHash, payment_data: &msgs::FinalOnionHopData,
/// payment_hash type, use to cross-lock hop
///
/// This is not exported to bindings users as we just use [u8; 32] directly
-#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentHash(pub [u8; 32]);
/// payment_preimage type, use to route payment between hop
///
/// This is not exported to bindings users as we just use [u8; 32] directly
-#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentPreimage(pub [u8; 32]);
/// payment_secret type, use to authenticate sender to the receiver and tie MPP HTLCs together
///
/// This is not exported to bindings users as we just use [u8; 32] directly
-#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct PaymentSecret(pub [u8; 32]);
use crate::prelude::*;
//! Further functional tests which test blockchain reorganizations.
#[cfg(anchors)]
-use crate::chain::keysinterface::{ChannelSigner, EcdsaChannelSigner};
+use crate::sign::{ChannelSigner, EcdsaChannelSigner};
#[cfg(anchors)]
use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, Balance};
if let Event::SpendableOutputs { outputs } = spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
let spend_tx = node.keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
- Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
check_spends!(spend_tx, spendable_tx);
} else { panic!(); }
}
let opt_anchors = get_opt_anchors!(nodes[0], nodes[1], chan_id);
let remote_txn = get_local_commitment_txn!(nodes[1], chan_id);
+ let sent_htlc_balance = Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: 3_000,
+ claimable_height: htlc_cltv_timeout,
+ payment_hash,
+ };
+ let sent_htlc_timeout_balance = Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: 4_000,
+ claimable_height: htlc_cltv_timeout,
+ payment_hash: timeout_payment_hash,
+ };
+ let received_htlc_balance = Balance::MaybePreimageClaimableHTLC {
+ claimable_amount_satoshis: 3_000,
+ expiry_height: htlc_cltv_timeout,
+ payment_hash,
+ };
+ let received_htlc_timeout_balance = Balance::MaybePreimageClaimableHTLC {
+ claimable_amount_satoshis: 4_000,
+ expiry_height: htlc_cltv_timeout,
+ payment_hash: timeout_payment_hash,
+ };
+ let received_htlc_claiming_balance = Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 3_000,
+ timeout_height: htlc_cltv_timeout,
+ payment_hash,
+ payment_preimage,
+ };
+ let received_htlc_timeout_claiming_balance = Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 4_000,
+ timeout_height: htlc_cltv_timeout,
+ payment_hash: timeout_payment_hash,
+ payment_preimage: timeout_payment_preimage,
+ };
+
// Before B receives the payment preimage, it only suggests the push_msat value of 1_000 sats
// as claimable. A lists both its to-self balance and the (possibly-claimable) HTLCs.
assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
claimable_amount_satoshis: 1_000_000 - 3_000 - 4_000 - 1_000 - 3 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 3_000,
- claimable_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 4_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, sent_htlc_balance.clone(), sent_htlc_timeout_balance.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
claimable_amount_satoshis: 1_000,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 3_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 4_000,
- expiry_height: htlc_cltv_timeout,
- }]),
+ }, received_htlc_balance.clone(), received_htlc_timeout_balance.clone()]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
nodes[1].node.claim_funds(payment_preimage);
chan_feerate * (channel::commitment_tx_base_weight(opt_anchors) +
if prev_commitment_tx { 1 } else { 2 } *
channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 4_000,
- claimable_height: htlc_cltv_timeout,
- }];
+ }, sent_htlc_timeout_balance.clone()];
if !prev_commitment_tx {
- a_expected_balances.push(Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 3_000,
- claimable_height: htlc_cltv_timeout,
- });
+ a_expected_balances.push(sent_htlc_balance.clone());
}
assert_eq!(sorted_vec(a_expected_balances),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
claimable_amount_satoshis: 1_000_000 - 3_000 - 4_000 - 1_000 - 3 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 3_000,
- claimable_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 4_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, sent_htlc_balance.clone(), sent_htlc_timeout_balance.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// The main non-HTLC balance is just awaiting confirmations, but the claimable height is the
// CSV delay, not ANTI_REORG_DELAY.
},
// Both HTLC balances are "contentious" as our counterparty could claim them if we wait too
// long.
- Balance::ContentiousClaimable {
- claimable_amount_satoshis: 3_000,
- timeout_height: htlc_cltv_timeout,
- }, Balance::ContentiousClaimable {
- claimable_amount_satoshis: 4_000,
- timeout_height: htlc_cltv_timeout,
- }]),
+ received_htlc_claiming_balance.clone(), received_htlc_timeout_claiming_balance.clone()]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
// After ANTI_REORG_DELAY, A will consider its balance fully spendable and generate a
// `SpendableOutputs` event. However, B still has to wait for the CSV delay.
- assert_eq!(sorted_vec(vec![Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 3_000,
- claimable_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 4_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ assert_eq!(sorted_vec(vec![sent_htlc_balance.clone(), sent_htlc_timeout_balance.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 1_000,
confirmation_height: node_b_commitment_claimable,
- }, Balance::ContentiousClaimable {
- claimable_amount_satoshis: 3_000,
- timeout_height: htlc_cltv_timeout,
- }, Balance::ContentiousClaimable {
- claimable_amount_satoshis: 4_000,
- timeout_height: htlc_cltv_timeout,
- }]),
+ }, received_htlc_claiming_balance.clone(), received_htlc_timeout_claiming_balance.clone()]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
test_spendable_output(&nodes[0], &remote_txn[0]);
} else {
expect_payment_sent!(nodes[0], payment_preimage);
}
- assert_eq!(sorted_vec(vec![Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 3_000,
- claimable_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 4_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ assert_eq!(sorted_vec(vec![sent_htlc_balance.clone(), sent_htlc_timeout_balance.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
- assert_eq!(vec![Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 4_000,
- claimable_height: htlc_cltv_timeout,
- }],
+ assert_eq!(vec![sent_htlc_timeout_balance.clone()],
nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
// When the HTLC timeout output is spendable in the next block, A should broadcast it
}, Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 3_000,
confirmation_height: node_b_htlc_claimable,
- }, Balance::ContentiousClaimable {
- claimable_amount_satoshis: 4_000,
- timeout_height: htlc_cltv_timeout,
- }]),
+ }, received_htlc_timeout_claiming_balance.clone()]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// After reaching the commitment output CSV, we'll get a SpendableOutputs event for it and have
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 3_000,
confirmation_height: node_b_htlc_claimable,
- }, Balance::ContentiousClaimable {
- claimable_amount_satoshis: 4_000,
- timeout_height: htlc_cltv_timeout,
- }]),
+ }, received_htlc_timeout_claiming_balance.clone()]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// After reaching the claimed HTLC output CSV, we'll get a SpendableOutptus event for it and
connect_blocks(&nodes[1], node_b_htlc_claimable - nodes[1].best_block_info().1);
test_spendable_output(&nodes[1], &b_broadcast_txn[0]);
- assert_eq!(vec![Balance::ContentiousClaimable {
- claimable_amount_satoshis: 4_000,
- timeout_height: htlc_cltv_timeout,
- }],
+ assert_eq!(vec![received_htlc_timeout_claiming_balance.clone()],
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
// Finally, mine the HTLC timeout transaction that A broadcasted (even though B should be able
// to claim this HTLC with the preimage it knows!). It will remain listed as a claimable HTLC
// until ANTI_REORG_DELAY confirmations on the spend.
mine_transaction(&nodes[1], &a_broadcast_txn[1]);
- assert_eq!(vec![Balance::ContentiousClaimable {
- claimable_amount_satoshis: 4_000,
- timeout_height: htlc_cltv_timeout,
- }],
+ assert_eq!(vec![received_htlc_timeout_claiming_balance.clone()],
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
assert_eq!(Vec::<Balance>::new(),
check_closed_broadcast!(nodes[0], true);
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
+ let htlc_balance_known_preimage = Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: 10_000,
+ claimable_height: htlc_cltv_timeout,
+ payment_hash,
+ };
+ let htlc_balance_unknown_preimage = Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: 20_000,
+ claimable_height: htlc_cltv_timeout,
+ payment_hash: payment_hash_2,
+ };
+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 1_000_000 - 10_000 - 20_000 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
confirmation_height: node_a_commitment_claimable,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- claimable_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, htlc_balance_known_preimage.clone(), htlc_balance_unknown_preimage.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// Get nodes[1]'s HTLC claim tx for the second HTLC
claimable_amount_satoshis: 1_000_000 - 10_000 - 20_000 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
confirmation_height: node_a_commitment_claimable,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- claimable_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, htlc_balance_known_preimage.clone(), htlc_balance_unknown_preimage.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
assert_eq!(as_txn[1].lock_time.0, nodes[0].best_block_info().1 + 1); // as_txn[1] can be included in the next block
}, Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 10_000,
confirmation_height: node_a_htlc_claimable,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, htlc_balance_unknown_preimage.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// Now confirm nodes[1]'s HTLC claim, giving nodes[0] the preimage. Note that the "maybe
}, Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 10_000,
confirmation_height: node_a_htlc_claimable,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, htlc_balance_unknown_preimage.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// Finally make the HTLC transactions have ANTI_REORG_DELAY blocks. This call previously
let chan_feerate = get_feerate!(nodes[0], nodes[1], chan_id) as u64;
let opt_anchors = get_opt_anchors!(nodes[0], nodes[1], chan_id);
+ let a_sent_htlc_balance = Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: 10_000,
+ claimable_height: htlc_cltv_timeout,
+ payment_hash: to_b_failed_payment_hash,
+ };
+ let a_received_htlc_balance = Balance::MaybePreimageClaimableHTLC {
+ claimable_amount_satoshis: 20_000,
+ expiry_height: htlc_cltv_timeout,
+ payment_hash: to_a_failed_payment_hash,
+ };
+ let b_received_htlc_balance = Balance::MaybePreimageClaimableHTLC {
+ claimable_amount_satoshis: 10_000,
+ expiry_height: htlc_cltv_timeout,
+ payment_hash: to_b_failed_payment_hash,
+ };
+ let b_sent_htlc_balance = Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: 20_000,
+ claimable_height: htlc_cltv_timeout,
+ payment_hash: to_a_failed_payment_hash,
+ };
+
// Both A and B will have an HTLC that's claimable on timeout and one that's claimable if they
// receive the preimage. These will remain the same through the channel closure and until the
// HTLC output is spent.
assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, a_received_htlc_balance.clone(), a_sent_htlc_balance.clone()]),
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
claimable_amount_satoshis: 500_000 - 20_000,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- claimable_height: htlc_cltv_timeout,
- }]),
+ }, b_received_htlc_balance.clone(), b_sent_htlc_balance.clone()]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// Get nodes[0]'s commitment transaction and HTLC-Timeout transaction
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
confirmation_height: node_a_commitment_claimable,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- claimable_height: htlc_cltv_timeout,
- }]);
+ }, a_received_htlc_balance.clone(), a_sent_htlc_balance.clone()]);
mine_transaction(&nodes[0], &as_txn[0]);
nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
let mut bs_pre_spend_claims = sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 500_000 - 20_000,
confirmation_height: node_b_commitment_claimable,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- claimable_height: htlc_cltv_timeout,
- }]);
+ }, b_received_htlc_balance.clone(), b_sent_htlc_balance.clone()]);
assert_eq!(bs_pre_spend_claims,
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
confirmation_height: node_a_commitment_claimable,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::ClaimableAwaitingConfirmations {
+ }, a_received_htlc_balance.clone(), Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 10_000,
confirmation_height: as_timeout_claimable_height,
}]),
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
confirmation_height: node_a_commitment_claimable,
- }, Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 20_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::ClaimableAwaitingConfirmations {
+ }, a_received_htlc_balance.clone(), Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 10_000,
confirmation_height: as_timeout_claimable_height,
}]),
// was already claimed.
mine_transaction(&nodes[1], &bs_htlc_timeout_claim[0]);
let bs_timeout_claimable_height = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
- assert_eq!(sorted_vec(vec![Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::ClaimableAwaitingConfirmations {
+ assert_eq!(sorted_vec(vec![b_received_htlc_balance.clone(), Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 20_000,
confirmation_height: bs_timeout_claimable_height,
}]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
mine_transaction(&nodes[1], &as_htlc_timeout_claim[0]);
- assert_eq!(sorted_vec(vec![Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- expiry_height: htlc_cltv_timeout,
- }, Balance::ClaimableAwaitingConfirmations {
+ assert_eq!(sorted_vec(vec![b_received_htlc_balance.clone(), Balance::ClaimableAwaitingConfirmations {
claimable_amount_satoshis: 20_000,
confirmation_height: bs_timeout_claimable_height,
}]),
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 2);
expect_payment_failed!(nodes[1], to_a_failed_payment_hash, false);
- assert_eq!(vec![Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: 10_000,
- expiry_height: htlc_cltv_timeout,
- }],
+ assert_eq!(vec![b_received_htlc_balance.clone()],
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
test_spendable_output(&nodes[1], &bs_htlc_timeout_claim[0]);
}, Balance::MaybeTimeoutClaimableHTLC {
claimable_amount_satoshis: 2_000,
claimable_height: missing_htlc_cltv_timeout,
+ payment_hash: missing_htlc_payment_hash,
}, Balance::MaybeTimeoutClaimableHTLC {
claimable_amount_satoshis: 4_000,
claimable_height: htlc_cltv_timeout,
+ payment_hash: timeout_payment_hash,
}, Balance::MaybeTimeoutClaimableHTLC {
claimable_amount_satoshis: 5_000,
claimable_height: live_htlc_cltv_timeout,
+ payment_hash: live_payment_hash,
}]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
}, Balance::MaybeTimeoutClaimableHTLC {
claimable_amount_satoshis: 4_000,
claimable_height: htlc_cltv_timeout,
+ payment_hash: revoked_payment_hash,
}, Balance::MaybeTimeoutClaimableHTLC {
claimable_amount_satoshis: 3_000,
claimable_height: htlc_cltv_timeout,
+ payment_hash: claimed_payment_hash,
}]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
// old `ChannelMonitor` that did not exercise said rebroadcasting logic.
if check_old_monitor_retries_after_upgrade {
let serialized_monitor = hex::decode(
- "0101fffffffffffffffff9550f22c95100160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665f00002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6302d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e2035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea0016001467822698d782e8421ebdf96d010de99382b7ec2300160014caf6d80fe2bab80473b021f57588a9c384bf23170000000000000000000000004d49e5da0000000000000000000000000000002a0270b20ad0f2c2bb30a55590fc77778495bc1b38c96476901145dda57491237f0f74c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000022002034c0cc0ad0dd5fe61dcf7ef58f995e3d34f8dbd24aa2a6fae68fefe102bf025c21391732ce658e1fe167300bb689a81e7db5399b9ee4095e217b0e997e8dd3d17a0000000000000000004a002103adde8029d3ee281a32e9db929b39f503ff9d7e93cd308eb157955344dc6def84022103205087e2dc1f6b9937e887dfa712c5bdfa950b01dbda3ebac4c85efdde48ee6a04020090004752210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db32103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b52ae00000000000186a0ffffffffffff0291e7c0a3232fb8650a6b4089568a81062b48a768780e5a74bb4a4a74e33aec2c029d5760248ec86c4a76d9df8308555785a06a65472fb995f5b392d520bbd000650090c1c94b11625690c9d84c5daa67b6ad19fcc7f9f23e194384140b08fcab9e8e810000ffffffffffff000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000002391732ce658e1fe167300bb689a81e7db5399b9ee4095e217b0e997e8dd3d17a00000000000000010000000000009896800000005166687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f292505000000009c009900202d704fbfe342a9ff6eaca14d80a24aaed0e680bbbdd36157b6f2798c61d906910120f9fe5e552aa0fc45020f0505efde432a4e373e5d393863973a6899f8c26d33d10208000000000098968004494800210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c23020500030241000408000001000000000006020000080800000000009896800a0400000046167c86cc0e598a6b541f7c9bf9ef17222e4a76f636e2d22185aeadd2b02d029c00000000000000000000000000000000000000000000000166687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925fffffffffffe01e3002004f8eda5676356f539169a8e9a1e86c7f125283328d6f4bded1b939b52a6a7e30108000000000000c299022103a1f98e85886df54add6908b4fc1ff515e44aedefe9eb9c02879c89994298fa79042103a650bf03971df0176c7b412247390ef717853e8bd487b204dccc2fe2078bb75206210390bbbcebe9f70ba5dfd98866a79f72f75e0a6ea550ef73b202dd87cd6477350a08210284152d57908488e666e872716a286eb670b3d06cbeebf3f2e4ad350e01ec5e5b0a2102295e2de39eb3dcc2882f8cc266df7882a8b6d2c32aa08799f49b693aad3be28e0c04000000fd0e00fd01fe002045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d01080000000000009b5e0221035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea04210230fde9c031f487db95ff55b7c0acbe0c7c26a8d82615e9184416bd350101616706210225afb4e88eac8b47b67adeaf085f5eb5d37d936f56138f0848de3d104edf113208210208e4687a95c172b86b920c3bc5dbd5f023094ec2cb0abdb74f9b624f45740df90a2102d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e20c04000000fd0efd01193b00010102080000000000989680040400000051062066687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925080400000000417e2650c201383711eed2a7cb8652c3e77ee6a395e81849c5c222217ed68b333c0ca9f1e900662ae68a7359efa7ef9d90613f2a62f7c3ff90f8c25e2cc974c9d39c009900202d704fbfe342a9ff6eaca14d80a24aaed0e680bbbdd36157b6f2798c61d906910120f9fe5e552aa0fc45020f0505efde432a4e373e5d393863973a6899f8c26d33d10208000000000098968004494800210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c23020500030241000408000001000000000006020000080800000000009896800a0400000046fffffffffffefffffffffffe000000000000000000000000000000000000000000000000ffe099e83ae3761c7f1b781d22613bd1f6977e9ad59fae12b3eba34462ee8a3d000000500000000000000002fd01da002045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d01fd01840200000000010174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800310270000000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d53495e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6350c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d304004730440220671c9badf26bd3a1ebd2d17020c6be20587d7822530daacc52c28839875eaec602204b575a21729ed27311f6d79fdf6fe8702b0a798f7d842e39ede1b56f249a613401473044022016a0da36f70cbf5d889586af88f238982889dc161462c56557125c7acfcb69e9022036ae10c6cc8cbc3b27d9e9ef6babb556086585bc819f252208bd175286699fdd014752210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db32103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b52ae50c9222002040000000b03209452ca8c90d4c78928b80ec41398f2a890324d8ad6e6c81408a0cb9b8d977b070406030400020090fd02a1002045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d01fd01840200000000010174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800310270000000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d53495e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6350c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d304004730440220671c9badf26bd3a1ebd2d17020c6be20587d7822530daacc52c28839875eaec602204b575a21729ed27311f6d79fdf6fe8702b0a798f7d842e39ede1b56f249a613401473044022016a0da36f70cbf5d889586af88f238982889dc161462c56557125c7acfcb69e9022036ae10c6cc8cbc3b27d9e9ef6babb556086585bc819f252208bd175286699fdd014752210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db32103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b52ae50c9222002040000000b03209452ca8c90d4c78928b80ec41398f2a890324d8ad6e6c81408a0cb9b8d977b0704cd01cb00c901c7002245cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d0001022102d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e204020090062b5e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c630821035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea0a200000000000000000000000004d49e5da0000000000000000000000000000002a0c0800000000000186a0000000000000000274c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e0000000000000001000000000022002034c0cc0ad0dd5fe61dcf7ef58f995e3d34f8dbd24aa2a6fae68fefe102bf025c45cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d000000000000000100000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d5349010100160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665ffd027100fd01e6fd01e300080000fffffffffffe02080000000000009b5e0408000000000000c3500604000000fd08b0af002102d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e20221035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea04210230fde9c031f487db95ff55b7c0acbe0c7c26a8d82615e9184416bd350101616706210225afb4e88eac8b47b67adeaf085f5eb5d37d936f56138f0848de3d104edf113208210208e4687a95c172b86b920c3bc5dbd5f023094ec2cb0abdb74f9b624f45740df90acdcc00a8020000000174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800310270000000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d53495e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6350c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d350c92220022045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d0c3c3b00010102080000000000989680040400000051062066687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f29250804000000000240671c9badf26bd3a1ebd2d17020c6be20587d7822530daacc52c28839875eaec64b575a21729ed27311f6d79fdf6fe8702b0a798f7d842e39ede1b56f249a613404010006407e2650c201383711eed2a7cb8652c3e77ee6a395e81849c5c222217ed68b333c0ca9f1e900662ae68a7359efa7ef9d90613f2a62f7c3ff90f8c25e2cc974c9d3010000000000000001010000000000000000090b2a953d93a124c600ecb1a0ccfed420169cdd37f538ad94a3e4e6318c93c14adf59cdfbb40bdd40950c9f8dd547d29d75a173e1376a7850743394c46dea2dfd01cefd01ca00fd017ffd017c00080000ffffffffffff0208000000000000c2990408000000000000c3500604000000fd08b0af002102295e2de39eb3dcc2882f8cc266df7882a8b6d2c32aa08799f49b693aad3be28e022103a1f98e85886df54add6908b4fc1ff515e44aedefe9eb9c02879c89994298fa79042103a650bf03971df0176c7b412247390ef717853e8bd487b204dccc2fe2078bb75206210390bbbcebe9f70ba5dfd98866a79f72f75e0a6ea550ef73b202dd87cd6477350a08210284152d57908488e666e872716a286eb670b3d06cbeebf3f2e4ad350e01ec5e5b0aa2a1007d020000000174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800299c2000000000000220020740e108cfbc93967b6ab242a351ebee7de51814cf78d366adefd78b10281f17e50c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d351c92220022004f8eda5676356f539169a8e9a1e86c7f125283328d6f4bded1b939b52a6a7e30c00024045cb2485594bb1ec08e7bb6af4f89c912bd53f006d7876ea956773e04a4aad4a40e2b8d4fc612102f0b54061b3c1239fb78783053e8e6f9d92b1b99f81ae9ec2040100060000fd019600b0af002103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b02210270b20ad0f2c2bb30a55590fc77778495bc1b38c96476901145dda57491237f0f042103b4e59df102747edc3a3e2283b42b88a8c8218ffd0dcfb52f2524b371d64cadaa062103d902b7b8b3434076d2b210e912c76645048b71e28995aad227a465a65ccd817608210301e9a52f923c157941de4a7692e601f758660969dcf5abdb67817efe84cce2ef0202009004010106b7b600b0af00210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db30221034d0f817cb19b4a3bd144b615459bd06cbab3b4bdc96d73e18549a992cee80e8104210380542b59a9679890cba529fe155a9508ef57dac7416d035b23666e3fb98c3814062103adde8029d3ee281a32e9db929b39f503ff9d7e93cd308eb157955344dc6def84082103205087e2dc1f6b9937e887dfa712c5bdfa950b01dbda3ebac4c85efdde48ee6a02020090082274c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e000000000287010108d30df34e3a1e00ecdd03a2c843db062479a81752c4dfd0cc4baef0f81e7bc7ef8820990daf8d8e8d30a3b4b08af12c9f5cd71e45c7238103e0c80ca13850862e4fd2c56b69b7195312518de1bfe9aed63c80bb7760d70b2a870d542d815895fd12423d11e2adb0cdf55d776dac8f487c9b3b7ea12f1b150eb15889cf41333ade465692bf1cdc360b9c2a19bf8c1ca4fed7639d8bc953d36c10d8c6c9a8c0a57608788979bcf145e61b308006896e21d03e92084f93bd78740c20639134a7a8fd019afd019600b0af002103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b02210270b20ad0f2c2bb30a55590fc77778495bc1b38c96476901145dda57491237f0f042103b4e59df102747edc3a3e2283b42b88a8c8218ffd0dcfb52f2524b371d64cadaa062103d902b7b8b3434076d2b210e912c76645048b71e28995aad227a465a65ccd817608210301e9a52f923c157941de4a7692e601f758660969dcf5abdb67817efe84cce2ef0202009004010106b7b600b0af00210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db30221034d0f817cb19b4a3bd144b615459bd06cbab3b4bdc96d73e18549a992cee80e8104210380542b59a9679890cba529fe155a9508ef57dac7416d035b23666e3fb98c3814062103adde8029d3ee281a32e9db929b39f503ff9d7e93cd308eb157955344dc6def84082103205087e2dc1f6b9937e887dfa712c5bdfa950b01dbda3ebac4c85efdde48ee6a02020090082274c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e000000000000000186a00000000000000000000000004d49e5da0000000000000000000000000000002a000000000000000001b77b61346a2a408afdb01743a2230cb36e55771a0790f67a0910e207fd223fc8000000000000000145cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d00000000041000080000000000989680020400000051160004000000510208000000000000000004040000000b000000000000000145cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d00000000b77b61346a2a408afdb01743a2230cb36e55771a0790f67a0910e207fd223fc80000005000000000000000000000000000000000000101300300050007010109210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c230d000f020000",
+ "0101fffffffffffffffff9550f22c95100160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665f00002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6302d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e2035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea0016001467822698d782e8421ebdf96d010de99382b7ec2300160014caf6d80fe2bab80473b021f57588a9c384bf23170000000000000000000000004d49e5da0000000000000000000000000000002a0270b20ad0f2c2bb30a55590fc77778495bc1b38c96476901145dda57491237f0f74c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000022002034c0cc0ad0dd5fe61dcf7ef58f995e3d34f8dbd24aa2a6fae68fefe102bf025c21391732ce658e1fe167300bb689a81e7db5399b9ee4095e217b0e997e8dd3d17a0000000000000000004a002103adde8029d3ee281a32e9db929b39f503ff9d7e93cd308eb157955344dc6def84022103205087e2dc1f6b9937e887dfa712c5bdfa950b01dbda3ebac4c85efdde48ee6a04020090004752210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db32103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b52ae00000000000186a0ffffffffffff0291e7c0a3232fb8650a6b4089568a81062b48a768780e5a74bb4a4a74e33aec2c029d5760248ec86c4a76d9df8308555785a06a65472fb995f5b392d520bbd000650090c1c94b11625690c9d84c5daa67b6ad19fcc7f9f23e194384140b08fcab9e8e810000ffffffffffff000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000002167c86cc0e598a6b541f7c9bf9ef17222e4a76f636e2d22185aeadd2b02d029c0000000000000000391732ce658e1fe167300bb689a81e7db5399b9ee4095e217b0e997e8dd3d17a00000000000000010000000000009896800000005166687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f29250500000000a0009d00202d704fbfe342a9ff6eaca14d80a24aaed0e680bbbdd36157b6f2798c61d906910120f9fe5e552aa0fc45020f0505efde432a4e373e5d393863973a6899f8c26d33d102080000000000989680044d4c00210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c2302090007000000000241000408000001000000000006020000080800000000009896800a04000000460000000000000000000000000000000166687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925fffffffffffe01e3002004f8eda5676356f539169a8e9a1e86c7f125283328d6f4bded1b939b52a6a7e30108000000000000c299022103a1f98e85886df54add6908b4fc1ff515e44aedefe9eb9c02879c89994298fa79042103a650bf03971df0176c7b412247390ef717853e8bd487b204dccc2fe2078bb75206210390bbbcebe9f70ba5dfd98866a79f72f75e0a6ea550ef73b202dd87cd6477350a08210284152d57908488e666e872716a286eb670b3d06cbeebf3f2e4ad350e01ec5e5b0a2102295e2de39eb3dcc2882f8cc266df7882a8b6d2c32aa08799f49b693aad3be28e0c04000000fd0e00fd0202002045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d01080000000000009b5e0221035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea04210230fde9c031f487db95ff55b7c0acbe0c7c26a8d82615e9184416bd350101616706210225afb4e88eac8b47b67adeaf085f5eb5d37d936f56138f0848de3d104edf113208210208e4687a95c172b86b920c3bc5dbd5f023094ec2cb0abdb74f9b624f45740df90a2102d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e20c04000000fd0efd011d3b00010102080000000000989680040400000051062066687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925080400000000417e2650c201383711eed2a7cb8652c3e77ee6a395e81849c5c222217ed68b333c0ca9f1e900662ae68a7359efa7ef9d90613f2a62f7c3ff90f8c25e2cc974c9d3a0009d00202d704fbfe342a9ff6eaca14d80a24aaed0e680bbbdd36157b6f2798c61d906910120f9fe5e552aa0fc45020f0505efde432a4e373e5d393863973a6899f8c26d33d102080000000000989680044d4c00210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c2302090007000000000241000408000001000000000006020000080800000000009896800a0400000046fffffffffffefffffffffffe000000000000000000000000000000000000000000000000f1600ef6ea657b8d411d553516ae35cedfe86b0cd48d1f91b32772facbae757d0000000b0000000000000002fd01da002045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d01fd01840200000000010174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800310270000000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d53495e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6350c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d304004730440220671c9badf26bd3a1ebd2d17020c6be20587d7822530daacc52c28839875eaec602204b575a21729ed27311f6d79fdf6fe8702b0a798f7d842e39ede1b56f249a613401473044022016a0da36f70cbf5d889586af88f238982889dc161462c56557125c7acfcb69e9022036ae10c6cc8cbc3b27d9e9ef6babb556086585bc819f252208bd175286699fdd014752210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db32103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b52ae50c9222002040000000b0320f1600ef6ea657b8d411d553516ae35cedfe86b0cd48d1f91b32772facbae757d0406030400020090fd02a1002045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d01fd01840200000000010174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800310270000000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d53495e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6350c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d304004730440220671c9badf26bd3a1ebd2d17020c6be20587d7822530daacc52c28839875eaec602204b575a21729ed27311f6d79fdf6fe8702b0a798f7d842e39ede1b56f249a613401473044022016a0da36f70cbf5d889586af88f238982889dc161462c56557125c7acfcb69e9022036ae10c6cc8cbc3b27d9e9ef6babb556086585bc819f252208bd175286699fdd014752210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db32103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b52ae50c9222002040000000b0320f1600ef6ea657b8d411d553516ae35cedfe86b0cd48d1f91b32772facbae757d04cd01cb00c901c7002245cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d0001022102d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e204020090062b5e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c630821035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea0a200000000000000000000000004d49e5da0000000000000000000000000000002a0c0800000000000186a0000000000000000274c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e0000000000000001000000000022002034c0cc0ad0dd5fe61dcf7ef58f995e3d34f8dbd24aa2a6fae68fefe102bf025c45cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d000000000000000100000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d5349010100160014d5a9aa98b89acc215fc3d23d6fec0ad59ca3665ffd027100fd01e6fd01e300080000fffffffffffe02080000000000009b5e0408000000000000c3500604000000fd08b0af002102d7dde8e10a5a22c9bd0d7ef5494d85683ac050253b917615d4f97af633f0a8e20221035f5e9d58b4328566223c107d86cf853e6b9fae1d26ff6d969be0178d1423c4ea04210230fde9c031f487db95ff55b7c0acbe0c7c26a8d82615e9184416bd350101616706210225afb4e88eac8b47b67adeaf085f5eb5d37d936f56138f0848de3d104edf113208210208e4687a95c172b86b920c3bc5dbd5f023094ec2cb0abdb74f9b624f45740df90acdcc00a8020000000174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800310270000000000002200208309b406e3b96e76cde414fbb8f5159f5b25b24075656c6382cec797854d53495e9b0000000000002200204c5f18e5e95b184f34d02ba6de8a2a4e36ae3d4ec87299ad81f3284dc7195c6350c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d350c92220022045cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d0c3c3b00010102080000000000989680040400000051062066687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f29250804000000000240671c9badf26bd3a1ebd2d17020c6be20587d7822530daacc52c28839875eaec64b575a21729ed27311f6d79fdf6fe8702b0a798f7d842e39ede1b56f249a613404010006407e2650c201383711eed2a7cb8652c3e77ee6a395e81849c5c222217ed68b333c0ca9f1e900662ae68a7359efa7ef9d90613f2a62f7c3ff90f8c25e2cc974c9d3010000000000000001010000000000000000090b2a953d93a124c600ecb1a0ccfed420169cdd37f538ad94a3e4e6318c93c14adf59cdfbb40bdd40950c9f8dd547d29d75a173e1376a7850743394c46dea2dfd01cefd01ca00fd017ffd017c00080000ffffffffffff0208000000000000c2990408000000000000c3500604000000fd08b0af002102295e2de39eb3dcc2882f8cc266df7882a8b6d2c32aa08799f49b693aad3be28e022103a1f98e85886df54add6908b4fc1ff515e44aedefe9eb9c02879c89994298fa79042103a650bf03971df0176c7b412247390ef717853e8bd487b204dccc2fe2078bb75206210390bbbcebe9f70ba5dfd98866a79f72f75e0a6ea550ef73b202dd87cd6477350a08210284152d57908488e666e872716a286eb670b3d06cbeebf3f2e4ad350e01ec5e5b0aa2a1007d020000000174c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e00000000000f55f9800299c2000000000000220020740e108cfbc93967b6ab242a351ebee7de51814cf78d366adefd78b10281f17e50c300000000000016001425df8ec4a074f80579fed67d4707d5ec8ed7e8d351c92220022004f8eda5676356f539169a8e9a1e86c7f125283328d6f4bded1b939b52a6a7e30c00024045cb2485594bb1ec08e7bb6af4f89c912bd53f006d7876ea956773e04a4aad4a40e2b8d4fc612102f0b54061b3c1239fb78783053e8e6f9d92b1b99f81ae9ec2040100060000fd019600b0af002103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b02210270b20ad0f2c2bb30a55590fc77778495bc1b38c96476901145dda57491237f0f042103b4e59df102747edc3a3e2283b42b88a8c8218ffd0dcfb52f2524b371d64cadaa062103d902b7b8b3434076d2b210e912c76645048b71e28995aad227a465a65ccd817608210301e9a52f923c157941de4a7692e601f758660969dcf5abdb67817efe84cce2ef0202009004010106b7b600b0af00210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db30221034d0f817cb19b4a3bd144b615459bd06cbab3b4bdc96d73e18549a992cee80e8104210380542b59a9679890cba529fe155a9508ef57dac7416d035b23666e3fb98c3814062103adde8029d3ee281a32e9db929b39f503ff9d7e93cd308eb157955344dc6def84082103205087e2dc1f6b9937e887dfa712c5bdfa950b01dbda3ebac4c85efdde48ee6a02020090082274c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e000000000287010108d30df34e3a1e00ecdd03a2c843db062479a81752c4dfd0cc4baef0f81e7bc7ef8820990daf8d8e8d30a3b4b08af12c9f5cd71e45c7238103e0c80ca13850862e4fd2c56b69b7195312518de1bfe9aed63c80bb7760d70b2a870d542d815895fd12423d11e2adb0cdf55d776dac8f487c9b3b7ea12f1b150eb15889cf41333ade465692bf1cdc360b9c2a19bf8c1ca4fed7639d8bc953d36c10d8c6c9a8c0a57608788979bcf145e61b308006896e21d03e92084f93bd78740c20639134a7a8fd019afd019600b0af002103c21e841cbc0b48197d060c71e116c185fa0ac281b7d0aa5924f535154437ca3b02210270b20ad0f2c2bb30a55590fc77778495bc1b38c96476901145dda57491237f0f042103b4e59df102747edc3a3e2283b42b88a8c8218ffd0dcfb52f2524b371d64cadaa062103d902b7b8b3434076d2b210e912c76645048b71e28995aad227a465a65ccd817608210301e9a52f923c157941de4a7692e601f758660969dcf5abdb67817efe84cce2ef0202009004010106b7b600b0af00210307a78def56cba9fc4db22a25928181de538ee59ba1a475ae113af7790acd0db30221034d0f817cb19b4a3bd144b615459bd06cbab3b4bdc96d73e18549a992cee80e8104210380542b59a9679890cba529fe155a9508ef57dac7416d035b23666e3fb98c3814062103adde8029d3ee281a32e9db929b39f503ff9d7e93cd308eb157955344dc6def84082103205087e2dc1f6b9937e887dfa712c5bdfa950b01dbda3ebac4c85efdde48ee6a02020090082274c52ab4f11296d62b66a6dba9513b04a3e7fb5a09a30cee22fce7294ab55b7e000000000000000186a00000000000000000000000004d49e5da0000000000000000000000000000002a00000000000000000000000000000000000000000000000001000000510000000000000001000000000000000145cfd42d0989e55b953f516ac7fd152bd90ec4438a2fc636f97ddd32a0c8fe0d00000000041000080000000000989680020400000051160004000000510208000000000000000004040000000b0000000000000000000101300300050007010109210355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c230d000f020000",
).unwrap();
reload_node!(nodes[0], &nodes[0].node.encode(), &[&serialized_monitor], persister, new_chain_monitor, node_deserialized);
}
// revoked outputs.
{
let txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
- assert_eq!(txn.len(), 2);
+ assert_eq!(txn.len(), 4);
- let (revoked_claim_a, revoked_claim_b) = if txn[0].input[0].previous_output.txid == revoked_commitment_a.txid() {
- (&txn[0], &txn[1])
+ let (revoked_htlc_claim_a, revoked_htlc_claim_b) = if txn[0].input[0].previous_output.txid == revoked_commitment_a.txid() {
+ (if txn[0].input.len() == 2 { &txn[0] } else { &txn[1] }, if txn[2].input.len() == 2 { &txn[2] } else { &txn[3] })
} else {
- (&txn[1], &txn[0])
+ (if txn[2].input.len() == 2 { &txn[2] } else { &txn[3] }, if txn[0].input.len() == 2 { &txn[0] } else { &txn[1] })
};
- // TODO: to_self claim must be separate from HTLC claims
- assert_eq!(revoked_claim_a.input.len(), 3); // Spends both HTLC outputs and to_self output
- assert_eq!(revoked_claim_a.output.len(), 1);
- check_spends!(revoked_claim_a, revoked_commitment_a);
- assert_eq!(revoked_claim_b.input.len(), 3); // Spends both HTLC outputs and to_self output
- assert_eq!(revoked_claim_b.output.len(), 1);
- check_spends!(revoked_claim_b, revoked_commitment_b);
+ assert_eq!(revoked_htlc_claim_a.input.len(), 2); // Spends both HTLC outputs
+ assert_eq!(revoked_htlc_claim_a.output.len(), 1);
+ check_spends!(revoked_htlc_claim_a, revoked_commitment_a);
+ assert_eq!(revoked_htlc_claim_b.input.len(), 2); // Spends both HTLC outputs
+ assert_eq!(revoked_htlc_claim_b.output.len(), 1);
+ check_spends!(revoked_htlc_claim_b, revoked_commitment_b);
}
// Since Bob was able to confirm his revoked commitment, he'll now try to claim the HTLCs
// the second level instead.
let revoked_claims = {
let txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
- assert_eq!(txn.len(), 4);
-
- let revoked_to_self_claim_a = txn.iter().find(|tx|
- tx.input.len() == 1 &&
- tx.output.len() == 1 &&
- tx.input[0].previous_output.txid == revoked_commitment_a.txid()
- ).unwrap();
- check_spends!(revoked_to_self_claim_a, revoked_commitment_a);
-
- let revoked_to_self_claim_b = txn.iter().find(|tx|
- tx.input.len() == 1 &&
- tx.output.len() == 1 &&
- tx.input[0].previous_output.txid == revoked_commitment_b.txid()
- ).unwrap();
- check_spends!(revoked_to_self_claim_b, revoked_commitment_b);
+ assert_eq!(txn.len(), 2);
let revoked_htlc_claims = txn.iter().filter(|tx|
tx.input.len() == 2 &&
assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
let spendable_output_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
- assert_eq!(spendable_output_events.len(), 4);
+ assert_eq!(spendable_output_events.len(), 2);
for (idx, event) in spendable_output_events.iter().enumerate() {
if let Event::SpendableOutputs { outputs } = event {
assert_eq!(outputs.len(), 1);
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(
- &[&outputs[0]], Vec::new(), Script::new_op_return(&[]), 253, &Secp256k1::new(),
+ &[&outputs[0]], Vec::new(), Script::new_op_return(&[]), 253, None, &Secp256k1::new(),
).unwrap();
check_spends!(spend_tx, revoked_claims[idx]);
} else {
assert!(nodes[0].node.list_channels().is_empty());
assert!(nodes[1].node.list_channels().is_empty());
- assert!(nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]).is_empty());
+ // On the Alice side, the individual to_self_claim are still pending confirmation.
+ assert_eq!(nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]).len(), 2);
// TODO: From Bob's PoV, he still thinks he can claim the outputs from his revoked commitment.
// This needs to be fixed before we enable pruning `ChannelMonitor`s once they don't have any
// balances to claim.
use bitcoin::secp256k1::PublicKey;
use bitcoin::secp256k1::ecdsa::Signature;
-use bitcoin::secp256k1;
+use bitcoin::{secp256k1, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hash_types::{Txid, BlockHash};
use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
use crate::util::logger;
-use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname};
+use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited};
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
/// An [`open_channel`] message to be sent to or received from a peer.
///
+/// Used in V1 channel establishment
+///
/// [`open_channel`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenChannel {
pub first_per_commitment_point: PublicKey,
/// The channel flags to be used
pub channel_flags: u8,
- /// Optionally, a request to pre-set the to-sender output's `scriptPubkey` for when we collaboratively close
- pub shutdown_scriptpubkey: OptionalField<Script>,
+ /// A request to pre-set the to-sender output's `scriptPubkey` for when we collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
/// The channel type that this channel will represent
///
/// If this is `None`, we derive the channel type from the intersection of our
pub channel_type: Option<ChannelTypeFeatures>,
}
+/// An open_channel2 message to be sent by or received from the channel initiator.
+///
+/// Used in V2 channel establishment
+///
+// TODO(dual_funding): Add spec link for `open_channel2`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct OpenChannelV2 {
+ /// The genesis hash of the blockchain where the channel is to be opened
+ pub chain_hash: BlockHash,
+ /// A temporary channel ID derived using a zeroed out value for the channel acceptor's revocation basepoint
+ pub temporary_channel_id: [u8; 32],
+ /// The feerate for the funding transaction set by the channel initiator
+ pub funding_feerate_sat_per_1000_weight: u32,
+ /// The feerate for the commitment transaction set by the channel initiator
+ pub commitment_feerate_sat_per_1000_weight: u32,
+ /// Part of the channel value contributed by the channel initiator
+ pub funding_satoshis: u64,
+ /// The threshold below which outputs on transactions broadcast by the channel initiator will be
+ /// omitted
+ pub dust_limit_satoshis: u64,
+ /// The maximum inbound HTLC value in flight towards channel initiator, in milli-satoshi
+ pub max_htlc_value_in_flight_msat: u64,
+ /// The minimum HTLC size incoming to channel initiator, in milli-satoshi
+ pub htlc_minimum_msat: u64,
+ /// The number of blocks which the counterparty will have to wait to claim on-chain funds if they
+ /// broadcast a commitment transaction
+ pub to_self_delay: u16,
+ /// The maximum number of inbound HTLCs towards channel initiator
+ pub max_accepted_htlcs: u16,
+ /// The locktime for the funding transaction
+ pub locktime: u32,
+ /// The channel initiator's key controlling the funding transaction
+ pub funding_pubkey: PublicKey,
+ /// Used to derive a revocation key for transactions broadcast by counterparty
+ pub revocation_basepoint: PublicKey,
+ /// A payment key to channel initiator for transactions broadcast by counterparty
+ pub payment_basepoint: PublicKey,
+ /// Used to derive a payment key to channel initiator for transactions broadcast by channel
+ /// initiator
+ pub delayed_payment_basepoint: PublicKey,
+ /// Used to derive an HTLC payment key to channel initiator
+ pub htlc_basepoint: PublicKey,
+ /// The first to-be-broadcast-by-channel-initiator transaction's per commitment point
+ pub first_per_commitment_point: PublicKey,
+ /// The second to-be-broadcast-by-channel-initiator transaction's per commitment point
+ pub second_per_commitment_point: PublicKey,
+ /// Channel flags
+ pub channel_flags: u8,
+ /// Optionally, a request to pre-set the to-channel-initiator output's scriptPubkey for when we
+ /// collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
+ /// The channel type that this channel will represent. If none is set, we derive the channel
+ /// type from the intersection of our feature bits with our counterparty's feature bits from
+ /// the Init message.
+ pub channel_type: Option<ChannelTypeFeatures>,
+ /// Optionally, a requirement that only confirmed inputs can be added
+ pub require_confirmed_inputs: Option<()>,
+}
+
/// An [`accept_channel`] message to be sent to or received from a peer.
///
+/// Used in V1 channel establishment
+///
/// [`accept_channel`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-accept_channel-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AcceptChannel {
pub htlc_basepoint: PublicKey,
/// The first to-be-broadcast-by-sender transaction's per commitment point
pub first_per_commitment_point: PublicKey,
- /// Optionally, a request to pre-set the to-sender output's scriptPubkey for when we collaboratively close
- pub shutdown_scriptpubkey: OptionalField<Script>,
+ /// A request to pre-set the to-sender output's scriptPubkey for when we collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
/// The channel type that this channel will represent.
///
/// If this is `None`, we derive the channel type from the intersection of
pub next_local_nonce: Option<musig2::types::PublicNonce>,
}
+/// An accept_channel2 message to be sent by or received from the channel accepter.
+///
+/// Used in V2 channel establishment
+///
+// TODO(dual_funding): Add spec link for `accept_channel2`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct AcceptChannelV2 {
+ /// The same `temporary_channel_id` received from the initiator's `open_channel2` message.
+ pub temporary_channel_id: [u8; 32],
+ /// Part of the channel value contributed by the channel acceptor
+ pub funding_satoshis: u64,
+ /// The threshold below which outputs on transactions broadcast by the channel acceptor will be
+ /// omitted
+ pub dust_limit_satoshis: u64,
+ /// The maximum inbound HTLC value in flight towards channel acceptor, in milli-satoshi
+ pub max_htlc_value_in_flight_msat: u64,
+ /// The minimum HTLC size incoming to channel acceptor, in milli-satoshi
+ pub htlc_minimum_msat: u64,
+ /// Minimum depth of the funding transaction before the channel is considered open
+ pub minimum_depth: u32,
+ /// The number of blocks which the counterparty will have to wait to claim on-chain funds if they
+ /// broadcast a commitment transaction
+ pub to_self_delay: u16,
+ /// The maximum number of inbound HTLCs towards channel acceptor
+ pub max_accepted_htlcs: u16,
+ /// The channel acceptor's key controlling the funding transaction
+ pub funding_pubkey: PublicKey,
+ /// Used to derive a revocation key for transactions broadcast by counterparty
+ pub revocation_basepoint: PublicKey,
+ /// A payment key to channel acceptor for transactions broadcast by counterparty
+ pub payment_basepoint: PublicKey,
+ /// Used to derive a payment key to channel acceptor for transactions broadcast by channel
+ /// acceptor
+ pub delayed_payment_basepoint: PublicKey,
+ /// Used to derive an HTLC payment key to channel acceptor for transactions broadcast by counterparty
+ pub htlc_basepoint: PublicKey,
+ /// The first to-be-broadcast-by-channel-acceptor transaction's per commitment point
+ pub first_per_commitment_point: PublicKey,
+ /// The second to-be-broadcast-by-channel-acceptor transaction's per commitment point
+ pub second_per_commitment_point: PublicKey,
+ /// Optionally, a request to pre-set the to-channel-acceptor output's scriptPubkey for when we
+ /// collaboratively close
+ pub shutdown_scriptpubkey: Option<Script>,
+ /// The channel type that this channel will represent. If none is set, we derive the channel
+ /// type from the intersection of our feature bits with our counterparty's feature bits from
+ /// the Init message.
+ ///
+ /// This is required to match the equivalent field in [`OpenChannelV2::channel_type`].
+ pub channel_type: Option<ChannelTypeFeatures>,
+ /// Optionally, a requirement that only confirmed inputs can be added
+ pub require_confirmed_inputs: Option<()>,
+}
+
/// A [`funding_created`] message to be sent to or received from a peer.
///
+/// Used in V1 channel establishment
+///
/// [`funding_created`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-funding_created-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingCreated {
/// A [`funding_signed`] message to be sent to or received from a peer.
///
+/// Used in V1 channel establishment
+///
/// [`funding_signed`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-funding_signed-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingSigned {
pub short_channel_id_alias: Option<u64>,
}
+/// A tx_add_input message for adding an input during interactive transaction construction
+///
+// TODO(dual_funding): Add spec link for `tx_add_input`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAddInput {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// A randomly chosen unique identifier for this input, which is even for initiators and odd for
+ /// non-initiators.
+ pub serial_id: u64,
+ /// Serialized transaction that contains the output this input spends to verify that it is non
+ /// malleable.
+ pub prevtx: TransactionU16LenLimited,
+ /// The index of the output being spent
+ pub prevtx_out: u32,
+ /// The sequence number of this input
+ pub sequence: u32,
+}
+
+/// A tx_add_output message for adding an output during interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_add_output`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAddOutput {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// A randomly chosen unique identifier for this output, which is even for initiators and odd for
+ /// non-initiators.
+ pub serial_id: u64,
+ /// The satoshi value of the output
+ pub sats: u64,
+ /// The scriptPubKey for the output
+ pub script: Script,
+}
+
+/// A tx_remove_input message for removing an input during interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_remove_input`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxRemoveInput {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// The serial ID of the input to be removed
+ pub serial_id: u64,
+}
+
+/// A tx_remove_output message for removing an output during interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_remove_output`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxRemoveOutput {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// The serial ID of the output to be removed
+ pub serial_id: u64,
+}
+
+/// A tx_complete message signalling the conclusion of a peer's transaction contributions during
+/// interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_complete`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxComplete {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+}
+
+/// A tx_signatures message containing the sender's signatures for a transaction constructed with
+/// interactive transaction construction.
+///
+// TODO(dual_funding): Add spec link for `tx_signatures`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxSignatures {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// The TXID
+ pub tx_hash: Txid,
+ /// The list of witnesses
+ pub witnesses: Vec<Witness>,
+}
+
+/// A tx_init_rbf message which initiates a replacement of the transaction after it's been
+/// completed.
+///
+// TODO(dual_funding): Add spec link for `tx_init_rbf`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxInitRbf {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// The locktime of the transaction
+ pub locktime: u32,
+ /// The feerate of the transaction
+ pub feerate_sat_per_1000_weight: u32,
+ /// The number of satoshis the sender will contribute to or, if negative, remove from
+ /// (e.g. splice-out) the funding output of the transaction
+ pub funding_output_contribution: Option<i64>,
+}
+
+/// A tx_ack_rbf message which acknowledges replacement of the transaction after it's been
+/// completed.
+///
+// TODO(dual_funding): Add spec link for `tx_ack_rbf`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAckRbf {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// The number of satoshis the sender will contribute to or, if negative, remove from
+ /// (e.g. splice-out) the funding output of the transaction
+ pub funding_output_contribution: Option<i64>,
+}
+
+/// A tx_abort message which signals the cancellation of an in-progress transaction negotiation.
+///
+// TODO(dual_funding): Add spec link for `tx_abort`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TxAbort {
+ /// The channel ID
+ pub channel_id: [u8; 32],
+ /// Message data
+ pub data: Vec<u8>,
+}
+
/// A [`shutdown`] message to be sent to or received from a peer.
///
/// [`shutdown`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#closing-initiation-shutdown
pub feerate_per_kw: u32,
}
-#[derive(Clone, Debug, PartialEq, Eq)]
-/// Proof that the sender knows the per-commitment secret of the previous commitment transaction.
-///
-/// This is used to convince the recipient that the channel is at a certain commitment
-/// number even if they lost that data due to a local failure. Of course, the peer may lie
-/// and even later commitments may have been revoked.
-pub struct DataLossProtect {
- /// Proof that the sender knows the per-commitment secret of a specific commitment transaction
- /// belonging to the recipient
- pub your_last_per_commitment_secret: [u8; 32],
- /// The sender's per-commitment point for their current commitment transaction
- pub my_current_per_commitment_point: PublicKey,
-}
-
/// A [`channel_reestablish`] message to be sent to or received from a peer.
///
/// [`channel_reestablish`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#message-retransmission
pub next_local_commitment_number: u64,
/// The next commitment number for the recipient
pub next_remote_commitment_number: u64,
- /// Optionally, a field proving that next_remote_commitment_number-1 has been revoked
- pub data_loss_protect: OptionalField<DataLossProtect>,
+ /// Proof that the sender knows the per-commitment secret of a specific commitment transaction
+ /// belonging to the recipient
+ pub your_last_per_commitment_secret: [u8; 32],
+ /// The sender's per-commitment point for their current commitment transaction
+ pub my_current_per_commitment_point: PublicKey,
+ /// The next funding transaction ID
+ pub next_funding_txid: Option<Txid>,
}
/// An [`announcement_signatures`] message to be sent to or received from a peer.
pub commitment_signed: CommitmentSigned,
}
-/// Messages could have optional fields to use with extended features
-/// As we wish to serialize these differently from `Option<T>`s (`Options` get a tag byte, but
-/// [`OptionalField`] simply gets `Present` if there are enough bytes to read into it), we have a
-/// separate enum type for them.
-///
-/// This is not exported to bindings users due to a free generic in `T`
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum OptionalField<T> {
- /// Optional field is included in message
- Present(T),
- /// Optional field is absent in message
- Absent
-}
-
/// A trait to describe an object which can receive channel messages.
///
/// Messages MAY be called in parallel when they originate from different `their_node_ids`, however
// Channel init:
/// Handle an incoming `open_channel` message from the given peer.
fn handle_open_channel(&self, their_node_id: &PublicKey, msg: &OpenChannel);
+ /// Handle an incoming `open_channel2` message from the given peer.
+ fn handle_open_channel_v2(&self, their_node_id: &PublicKey, msg: &OpenChannelV2);
/// Handle an incoming `accept_channel` message from the given peer.
fn handle_accept_channel(&self, their_node_id: &PublicKey, msg: &AcceptChannel);
+ /// Handle an incoming `accept_channel2` message from the given peer.
+ fn handle_accept_channel_v2(&self, their_node_id: &PublicKey, msg: &AcceptChannelV2);
/// Handle an incoming `funding_created` message from the given peer.
fn handle_funding_created(&self, their_node_id: &PublicKey, msg: &FundingCreated);
/// Handle an incoming `funding_signed` message from the given peer.
/// Handle an incoming `closing_signed` message from the given peer.
fn handle_closing_signed(&self, their_node_id: &PublicKey, msg: &ClosingSigned);
+ // Interactive channel construction
+ /// Handle an incoming `tx_add_input message` from the given peer.
+ fn handle_tx_add_input(&self, their_node_id: &PublicKey, msg: &TxAddInput);
+ /// Handle an incoming `tx_add_output` message from the given peer.
+ fn handle_tx_add_output(&self, their_node_id: &PublicKey, msg: &TxAddOutput);
+ /// Handle an incoming `tx_remove_input` message from the given peer.
+ fn handle_tx_remove_input(&self, their_node_id: &PublicKey, msg: &TxRemoveInput);
+ /// Handle an incoming `tx_remove_output` message from the given peer.
+ fn handle_tx_remove_output(&self, their_node_id: &PublicKey, msg: &TxRemoveOutput);
+ /// Handle an incoming `tx_complete message` from the given peer.
+ fn handle_tx_complete(&self, their_node_id: &PublicKey, msg: &TxComplete);
+ /// Handle an incoming `tx_signatures` message from the given peer.
+ fn handle_tx_signatures(&self, their_node_id: &PublicKey, msg: &TxSignatures);
+ /// Handle an incoming `tx_init_rbf` message from the given peer.
+ fn handle_tx_init_rbf(&self, their_node_id: &PublicKey, msg: &TxInitRbf);
+ /// Handle an incoming `tx_ack_rbf` message from the given peer.
+ fn handle_tx_ack_rbf(&self, their_node_id: &PublicKey, msg: &TxAckRbf);
+ /// Handle an incoming `tx_abort message` from the given peer.
+ fn handle_tx_abort(&self, their_node_id: &PublicKey, msg: &TxAbort);
+
// HTLC handling:
/// Handle an incoming `update_add_htlc` message from the given peer.
fn handle_update_add_htlc(&self, their_node_id: &PublicKey, msg: &UpdateAddHTLC);
}
}
-impl Writeable for OptionalField<Script> {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- match *self {
- OptionalField::Present(ref script) => {
- // Note that Writeable for script includes the 16-bit length tag for us
- script.write(w)?;
- },
- OptionalField::Absent => {}
- }
- Ok(())
- }
-}
-
-impl Readable for OptionalField<Script> {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- match <u16 as Readable>::read(r) {
- Ok(len) => {
- let mut buf = vec![0; len as usize];
- r.read_exact(&mut buf)?;
- Ok(OptionalField::Present(Script::from(buf)))
- },
- Err(DecodeError::ShortRead) => Ok(OptionalField::Absent),
- Err(e) => Err(e)
- }
- }
-}
-
-impl Writeable for OptionalField<u64> {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- match *self {
- OptionalField::Present(ref value) => {
- value.write(w)?;
- },
- OptionalField::Absent => {}
- }
- Ok(())
- }
-}
-
-impl Readable for OptionalField<u64> {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let value: u64 = Readable::read(r)?;
- Ok(OptionalField::Present(value))
- }
-}
-
#[cfg(not(taproot))]
impl_writeable_msg!(AcceptChannel, {
temporary_channel_id,
delayed_payment_basepoint,
htlc_basepoint,
first_per_commitment_point,
- shutdown_scriptpubkey
}, {
+ (0, shutdown_scriptpubkey, (option, encoding: (Script, WithoutLength))), // Don't encode length twice.
(1, channel_type, option),
});
delayed_payment_basepoint,
htlc_basepoint,
first_per_commitment_point,
- shutdown_scriptpubkey
}, {
+ (0, shutdown_scriptpubkey, (option, encoding: (Script, WithoutLength))), // Don't encode length twice.
(1, channel_type, option),
(4, next_local_nonce, option),
});
+impl_writeable_msg!(AcceptChannelV2, {
+ temporary_channel_id,
+ funding_satoshis,
+ dust_limit_satoshis,
+ max_htlc_value_in_flight_msat,
+ htlc_minimum_msat,
+ minimum_depth,
+ to_self_delay,
+ max_accepted_htlcs,
+ funding_pubkey,
+ revocation_basepoint,
+ payment_basepoint,
+ delayed_payment_basepoint,
+ htlc_basepoint,
+ first_per_commitment_point,
+ second_per_commitment_point,
+}, {
+ (0, shutdown_scriptpubkey, option),
+ (1, channel_type, option),
+ (2, require_confirmed_inputs, option),
+});
+
+impl_writeable_msg!(TxAddInput, {
+ channel_id,
+ serial_id,
+ prevtx,
+ prevtx_out,
+ sequence,
+}, {});
+
+impl_writeable_msg!(TxAddOutput, {
+ channel_id,
+ serial_id,
+ sats,
+ script,
+}, {});
+
+impl_writeable_msg!(TxRemoveInput, {
+ channel_id,
+ serial_id,
+}, {});
+
+impl_writeable_msg!(TxRemoveOutput, {
+ channel_id,
+ serial_id,
+}, {});
+
+impl_writeable_msg!(TxComplete, {
+ channel_id,
+}, {});
+
+impl_writeable_msg!(TxSignatures, {
+ channel_id,
+ tx_hash,
+ witnesses,
+}, {});
+
+impl_writeable_msg!(TxInitRbf, {
+ channel_id,
+ locktime,
+ feerate_sat_per_1000_weight,
+}, {
+ (0, funding_output_contribution, option),
+});
+
+impl_writeable_msg!(TxAckRbf, {
+ channel_id,
+}, {
+ (0, funding_output_contribution, option),
+});
+
+impl_writeable_msg!(TxAbort, {
+ channel_id,
+ data,
+}, {});
+
impl_writeable_msg!(AnnouncementSignatures, {
channel_id,
short_channel_id,
bitcoin_signature
}, {});
-impl Writeable for ChannelReestablish {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- self.channel_id.write(w)?;
- self.next_local_commitment_number.write(w)?;
- self.next_remote_commitment_number.write(w)?;
- match self.data_loss_protect {
- OptionalField::Present(ref data_loss_protect) => {
- (*data_loss_protect).your_last_per_commitment_secret.write(w)?;
- (*data_loss_protect).my_current_per_commitment_point.write(w)?;
- },
- OptionalField::Absent => {}
- }
- Ok(())
- }
-}
-
-impl Readable for ChannelReestablish{
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- Ok(Self {
- channel_id: Readable::read(r)?,
- next_local_commitment_number: Readable::read(r)?,
- next_remote_commitment_number: Readable::read(r)?,
- data_loss_protect: {
- match <[u8; 32] as Readable>::read(r) {
- Ok(your_last_per_commitment_secret) =>
- OptionalField::Present(DataLossProtect {
- your_last_per_commitment_secret,
- my_current_per_commitment_point: Readable::read(r)?,
- }),
- Err(DecodeError::ShortRead) => OptionalField::Absent,
- Err(e) => return Err(e)
- }
- }
- })
- }
-}
+impl_writeable_msg!(ChannelReestablish, {
+ channel_id,
+ next_local_commitment_number,
+ next_remote_commitment_number,
+ your_last_per_commitment_secret,
+ my_current_per_commitment_point,
+}, {
+ (0, next_funding_txid, option),
+});
impl_writeable_msg!(ClosingSigned,
{ channel_id, fee_satoshis, signature },
(3, remote_network_address, option)
});
Ok(Init {
- features: features.or(global_features),
+ features: features | global_features,
remote_network_address,
})
}
htlc_basepoint,
first_per_commitment_point,
channel_flags,
- shutdown_scriptpubkey
}, {
+ (0, shutdown_scriptpubkey, (option, encoding: (Script, WithoutLength))), // Don't encode length twice.
(1, channel_type, option),
});
+impl_writeable_msg!(OpenChannelV2, {
+ chain_hash,
+ temporary_channel_id,
+ funding_feerate_sat_per_1000_weight,
+ commitment_feerate_sat_per_1000_weight,
+ funding_satoshis,
+ dust_limit_satoshis,
+ max_htlc_value_in_flight_msat,
+ htlc_minimum_msat,
+ to_self_delay,
+ max_accepted_htlcs,
+ locktime,
+ funding_pubkey,
+ revocation_basepoint,
+ payment_basepoint,
+ delayed_payment_basepoint,
+ htlc_basepoint,
+ first_per_commitment_point,
+ second_per_commitment_point,
+ channel_flags,
+}, {
+ (0, shutdown_scriptpubkey, option),
+ (1, channel_type, option),
+ (2, require_confirmed_inputs, option),
+});
+
#[cfg(not(taproot))]
impl_writeable_msg!(RevokeAndACK, {
channel_id,
#[cfg(test)]
mod tests {
+ use bitcoin::{Transaction, PackedLockTime, TxIn, Script, Sequence, Witness, TxOut};
use hex;
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
- use crate::ln::msgs;
- use crate::ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat};
+ use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, OnionHopDataFormat};
use crate::routing::gossip::{NodeAlias, NodeId};
- use crate::util::ser::{Writeable, Readable, Hostname};
+ use crate::util::ser::{Writeable, Readable, Hostname, TransactionU16LenLimited};
use bitcoin::hashes::hex::FromHex;
use bitcoin::util::address::Address;
use crate::io::{self, Cursor};
use crate::prelude::*;
use core::convert::TryFrom;
+ use core::str::FromStr;
+
+ use crate::chain::transaction::OutPoint;
#[test]
- fn encoding_channel_reestablish_no_secret() {
+ fn encoding_channel_reestablish() {
+ let public_key = {
+ let secp_ctx = Secp256k1::new();
+ PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap())
+ };
+
let cr = msgs::ChannelReestablish {
channel_id: [4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0],
next_local_commitment_number: 3,
next_remote_commitment_number: 4,
- data_loss_protect: OptionalField::Absent,
+ your_last_per_commitment_secret: [9;32],
+ my_current_per_commitment_point: public_key,
+ next_funding_txid: None,
};
let encoded_value = cr.encode();
assert_eq!(
encoded_value,
- vec![4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4]
+ vec![
+ 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, // channel_id
+ 0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number
+ 0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // your_last_per_commitment_secret
+ 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, // my_current_per_commitment_point
+ ]
);
}
#[test]
- fn encoding_channel_reestablish_with_secret() {
+ fn encoding_channel_reestablish_with_next_funding_txid() {
let public_key = {
let secp_ctx = Secp256k1::new();
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap())
channel_id: [4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0],
next_local_commitment_number: 3,
next_remote_commitment_number: 4,
- data_loss_protect: OptionalField::Present(msgs::DataLossProtect { your_last_per_commitment_secret: [9;32], my_current_per_commitment_point: public_key}),
+ your_last_per_commitment_secret: [9;32],
+ my_current_per_commitment_point: public_key,
+ next_funding_txid: Some(Txid::from_hash(bitcoin::hashes::Hash::from_slice(&[
+ 48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124,
+ ]).unwrap())),
};
let encoded_value = cr.encode();
assert_eq!(
encoded_value,
- vec![4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143]
+ vec![
+ 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, // channel_id
+ 0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number
+ 0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // your_last_per_commitment_secret
+ 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, // my_current_per_commitment_point
+ 0, // Type (next_funding_txid)
+ 32, // Length
+ 48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, // Value
+ ]
);
}
htlc_basepoint: pubkey_5,
first_per_commitment_point: pubkey_6,
channel_flags: if random_bit { 1 << 5 } else { 0 },
- shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent },
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
channel_type: if incl_chan_type { Some(ChannelTypeFeatures::empty()) } else { None },
};
let encoded_value = open_channel.encode();
do_encoding_open_channel(true, true, true);
}
+ fn do_encoding_open_channelv2(random_bit: bool, shutdown: bool, incl_chan_type: bool, require_confirmed_inputs: bool) {
+ let secp_ctx = Secp256k1::new();
+ let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
+ let (_, pubkey_2) = get_keys_from!("0202020202020202020202020202020202020202020202020202020202020202", secp_ctx);
+ let (_, pubkey_3) = get_keys_from!("0303030303030303030303030303030303030303030303030303030303030303", secp_ctx);
+ let (_, pubkey_4) = get_keys_from!("0404040404040404040404040404040404040404040404040404040404040404", secp_ctx);
+ let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
+ let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
+ let (_, pubkey_7) = get_keys_from!("0707070707070707070707070707070707070707070707070707070707070707", secp_ctx);
+ let open_channelv2 = msgs::OpenChannelV2 {
+ chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
+ temporary_channel_id: [2; 32],
+ funding_feerate_sat_per_1000_weight: 821716,
+ commitment_feerate_sat_per_1000_weight: 821716,
+ funding_satoshis: 1311768467284833366,
+ dust_limit_satoshis: 3608586615801332854,
+ max_htlc_value_in_flight_msat: 8517154655701053848,
+ htlc_minimum_msat: 2316138423780173,
+ to_self_delay: 49340,
+ max_accepted_htlcs: 49340,
+ locktime: 305419896,
+ funding_pubkey: pubkey_1,
+ revocation_basepoint: pubkey_2,
+ payment_basepoint: pubkey_3,
+ delayed_payment_basepoint: pubkey_4,
+ htlc_basepoint: pubkey_5,
+ first_per_commitment_point: pubkey_6,
+ second_per_commitment_point: pubkey_7,
+ channel_flags: if random_bit { 1 << 5 } else { 0 },
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
+ channel_type: if incl_chan_type { Some(ChannelTypeFeatures::empty()) } else { None },
+ require_confirmed_inputs: if require_confirmed_inputs { Some(()) } else { None },
+ };
+ let encoded_value = open_channelv2.encode();
+ let mut target_value = Vec::new();
+ target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
+ target_value.append(&mut hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap());
+ target_value.append(&mut hex::decode("000c89d4").unwrap());
+ target_value.append(&mut hex::decode("000c89d4").unwrap());
+ target_value.append(&mut hex::decode("1234567890123456").unwrap());
+ target_value.append(&mut hex::decode("3214466870114476").unwrap());
+ target_value.append(&mut hex::decode("7633030896203198").unwrap());
+ target_value.append(&mut hex::decode("00083a840000034d").unwrap());
+ target_value.append(&mut hex::decode("c0bc").unwrap());
+ target_value.append(&mut hex::decode("c0bc").unwrap());
+ target_value.append(&mut hex::decode("12345678").unwrap());
+ target_value.append(&mut hex::decode("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap());
+ target_value.append(&mut hex::decode("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766").unwrap());
+ target_value.append(&mut hex::decode("02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337").unwrap());
+ target_value.append(&mut hex::decode("03462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap());
+ target_value.append(&mut hex::decode("0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f7").unwrap());
+ target_value.append(&mut hex::decode("03f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap());
+ target_value.append(&mut hex::decode("02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f").unwrap());
+
+ if random_bit {
+ target_value.append(&mut hex::decode("20").unwrap());
+ } else {
+ target_value.append(&mut hex::decode("00").unwrap());
+ }
+ if shutdown {
+ target_value.append(&mut hex::decode("001b").unwrap()); // Type 0 + Length 27
+ target_value.append(&mut hex::decode("001976a91479b000887626b294a914501a4cd226b58b23598388ac").unwrap());
+ }
+ if incl_chan_type {
+ target_value.append(&mut hex::decode("0100").unwrap());
+ }
+ if require_confirmed_inputs {
+ target_value.append(&mut hex::decode("0200").unwrap());
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_open_channelv2() {
+ do_encoding_open_channelv2(false, false, false, false);
+ do_encoding_open_channelv2(false, false, false, true);
+ do_encoding_open_channelv2(false, false, true, false);
+ do_encoding_open_channelv2(false, false, true, true);
+ do_encoding_open_channelv2(false, true, false, false);
+ do_encoding_open_channelv2(false, true, false, true);
+ do_encoding_open_channelv2(false, true, true, false);
+ do_encoding_open_channelv2(false, true, true, true);
+ do_encoding_open_channelv2(true, false, false, false);
+ do_encoding_open_channelv2(true, false, false, true);
+ do_encoding_open_channelv2(true, false, true, false);
+ do_encoding_open_channelv2(true, false, true, true);
+ do_encoding_open_channelv2(true, true, false, false);
+ do_encoding_open_channelv2(true, true, false, true);
+ do_encoding_open_channelv2(true, true, true, false);
+ do_encoding_open_channelv2(true, true, true, true);
+ }
+
fn do_encoding_accept_channel(shutdown: bool) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
delayed_payment_basepoint: pubkey_4,
htlc_basepoint: pubkey_5,
first_per_commitment_point: pubkey_6,
- shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent },
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
channel_type: None,
#[cfg(taproot)]
next_local_nonce: None,
do_encoding_accept_channel(true);
}
+ fn do_encoding_accept_channelv2(shutdown: bool) {
+ let secp_ctx = Secp256k1::new();
+ let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
+ let (_, pubkey_2) = get_keys_from!("0202020202020202020202020202020202020202020202020202020202020202", secp_ctx);
+ let (_, pubkey_3) = get_keys_from!("0303030303030303030303030303030303030303030303030303030303030303", secp_ctx);
+ let (_, pubkey_4) = get_keys_from!("0404040404040404040404040404040404040404040404040404040404040404", secp_ctx);
+ let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
+ let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
+ let (_, pubkey_7) = get_keys_from!("0707070707070707070707070707070707070707070707070707070707070707", secp_ctx);
+ let accept_channelv2 = msgs::AcceptChannelV2 {
+ temporary_channel_id: [2; 32],
+ funding_satoshis: 1311768467284833366,
+ dust_limit_satoshis: 1311768467284833366,
+ max_htlc_value_in_flight_msat: 2536655962884945560,
+ htlc_minimum_msat: 2316138423780173,
+ minimum_depth: 821716,
+ to_self_delay: 49340,
+ max_accepted_htlcs: 49340,
+ funding_pubkey: pubkey_1,
+ revocation_basepoint: pubkey_2,
+ payment_basepoint: pubkey_3,
+ delayed_payment_basepoint: pubkey_4,
+ htlc_basepoint: pubkey_5,
+ first_per_commitment_point: pubkey_6,
+ second_per_commitment_point: pubkey_7,
+ shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
+ channel_type: None,
+ require_confirmed_inputs: None,
+ };
+ let encoded_value = accept_channelv2.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // temporary_channel_id
+ target_value.append(&mut hex::decode("1234567890123456").unwrap()); // funding_satoshis
+ target_value.append(&mut hex::decode("1234567890123456").unwrap()); // dust_limit_satoshis
+ target_value.append(&mut hex::decode("2334032891223698").unwrap()); // max_htlc_value_in_flight_msat
+ target_value.append(&mut hex::decode("00083a840000034d").unwrap()); // htlc_minimum_msat
+ target_value.append(&mut hex::decode("000c89d4").unwrap()); // minimum_depth
+ target_value.append(&mut hex::decode("c0bc").unwrap()); // to_self_delay
+ target_value.append(&mut hex::decode("c0bc").unwrap()); // max_accepted_htlcs
+ target_value.append(&mut hex::decode("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap()); // funding_pubkey
+ target_value.append(&mut hex::decode("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766").unwrap()); // revocation_basepoint
+ target_value.append(&mut hex::decode("02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337").unwrap()); // payment_basepoint
+ target_value.append(&mut hex::decode("03462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap()); // delayed_payment_basepoint
+ target_value.append(&mut hex::decode("0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f7").unwrap()); // htlc_basepoint
+ target_value.append(&mut hex::decode("03f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap()); // first_per_commitment_point
+ target_value.append(&mut hex::decode("02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f").unwrap()); // second_per_commitment_point
+ if shutdown {
+ target_value.append(&mut hex::decode("001b").unwrap()); // Type 0 + Length 27
+ target_value.append(&mut hex::decode("001976a91479b000887626b294a914501a4cd226b58b23598388ac").unwrap());
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_accept_channelv2() {
+ do_encoding_accept_channelv2(false);
+ do_encoding_accept_channelv2(true);
+ }
+
#[test]
fn encoding_funding_created() {
let secp_ctx = Secp256k1::new();
assert_eq!(encoded_value, target_value);
}
+ #[test]
+ fn encoding_tx_add_input() {
+ let tx_add_input = msgs::TxAddInput {
+ channel_id: [2; 32],
+ serial_id: 4886718345,
+ prevtx: TransactionU16LenLimited::new(Transaction {
+ version: 2,
+ lock_time: PackedLockTime(0),
+ input: vec![TxIn {
+ previous_output: OutPoint { txid: Txid::from_hex("305bab643ee297b8b6b76b320792c8223d55082122cb606bf89382146ced9c77").unwrap(), index: 2 }.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: Sequence(0xfffffffd),
+ witness: Witness::from_vec(vec![
+ hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap(),
+ hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap()]),
+ }],
+ output: vec![
+ TxOut {
+ value: 12704566,
+ script_pubkey: Address::from_str("bc1qzlffunw52jav8vwdu5x3jfk6sr8u22rmq3xzw2").unwrap().script_pubkey(),
+ },
+ TxOut {
+ value: 245148,
+ script_pubkey: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().script_pubkey(),
+ },
+ ],
+ }).unwrap(),
+ prevtx_out: 305419896,
+ sequence: 305419896,
+ };
+ let encoded_value = tx_add_input.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202000000012345678900de02000000000101779ced6c148293f86b60cb222108553d22c89207326bb7b6b897e23e64ab5b300200000000fdffffff0236dbc1000000000016001417d29e4dd454bac3b1cde50d1926da80cfc5287b9cbd03000000000016001436ec78d514df462da95e6a00c24daa8915362d420247304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701210301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944000000001234567812345678").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_add_output() {
+ let tx_add_output = msgs::TxAddOutput {
+ channel_id: [2; 32],
+ serial_id: 4886718345,
+ sats: 4886718345,
+ script: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().script_pubkey(),
+ };
+ let encoded_value = tx_add_output.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202000000012345678900000001234567890016001436ec78d514df462da95e6a00c24daa8915362d42").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_remove_input() {
+ let tx_remove_input = msgs::TxRemoveInput {
+ channel_id: [2; 32],
+ serial_id: 4886718345,
+ };
+ let encoded_value = tx_remove_input.encode();
+ let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020000000123456789").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_remove_output() {
+ let tx_remove_output = msgs::TxRemoveOutput {
+ channel_id: [2; 32],
+ serial_id: 4886718345,
+ };
+ let encoded_value = tx_remove_output.encode();
+ let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020000000123456789").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_complete() {
+ let tx_complete = msgs::TxComplete {
+ channel_id: [2; 32],
+ };
+ let encoded_value = tx_complete.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_signatures() {
+ let tx_signatures = msgs::TxSignatures {
+ channel_id: [2; 32],
+ tx_hash: Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
+ witnesses: vec![
+ Witness::from_vec(vec![
+ hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap(),
+ hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap()]),
+ Witness::from_vec(vec![
+ hex::decode("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap(),
+ hex::decode("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap()]),
+ ],
+ };
+ let encoded_value = tx_signatures.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
+ target_value.append(&mut hex::decode("6e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2").unwrap()); // tx_hash (sha256) (big endian byte order)
+ target_value.append(&mut hex::decode("0002").unwrap()); // num_witnesses (u16)
+ // Witness 1
+ target_value.append(&mut hex::decode("006b").unwrap()); // len of witness_data
+ target_value.append(&mut hex::decode("02").unwrap()); // num_witness_elements (VarInt)
+ target_value.append(&mut hex::decode("47").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap());
+ target_value.append(&mut hex::decode("21").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap());
+ // Witness 2
+ target_value.append(&mut hex::decode("006c").unwrap()); // len of witness_data
+ target_value.append(&mut hex::decode("02").unwrap()); // num_witness_elements (VarInt)
+ target_value.append(&mut hex::decode("48").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap());
+ target_value.append(&mut hex::decode("21").unwrap()); // len of witness element data (VarInt)
+ target_value.append(&mut hex::decode("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap());
+ assert_eq!(encoded_value, target_value);
+ }
+
+ fn do_encoding_tx_init_rbf(funding_value_with_hex_target: Option<(i64, &str)>) {
+ let tx_init_rbf = msgs::TxInitRbf {
+ channel_id: [2; 32],
+ locktime: 305419896,
+ feerate_sat_per_1000_weight: 20190119,
+ funding_output_contribution: if let Some((value, _)) = funding_value_with_hex_target { Some(value) } else { None },
+ };
+ let encoded_value = tx_init_rbf.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
+ target_value.append(&mut hex::decode("12345678").unwrap()); // locktime
+ target_value.append(&mut hex::decode("013413a7").unwrap()); // feerate_sat_per_1000_weight
+ if let Some((_, target)) = funding_value_with_hex_target {
+ target_value.push(0x00); // Type
+ target_value.push(target.len() as u8 / 2); // Length
+ target_value.append(&mut hex::decode(target).unwrap()); // Value (i64)
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_init_rbf() {
+ do_encoding_tx_init_rbf(Some((1311768467284833366, "1234567890123456")));
+ do_encoding_tx_init_rbf(Some((13117684672, "000000030DDFFBC0")));
+ do_encoding_tx_init_rbf(None);
+ }
+
+ fn do_encoding_tx_ack_rbf(funding_value_with_hex_target: Option<(i64, &str)>) {
+ let tx_ack_rbf = msgs::TxAckRbf {
+ channel_id: [2; 32],
+ funding_output_contribution: if let Some((value, _)) = funding_value_with_hex_target { Some(value) } else { None },
+ };
+ let encoded_value = tx_ack_rbf.encode();
+ let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap();
+ if let Some((_, target)) = funding_value_with_hex_target {
+ target_value.push(0x00); // Type
+ target_value.push(target.len() as u8 / 2); // Length
+ target_value.append(&mut hex::decode(target).unwrap()); // Value (i64)
+ }
+ assert_eq!(encoded_value, target_value);
+ }
+
+ #[test]
+ fn encoding_tx_ack_rbf() {
+ do_encoding_tx_ack_rbf(Some((1311768467284833366, "1234567890123456")));
+ do_encoding_tx_ack_rbf(Some((13117684672, "000000030DDFFBC0")));
+ do_encoding_tx_ack_rbf(None);
+ }
+
+ #[test]
+ fn encoding_tx_abort() {
+ let tx_abort = msgs::TxAbort {
+ channel_id: [2; 32],
+ data: hex::decode("54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E").unwrap(),
+ };
+ let encoded_value = tx_abort.encode();
+ let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202002C54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E").unwrap();
+ assert_eq!(encoded_value, target_value);
+ }
+
fn do_encoding_shutdown(script_type: u8) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
//! returned errors decode to the correct thing.
use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
-use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
+use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason};
use crate::ln::{PaymentHash, PaymentSecret};
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
// break the first (non-final) hop payload by swapping the realm (0) byte for a byte
// describing a length-1 TLV payload, which is obviously bogus.
new_payloads[0].data[0] = 1;
- msg.onion_routing_packet = onion_utils::construct_onion_packet_with_writable_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
+ msg.onion_routing_packet = onion_utils::construct_onion_packet_with_writable_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
}, ||{}, true, Some(PERM|22), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id));
// final node failure
// break the last-hop payload by swapping the realm (0) byte for a byte describing a
// length-1 TLV payload, which is obviously bogus.
new_payloads[1].data[0] = 1;
- msg.onion_routing_packet = onion_utils::construct_onion_packet_with_writable_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
+ msg.onion_routing_packet = onion_utils::construct_onion_packet_with_writable_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
}, ||{}, false, Some(PERM|22), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id));
// the following three with run_onion_failure_test_with_fail_intercept() test only the origin node
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads(
&route.paths[0], 40000, RecipientOnionFields::spontaneous_empty(), height, &None).unwrap();
- let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
+ let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
msg.cltv_expiry = htlc_cltv;
msg.onion_routing_packet = onion_packet;
}, ||{}, true, Some(21), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id));
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(*channel_to_update_counterparty, TEST_FINAL_CLTV)
- .with_features(nodes[2].node.invoice_features())
- .with_route_hints(hop_hints);
- get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, PAYMENT_AMT, TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_route_hints(hop_hints).unwrap();
+ get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, PAYMENT_AMT)
};
send_along_route_with_secret(&nodes[0], route.clone(), &[&[&nodes[1], &nodes[2]]], PAYMENT_AMT,
payment_hash, payment_secret);
create_announced_chan_between_nodes(&nodes, 1, 2);
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(InvoiceFeatures::empty());
- let (route, _payment_hash, _payment_preimage, _payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 40000, TEST_FINAL_CLTV);
+ .with_bolt11_features(InvoiceFeatures::empty()).unwrap();
+ let (route, _payment_hash, _payment_preimage, _payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 40000);
let hops = &route.paths[0].hops;
// Asserts that the first hop to `node[1]` signals no support for variable length onions.
let phantom_pubkey = $nodes[1].keys_manager.get_node_id(Recipient::PhantomNode).unwrap();
let phantom_route_hint = $nodes[1].node.get_phantom_route_hints();
let payment_params = PaymentParameters::from_node_id(phantom_pubkey, TEST_FINAL_CLTV)
- .with_features($nodes[1].node.invoice_features())
+ .with_bolt11_features($nodes[1].node.invoice_features()).unwrap()
.with_route_hints(vec![RouteHint(vec![
RouteHintHop {
src_node_id: $nodes[0].node.get_our_node_id(),
htlc_minimum_msat: None,
htlc_maximum_msat: None,
}
- ])]);
+ ])]).unwrap();
let scorer = test_utils::TestScorer::new();
let network_graph = $nodes[0].network_graph.read_only();
(get_route(
&$nodes[0].node.get_our_node_id(), &payment_params, &network_graph,
Some(&$nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
- $amt, TEST_FINAL_CLTV, $nodes[0].logger, &scorer, &[0u8; 32]
+ $amt, $nodes[0].logger, &scorer, &(), &[0u8; 32]
).unwrap(), phantom_route_hint.phantom_scid)
}
}}
onion_keys.remove(0);
onion_payloads.remove(0);
- let new_onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
+ let new_onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
onion_packet.hop_data = new_onion_packet.hop_data;
onion_packet.hmac = new_onion_packet.hmac;
},
}
}
-pub(super) fn route_size_insane(payloads: &Vec<msgs::OnionHopData>) -> bool {
- let mut len = 0;
- for payload in payloads.iter() {
- let mut payload_len = LengthCalculatingWriter(0);
- payload.write(&mut payload_len).expect("Failed to calculate length");
- assert!(payload_len.0 + 32 < ONION_DATA_LEN);
- len += payload_len.0 + 32;
- if len > ONION_DATA_LEN {
- return true;
- }
- }
- false
-}
-
-/// panics if route_size_insane(payloads)
-pub(super) fn construct_onion_packet(payloads: Vec<msgs::OnionHopData>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], associated_data: &PaymentHash) -> msgs::OnionPacket {
+pub(super) fn construct_onion_packet(payloads: Vec<msgs::OnionHopData>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], associated_data: &PaymentHash) -> Result<msgs::OnionPacket, ()> {
let mut packet_data = [0; ONION_DATA_LEN];
let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
#[cfg(test)]
/// Used in testing to write bogus `BogusOnionHopData` as well as `RawOnionHopData`, which is
/// otherwise not representable in `msgs::OnionHopData`.
-pub(super) fn construct_onion_packet_with_writable_hopdata<HD: Writeable>(payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], associated_data: &PaymentHash) -> msgs::OnionPacket {
+pub(super) fn construct_onion_packet_with_writable_hopdata<HD: Writeable>(payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], associated_data: &PaymentHash) -> Result<msgs::OnionPacket, ()> {
let mut packet_data = [0; ONION_DATA_LEN];
let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
payloads.iter().map(|p| p.serialized_length() + 32 /* HMAC */).sum()
}
-/// panics if payloads_serialized_length(payloads) > packet_data_len
pub(crate) fn construct_onion_message_packet<HD: Writeable, P: Packet<Data = Vec<u8>>>(
- payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], packet_data_len: usize) -> P
+ payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], packet_data_len: usize) -> Result<P, ()>
{
let mut packet_data = vec![0; packet_data_len];
construct_onion_packet_with_init_noise::<_, _>(payloads, onion_keys, packet_data, None)
}
-/// panics if payloads_serialized_length(payloads) > packet_data.len()
fn construct_onion_packet_with_init_noise<HD: Writeable, P: Packet>(
- mut payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, mut packet_data: P::Data, associated_data: Option<&PaymentHash>) -> P
+ mut payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, mut packet_data: P::Data, associated_data: Option<&PaymentHash>) -> Result<P, ()>
{
let filler = {
let packet_data = packet_data.as_mut();
let mut payload_len = LengthCalculatingWriter(0);
payload.write(&mut payload_len).expect("Failed to calculate length");
pos += payload_len.0 + 32;
- assert!(pos <= packet_data.len());
+ if pos > packet_data.len() {
+ return Err(());
+ }
res.resize(pos, 0u8);
chacha.process_in_place(&mut res);
chacha.process_in_place(packet_data);
if i == 0 {
- packet_data[ONION_DATA_LEN - filler.len()..ONION_DATA_LEN].copy_from_slice(&filler[..]);
+ let stop_index = packet_data.len();
+ let start_index = stop_index.checked_sub(filler.len()).ok_or(())?;
+ packet_data[start_index..stop_index].copy_from_slice(&filler[..]);
}
let mut hmac = HmacEngine::<Sha256>::new(&keys.mu);
hmac_res = Hmac::from_engine(hmac).into_inner();
}
- P::new(onion_keys.first().unwrap().ephemeral_pubkey, packet_data, hmac_res)
+ Ok(P::new(onion_keys.first().unwrap().ephemeral_pubkey, packet_data, hmac_res))
}
/// Encrypts a failure packet. raw_packet can either be a
let pad_keytype_seed = super::gen_pad_from_shared_secret(&get_test_session_key().secret_bytes());
- let packet: msgs::OnionPacket = super::construct_onion_packet_with_writable_hopdata::<_>(payloads, onion_keys, pad_keytype_seed, &PaymentHash([0x42; 32]));
+ let packet: msgs::OnionPacket = super::construct_onion_packet_with_writable_hopdata::<_>(payloads, onion_keys, pad_keytype_seed, &PaymentHash([0x42; 32])).unwrap();
assert_eq!(packet.encode(), hex::decode("0002EEC7245D6B7D2CCB30380BFBE2A3648CD7A942653F5AA340EDCEA1F283686619F7F3416A5AA36DC7EEB3EC6D421E9615471AB870A33AC07FA5D5A51DF0A8823AABE3FEA3F90D387529D4F72837F9E687230371CCD8D263072206DBED0234F6505E21E282ABD8C0E4F5B9FF8042800BBAB065036EADD0149B37F27DDE664725A49866E052E809D2B0198AB9610FAA656BBF4EC516763A59F8F42C171B179166BA38958D4F51B39B3E98706E2D14A2DAFD6A5DF808093ABFCA5AEAACA16EDED5DB7D21FB0294DD1A163EDF0FB445D5C8D7D688D6DD9C541762BF5A5123BF9939D957FE648416E88F1B0928BFA034982B22548E1A4D922690EECF546275AFB233ACF4323974680779F1A964CFE687456035CC0FBA8A5428430B390F0057B6D1FE9A8875BFA89693EEB838CE59F09D207A503EE6F6299C92D6361BC335FCBF9B5CD44747AADCE2CE6069CFDC3D671DAEF9F8AE590CF93D957C9E873E9A1BC62D9640DC8FC39C14902D49A1C80239B6C5B7FD91D05878CBF5FFC7DB2569F47C43D6C0D27C438ABFF276E87364DEB8858A37E5A62C446AF95D8B786EAF0B5FCF78D98B41496794F8DCAAC4EEF34B2ACFB94C7E8C32A9E9866A8FA0B6F2A06F00A1CCDE569F97EEC05C803BA7500ACC96691D8898D73D8E6A47B8F43C3D5DE74458D20EDA61474C426359677001FBD75A74D7D5DB6CB4FEB83122F133206203E4E2D293F838BF8C8B3A29ACB321315100B87E80E0EDB272EE80FDA944E3FB6084ED4D7F7C7D21C69D9DA43D31A90B70693F9B0CC3EAC74C11AB8FF655905688916CFA4EF0BD04135F2E50B7C689A21D04E8E981E74C6058188B9B1F9DFC3EEC6838E9FFBCF22CE738D8A177C19318DFFEF090CEE67E12DE1A3E2A39F61247547BA5257489CBC11D7D91ED34617FCC42F7A9DA2E3CF31A94A210A1018143173913C38F60E62B24BF0D7518F38B5BAB3E6A1F8AEB35E31D6442C8ABB5178EFC892D2E787D79C6AD9E2FC271792983FA9955AC4D1D84A36C024071BC6E431B625519D556AF38185601F70E29035EA6A09C8B676C9D88CF7E05E0F17098B584C4168735940263F940033A220F40BE4C85344128B14BEB9E75696DB37014107801A59B13E89CD9D2258C169D523BE6D31552C44C82FF4BB18EC9F099F3BF0E5B1BB2BA9A87D7E26F98D294927B600B5529C47E04D98956677CBCEE8FA2B60F49776D8B8C367465B7C626DA53700684FB6C918EAD0EAB8360E4F60EDD25B4F43816A75ECF70F909301825B512469F8389D79402311D8AECB7B3EF8599E79485A4388D87744D899F7C47EE644361E17040A7958C8911BE6F463AB6A9B2AFACD688EC55EF517B38F1339EFC54487232798BB25522FF4572FF68567FE830F92F7B8113EFCE3E98C3FFFBAEDCE4FD8B50E41DA97C0C08E423A72689CC68E68F752A5E3A9003E64E35C957CA2E1C48BB6F64B05F56B70B575AD2F278D57850A7AD568C24A4D32A3D74B29F03DC125488BC7C637DA582357F40B0A52D16B3B40BB2C2315D03360BC24209E20972C200566BCF3BBE5C5B0AEDD83132A8A4D5B4242BA370B6D67D9B67EB01052D132C7866B9CB502E44796D9D356E4E3CB47CC527322CD24976FE7C9257A2864151A38E568EF7A79F10D6EF27CC04CE382347A2488B1F404FDBF407FE1CA1C9D0D5649E34800E25E18951C98CAE9F43555EEF65FEE1EA8F15828807366C3B612CD5753BF9FB8FCED08855F742CDDD6F765F74254F03186683D646E6F09AC2805586C7CF11998357CAFC5DF3F285329366F475130C928B2DCEBA4AA383758E7A9D20705C4BB9DB619E2992F608A1BA65DB254BB389468741D0502E2588AEB54390AC600C19AF5C8E61383FC1BEBE0029E4474051E4EF908828DB9CCA13277EF65DB3FD47CCC2179126AAEFB627719F421E20").unwrap());
}
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
-use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
+use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::events::{self, PaymentFailureReason};
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
+use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
use crate::ln::onion_utils::HTLCFailReason;
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
use crate::util::errors::APIError;
/// and add a pending payment that was already fulfilled.
Fulfilled {
session_privs: HashSet<[u8; 32]>,
+ /// Filled in for any payment which moved to `Fulfilled` on LDK 0.0.104 or later.
payment_hash: Option<PaymentHash>,
timer_ticks_without_htlcs: u8,
},
retry_strategy: Retry, route_params: RouteParameters, router: &R,
first_hops: Vec<ChannelDetails>, compute_inflight_htlcs: IH, entropy_source: &ES,
node_signer: &NS, best_block_height: u32, logger: &L,
- pending_events: &Mutex<Vec<events::Event>>, send_payment_along_path: SP,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
) -> Result<(), RetryableSendFailure>
where
R::Target: Router,
payment_id: PaymentId, retry_strategy: Retry, route_params: RouteParameters, router: &R,
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
node_signer: &NS, best_block_height: u32, logger: &L,
- pending_events: &Mutex<Vec<events::Event>>, send_payment_along_path: SP
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP
) -> Result<PaymentHash, RetryableSendFailure>
where
R::Target: Router,
pub(super) fn check_retry_payments<R: Deref, ES: Deref, NS: Deref, SP, IH, FH, L: Deref>(
&self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
- best_block_height: u32, pending_events: &Mutex<Vec<events::Event>>, logger: &L,
+ best_block_height: u32,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, logger: &L,
send_payment_along_path: SP,
)
where
if !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 {
pmt.mark_abandoned(PaymentFailureReason::RetriesExhausted);
if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = pmt {
- pending_events.lock().unwrap().push(events::Event::PaymentFailed {
+ pending_events.lock().unwrap().push_back((events::Event::PaymentFailed {
payment_id: *pmt_id,
payment_hash: *payment_hash,
reason: *reason,
- });
+ }, None));
retain = false;
}
}
keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, route_params: RouteParameters,
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
node_signer: &NS, best_block_height: u32, logger: &L,
- pending_events: &Mutex<Vec<events::Event>>, send_payment_along_path: SP,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
) -> Result<(), RetryableSendFailure>
where
R::Target: Router,
&self, payment_hash: PaymentHash, payment_id: PaymentId, route_params: RouteParameters,
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: &IH, entropy_source: &ES,
node_signer: &NS, best_block_height: u32, logger: &L,
- pending_events: &Mutex<Vec<events::Event>>, send_payment_along_path: &SP,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: &SP,
)
where
R::Target: Router,
$payment.get_mut().mark_abandoned($reason);
if let PendingOutboundPayment::Abandoned { reason, .. } = $payment.get() {
if $payment.get().remaining_parts() == 0 {
- pending_events.lock().unwrap().push(events::Event::PaymentFailed {
+ pending_events.lock().unwrap().push_back((events::Event::PaymentFailed {
payment_id,
payment_hash,
reason: *reason,
- });
+ }, None));
$payment.remove();
}
}
&self, err: PaymentSendFailure, payment_id: PaymentId, payment_hash: PaymentHash, route: Route,
mut route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
inflight_htlcs: &IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L,
- pending_events: &Mutex<Vec<events::Event>>, send_payment_along_path: &SP,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: &SP,
)
where
R::Target: Router,
fn push_path_failed_evs_and_scids<I: ExactSizeIterator + Iterator<Item = Result<(), APIError>>, L: Deref>(
payment_id: PaymentId, payment_hash: PaymentHash, route_params: &mut RouteParameters,
- paths: Vec<Path>, path_results: I, logger: &L, pending_events: &Mutex<Vec<events::Event>>
+ paths: Vec<Path>, path_results: I, logger: &L,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
) where L::Target: Logger {
let mut events = pending_events.lock().unwrap();
debug_assert_eq!(paths.len(), path_results.len());
failed_scid = Some(scid);
route_params.payment_params.previously_failed_channels.push(scid);
}
- events.push(events::Event::PaymentPathFailed {
+ events.push_back((events::Event::PaymentPathFailed {
payment_id: Some(payment_id),
payment_hash,
payment_failed_permanently: false,
error_code: None,
#[cfg(test)]
error_data: None,
- });
+ }, None));
}
}
}
pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
- path: Path, from_onchain: bool, pending_events: &Mutex<Vec<events::Event>>, logger: &L
+ path: Path, from_onchain: bool,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
+ logger: &L,
) where L::Target: Logger {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
if !payment.get().is_fulfilled() {
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
let fee_paid_msat = payment.get().get_pending_fee_msat();
- pending_events.push(
- events::Event::PaymentSent {
- payment_id: Some(payment_id),
- payment_preimage,
- payment_hash,
- fee_paid_msat,
- }
- );
+ pending_events.push_back((events::Event::PaymentSent {
+ payment_id: Some(payment_id),
+ payment_preimage,
+ payment_hash,
+ fee_paid_msat,
+ }, None));
payment.get_mut().mark_fulfilled();
}
// irrevocably fulfilled.
if payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
let payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0).into_inner()));
- pending_events.push(
- events::Event::PaymentPathSuccessful {
- payment_id,
- payment_hash,
- path,
- }
- );
+ pending_events.push_back((events::Event::PaymentPathSuccessful {
+ payment_id,
+ payment_hash,
+ path,
+ }, None));
}
}
} else {
}
}
- pub(super) fn finalize_claims(&self, sources: Vec<HTLCSource>, pending_events: &Mutex<Vec<events::Event>>) {
+ pub(super) fn finalize_claims(&self, sources: Vec<HTLCSource>,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>)
+ {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
let mut pending_events = pending_events.lock().unwrap();
for source in sources {
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
assert!(payment.get().is_fulfilled());
if payment.get_mut().remove(&session_priv_bytes, None) {
- pending_events.push(
- events::Event::PaymentPathSuccessful {
- payment_id,
- payment_hash: payment.get().payment_hash(),
- path,
- }
- );
+ let payment_hash = payment.get().payment_hash();
+ debug_assert!(payment_hash.is_some());
+ pending_events.push_back((events::Event::PaymentPathSuccessful {
+ payment_id,
+ payment_hash,
+ path,
+ }, None));
}
}
}
}
}
- pub(super) fn remove_stale_resolved_payments(&self, pending_events: &Mutex<Vec<events::Event>>) {
+ pub(super) fn remove_stale_resolved_payments(&self,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>)
+ {
// If an outbound payment was completed, and no pending HTLCs remain, we should remove it
// from the map. However, if we did that immediately when the last payment HTLC is claimed,
// this could race the user making a duplicate send_payment call and our idempotency
if let PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment {
let mut no_remaining_entries = session_privs.is_empty();
if no_remaining_entries {
- for ev in pending_events.iter() {
+ for (ev, _) in pending_events.iter() {
match ev {
events::Event::PaymentSent { payment_id: Some(ev_payment_id), .. } |
events::Event::PaymentPathSuccessful { payment_id: ev_payment_id, .. } |
// Returns a bool indicating whether a PendingHTLCsForwardable event should be generated.
pub(super) fn fail_htlc<L: Deref>(
&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason,
- path: &Path, session_priv: &SecretKey, payment_id: &PaymentId, probing_cookie_secret: [u8; 32],
- secp_ctx: &Secp256k1<secp256k1::All>, pending_events: &Mutex<Vec<events::Event>>, logger: &L
+ path: &Path, session_priv: &SecretKey, payment_id: &PaymentId,
+ probing_cookie_secret: [u8; 32], secp_ctx: &Secp256k1<secp256k1::All>,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, logger: &L,
) -> bool where L::Target: Logger {
#[cfg(test)]
let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_error.decode_onion_failure(secp_ctx, logger, &source);
}
};
let mut pending_events = pending_events.lock().unwrap();
- pending_events.push(path_failure);
- if let Some(ev) = full_failure_ev { pending_events.push(ev); }
+ pending_events.push_back((path_failure, None));
+ if let Some(ev) = full_failure_ev { pending_events.push_back((ev, None)); }
pending_retry_ev
}
pub(super) fn abandon_payment(
- &self, payment_id: PaymentId, reason: PaymentFailureReason, pending_events: &Mutex<Vec<events::Event>>
+ &self, payment_id: PaymentId, reason: PaymentFailureReason,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>
) {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
payment.get_mut().mark_abandoned(reason);
if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = payment.get() {
if payment.get().remaining_parts() == 0 {
- pending_events.lock().unwrap().push(events::Event::PaymentFailed {
+ pending_events.lock().unwrap().push_back((events::Event::PaymentFailed {
payment_id,
payment_hash: *payment_hash,
reason: *reason,
- });
+ }, None));
payment.remove();
}
}
use crate::util::errors::APIError;
use crate::util::test_utils;
+ use alloc::collections::VecDeque;
+
#[test]
#[cfg(feature = "std")]
fn fails_paying_after_expiration() {
payment_params,
final_value_msat: 0,
};
- let pending_events = Mutex::new(Vec::new());
+ let pending_events = Mutex::new(VecDeque::new());
if on_retry {
outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(),
PaymentId([0; 32]), None, &Route { paths: vec![], payment_params: None },
&pending_events, &|_, _, _, _, _, _, _, _| Ok(()));
let events = pending_events.lock().unwrap();
assert_eq!(events.len(), 1);
- if let Event::PaymentFailed { ref reason, .. } = events[0] {
+ if let Event::PaymentFailed { ref reason, .. } = events[0].0 {
assert_eq!(reason.unwrap(), PaymentFailureReason::PaymentExpired);
} else { panic!("Unexpected event"); }
} else {
router.expect_find_route(route_params.clone(),
Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }));
- let pending_events = Mutex::new(Vec::new());
+ let pending_events = Mutex::new(VecDeque::new());
if on_retry {
outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(),
PaymentId([0; 32]), None, &Route { paths: vec![], payment_params: None },
&pending_events, &|_, _, _, _, _, _, _, _| Ok(()));
let events = pending_events.lock().unwrap();
assert_eq!(events.len(), 1);
- if let Event::PaymentFailed { .. } = events[0] { } else { panic!("Unexpected event"); }
+ if let Event::PaymentFailed { .. } = events[0].0 { } else { panic!("Unexpected event"); }
} else {
let err = outbound_payments.send_payment(
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
// Ensure that a ChannelUnavailable error will result in blaming an scid in the
// PaymentPathFailed event.
- let pending_events = Mutex::new(Vec::new());
+ let pending_events = Mutex::new(VecDeque::new());
outbound_payments.send_payment(
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(),
assert_eq!(events.len(), 2);
if let Event::PaymentPathFailed {
short_channel_id,
- failure: PathFailure::InitialSend { err: APIError::ChannelUnavailable { .. }}, .. } = events[0]
+ failure: PathFailure::InitialSend { err: APIError::ChannelUnavailable { .. }}, .. } = events[0].0
{
assert_eq!(short_channel_id, Some(failed_scid));
} else { panic!("Unexpected event"); }
- if let Event::PaymentFailed { .. } = events[1] { } else { panic!("Unexpected event"); }
+ if let Event::PaymentFailed { .. } = events[1].0 { } else { panic!("Unexpected event"); }
events.clear();
core::mem::drop(events);
assert_eq!(events.len(), 2);
if let Event::PaymentPathFailed {
short_channel_id,
- failure: PathFailure::InitialSend { err: APIError::APIMisuseError { .. }}, .. } = events[0]
+ failure: PathFailure::InitialSend { err: APIError::APIMisuseError { .. }}, .. } = events[0].0
{
assert_eq!(short_channel_id, None);
} else { panic!("Unexpected event"); }
- if let Event::PaymentFailed { .. } = events[1] { } else { panic!("Unexpected event"); }
+ if let Event::PaymentFailed { .. } = events[1].0 { } else { panic!("Unexpected event"); }
}
}
use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use crate::chain::transaction::OutPoint;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason};
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
use crate::util::ser::Writeable;
use crate::util::string::UntrustedString;
-use bitcoin::{Block, BlockHeader, TxMerkleNode};
-use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
use crate::prelude::*;
check_added_monitors!(nodes[1], 1);
expect_payment_claimed!(nodes[1], payment_hash, 10_000_000);
- let mut header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[1].clone()]});
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, vec![node_txn[1].clone()]));
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
assert_eq!(claim_txn.len(), 1);
check_spends!(claim_txn[0], node_txn[1]);
- header.prev_blockhash = nodes[0].best_block_hash();
- connect_block(&nodes[0], &Block { header, txdata: vec![node_txn[1].clone()]});
+ connect_block(&nodes[0], &create_dummy_block(nodes[0].best_block_hash(), 42, vec![node_txn[1].clone()]));
if confirm_commitment_tx {
connect_blocks(&nodes[0], BREAKDOWN_TIMEOUT as u32 - 1);
}
- header.prev_blockhash = nodes[0].best_block_hash();
- let claim_block = Block { header, txdata: if payment_timeout { timeout_txn } else { vec![claim_txn[0].clone()] } };
+ let claim_block = create_dummy_block(nodes[0].best_block_hash(), 42, if payment_timeout { timeout_txn } else { vec![claim_txn[0].clone()] });
if payment_timeout {
assert!(confirm_commitment_tx); // Otherwise we're spending below our CSV!
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(amt_msat), expiry_secs, None).unwrap();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[1].node.invoice_features());
+ .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
let scorer = test_utils::TestScorer::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let route = get_route(
&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(),
Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
- amt_msat, TEST_FINAL_CLTV, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
+ amt_msat, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
nodes[0].node.send_payment_with_route(&route, payment_hash,
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 1);
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42);
- let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 9_998_000, 42);
+ let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 9_998_000);
let (payment_hash, payment_id) = nodes[0].node.send_probe(route.paths[0].clone()).unwrap();
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42);
// Send a dust HTLC, which will be treated as if it timed out once the channel hits the chain.
- let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 1_000, 42);
+ let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 1_000);
let (payment_hash, payment_id) = nodes[0].node.send_probe(route.paths[0].clone()).unwrap();
// node[0] -- update_add_htlcs -> node[1]
htlc_minimum_msat: None,
htlc_maximum_msat: None,
}])
- ])
- .with_features(nodes[2].node.invoice_features());
+ ]).unwrap()
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
let route = get_route(
&nodes[0].node.get_our_node_id(), &route_params.payment_params,
&nodes[0].network_graph.read_only(), None, route_params.final_value_msat,
- route_params.payment_params.final_cltv_expiry_delta, nodes[0].logger, &scorer,
- &random_seed_bytes,
+ nodes[0].logger, &scorer, &(), &random_seed_bytes,
).unwrap();
let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap();
_ => panic!("Unexpected event")
}
} else if test == InterceptTest::Timeout {
- let mut block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![],
- };
+ let mut block = create_dummy_block(nodes[0].best_block_hash(), 42, Vec::new());
connect_block(&nodes[0], &block);
connect_block(&nodes[1], &block);
for _ in 0..TEST_FINAL_CLTV {
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params: payment_params.clone(),
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
invoice_features.set_basic_mpp_optional();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_expiry_time(payment_expiry_secs as u64)
- .with_features(invoice_features);
+ .with_bolt11_features(invoice_features).unwrap();
let mut route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[3]);
let mut route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[1].node.invoice_features()),
+ .with_bolt11_features(nodes[1].node.invoice_features()).unwrap(),
final_value_msat: 10_000_000,
};
let mut route = nodes[0].router.find_route(&nodes[0].node.get_our_node_id(), &route_params,
let payment_metadata = vec![44, 49, 52, 142];
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[1].node.invoice_features());
+ .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
let mut route_params = RouteParameters {
payment_params,
final_value_msat: amt_msat,
use crate::prelude::*;
-use crate::chain::keysinterface::{NodeSigner, Recipient};
+use crate::sign::{NodeSigner, Recipient};
use crate::ln::msgs::LightningError;
use crate::ln::msgs;
use crate::ln::wire;
use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey};
-use crate::chain::keysinterface::{KeysManager, NodeSigner, Recipient};
+use crate::sign::{KeysManager, NodeSigner, Recipient};
use crate::events::{MessageSendEvent, MessageSendEventsProvider, OnionMessageProvider};
use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::msgs;
/// in the process. Each message is paired with the node id of the intended recipient. If no
/// connection to the node exists, then the message is simply not sent.
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)>;
+
+ /// Gets the node feature flags which this handler itself supports. All available handlers are
+ /// queried similarly and their feature flags are OR'd together to form the [`NodeFeatures`]
+ /// which are broadcasted in our [`NodeAnnouncement`] message.
+ ///
+ /// [`NodeAnnouncement`]: crate::ln::msgs::NodeAnnouncement
+ fn provided_node_features(&self) -> NodeFeatures;
+
+ /// Gets the init feature flags which should be sent to the given peer. All available handlers
+ /// are queried similarly and their feature flags are OR'd together to form the [`InitFeatures`]
+ /// which are sent in our [`Init`] message.
+ ///
+ /// [`Init`]: crate::ln::msgs::Init
+ fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures;
}
/// A dummy struct which implements `RoutingMessageHandler` without storing any routing information
}
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> { Vec::new() }
+
+ fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
+
+ fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
+ InitFeatures::empty()
+ }
}
/// A dummy struct which implements `ChannelMessageHandler` without having any channels.
features.set_zero_conf_optional();
features
}
+
+ fn handle_open_channel_v2(&self, their_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.temporary_channel_id);
+ }
+
+ fn handle_accept_channel_v2(&self, their_node_id: &PublicKey, msg: &msgs::AcceptChannelV2) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.temporary_channel_id);
+ }
+
+ fn handle_tx_add_input(&self, their_node_id: &PublicKey, msg: &msgs::TxAddInput) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_add_output(&self, their_node_id: &PublicKey, msg: &msgs::TxAddOutput) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_remove_input(&self, their_node_id: &PublicKey, msg: &msgs::TxRemoveInput) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_remove_output(&self, their_node_id: &PublicKey, msg: &msgs::TxRemoveOutput) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_complete(&self, their_node_id: &PublicKey, msg: &msgs::TxComplete) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_signatures(&self, their_node_id: &PublicKey, msg: &msgs::TxSignatures) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_init_rbf(&self, their_node_id: &PublicKey, msg: &msgs::TxInitRbf) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_ack_rbf(&self, their_node_id: &PublicKey, msg: &msgs::TxAckRbf) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
+
+ fn handle_tx_abort(&self, their_node_id: &PublicKey, msg: &msgs::TxAbort) {
+ ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
+ }
}
+
impl Deref for ErroringMessageHandler {
type Target = ErroringMessageHandler;
fn deref(&self) -> &Self { self }
}
/// Provides references to trait impls which handle different types of messages.
-pub struct MessageHandler<CM: Deref, RM: Deref, OM: Deref> where
- CM::Target: ChannelMessageHandler,
- RM::Target: RoutingMessageHandler,
- OM::Target: OnionMessageHandler,
+pub struct MessageHandler<CM: Deref, RM: Deref, OM: Deref, CustomM: Deref> where
+ CM::Target: ChannelMessageHandler,
+ RM::Target: RoutingMessageHandler,
+ OM::Target: OnionMessageHandler,
+ CustomM::Target: CustomMessageHandler,
{
/// A message handler which handles messages specific to channels. Usually this is just a
/// [`ChannelManager`] object or an [`ErroringMessageHandler`].
/// [`P2PGossipSync`]: crate::routing::gossip::P2PGossipSync
pub route_handler: RM,
- /// A message handler which handles onion messages. For now, this can only be an
- /// [`IgnoringMessageHandler`].
+ /// A message handler which handles onion messages. This should generally be an
+ /// [`OnionMessenger`], but can also be an [`IgnoringMessageHandler`].
+ ///
+ /// [`OnionMessenger`]: crate::onion_message::OnionMessenger
pub onion_message_handler: OM,
+
+ /// A message handler which handles custom messages. The only LDK-provided implementation is
+ /// [`IgnoringMessageHandler`].
+ pub custom_message_handler: CustomM,
}
/// Provides an object which can be used to send data to and which uniquely identifies a connection
sync_status: InitSyncTracker,
msgs_sent_since_pong: usize,
- awaiting_pong_timer_tick_intervals: i8,
+ awaiting_pong_timer_tick_intervals: i64,
received_message_since_timer_tick: bool,
sent_gossip_timestamp_filter: bool,
/// This is not exported to bindings users as general type aliases don't make sense in bindings.
pub type SimpleRefPeerManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, SD, M, T, F, C, L> = PeerManager<SD, SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'm, M, T, F, L>, &'f P2PGossipSync<&'g NetworkGraph<&'f L>, &'h C, &'f L>, &'i SimpleRefOnionMessenger<'j, 'k, L>, &'f L, IgnoringMessageHandler, &'c KeysManager>;
+
+/// A generic trait which is implemented for all [`PeerManager`]s. This makes bounding functions or
+/// structs on any [`PeerManager`] much simpler as only this trait is needed as a bound, rather
+/// than the full set of bounds on [`PeerManager`] itself.
+#[allow(missing_docs)]
+pub trait APeerManager {
+ type Descriptor: SocketDescriptor;
+ type CMT: ChannelMessageHandler + ?Sized;
+ type CM: Deref<Target=Self::CMT>;
+ type RMT: RoutingMessageHandler + ?Sized;
+ type RM: Deref<Target=Self::RMT>;
+ type OMT: OnionMessageHandler + ?Sized;
+ type OM: Deref<Target=Self::OMT>;
+ type LT: Logger + ?Sized;
+ type L: Deref<Target=Self::LT>;
+ type CMHT: CustomMessageHandler + ?Sized;
+ type CMH: Deref<Target=Self::CMHT>;
+ type NST: NodeSigner + ?Sized;
+ type NS: Deref<Target=Self::NST>;
+ /// Gets a reference to the underlying [`PeerManager`].
+ fn as_ref(&self) -> &PeerManager<Self::Descriptor, Self::CM, Self::RM, Self::OM, Self::L, Self::CMH, Self::NS>;
+}
+
+impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CMH: Deref, NS: Deref>
+APeerManager for PeerManager<Descriptor, CM, RM, OM, L, CMH, NS> where
+ CM::Target: ChannelMessageHandler,
+ RM::Target: RoutingMessageHandler,
+ OM::Target: OnionMessageHandler,
+ L::Target: Logger,
+ CMH::Target: CustomMessageHandler,
+ NS::Target: NodeSigner,
+{
+ type Descriptor = Descriptor;
+ type CMT = <CM as Deref>::Target;
+ type CM = CM;
+ type RMT = <RM as Deref>::Target;
+ type RM = RM;
+ type OMT = <OM as Deref>::Target;
+ type OM = OM;
+ type LT = <L as Deref>::Target;
+ type L = L;
+ type CMHT = <CMH as Deref>::Target;
+ type CMH = CMH;
+ type NST = <NS as Deref>::Target;
+ type NS = NS;
+ fn as_ref(&self) -> &PeerManager<Descriptor, CM, RM, OM, L, CMH, NS> { self }
+}
+
/// A PeerManager manages a set of peers, described by their [`SocketDescriptor`] and marshalls
/// socket events into messages which it passes on to its [`MessageHandler`].
///
L::Target: Logger,
CMH::Target: CustomMessageHandler,
NS::Target: NodeSigner {
- message_handler: MessageHandler<CM, RM, OM>,
+ message_handler: MessageHandler<CM, RM, OM, CMH>,
/// Connection state for each connected peer - we have an outer read-write lock which is taken
/// as read while we're doing processing for a peer and taken write when a peer is being added
/// or removed.
last_node_announcement_serial: AtomicU32,
ephemeral_key_midstate: Sha256Engine,
- custom_message_handler: CMH,
peer_counter: AtomicCounter,
chan_handler: channel_message_handler,
route_handler: IgnoringMessageHandler{},
onion_message_handler,
- }, current_time, ephemeral_random_data, logger, IgnoringMessageHandler{}, node_signer)
+ custom_message_handler: IgnoringMessageHandler{},
+ }, current_time, ephemeral_random_data, logger, node_signer)
}
}
chan_handler: ErroringMessageHandler::new(),
route_handler: routing_message_handler,
onion_message_handler: IgnoringMessageHandler{},
- }, current_time, ephemeral_random_data, logger, IgnoringMessageHandler{}, node_signer)
+ custom_message_handler: IgnoringMessageHandler{},
+ }, current_time, ephemeral_random_data, logger, node_signer)
}
}
/// incremented irregularly internally. In general it is best to simply use the current UNIX
/// timestamp, however if it is not available a persistent counter that increases once per
/// minute should suffice.
- pub fn new(message_handler: MessageHandler<CM, RM, OM>, current_time: u32, ephemeral_random_data: &[u8; 32], logger: L, custom_message_handler: CMH, node_signer: NS) -> Self {
+ pub fn new(message_handler: MessageHandler<CM, RM, OM, CMH>, current_time: u32, ephemeral_random_data: &[u8; 32], logger: L, node_signer: NS) -> Self {
let mut ephemeral_key_midstate = Sha256::engine();
ephemeral_key_midstate.input(ephemeral_random_data);
gossip_processing_backlog_lifted: AtomicBool::new(false),
last_node_announcement_serial: AtomicU32::new(current_time),
logger,
- custom_message_handler,
node_signer,
secp_ctx,
}
SecretKey::from_slice(&Sha256::from_engine(ephemeral_hash).into_inner()).expect("You broke SHA-256!")
}
+ fn init_features(&self, their_node_id: &PublicKey) -> InitFeatures {
+ self.message_handler.chan_handler.provided_init_features(their_node_id)
+ | self.message_handler.route_handler.provided_init_features(their_node_id)
+ | self.message_handler.onion_message_handler.provided_init_features(their_node_id)
+ | self.message_handler.custom_message_handler.provided_init_features(their_node_id)
+ }
+
/// Indicates a new outbound connection has been established to a node with the given `node_id`
/// and an optional remote network address.
///
peer.set_their_node_id(their_node_id);
insert_node_id!();
- let features = self.message_handler.chan_handler.provided_init_features(&their_node_id)
- .or(self.message_handler.route_handler.provided_init_features(&their_node_id))
- .or(self.message_handler.onion_message_handler.provided_init_features(&their_node_id));
+ let features = self.init_features(&their_node_id);
let resp = msgs::Init { features, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
peer.pending_read_is_header = true;
peer.set_their_node_id(their_node_id);
insert_node_id!();
- let features = self.message_handler.chan_handler.provided_init_features(&their_node_id)
- .or(self.message_handler.route_handler.provided_init_features(&their_node_id))
- .or(self.message_handler.onion_message_handler.provided_init_features(&their_node_id));
+ let features = self.init_features(&their_node_id);
let resp = msgs::Init { features, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
peer.pending_read_is_header = true;
let mut reader = io::Cursor::new(&msg_data[..]);
- let message_result = wire::read(&mut reader, &*self.custom_message_handler);
+ let message_result = wire::read(&mut reader, &*self.message_handler.custom_message_handler);
let message = match message_result {
Ok(x) => x,
Err(e) => {
// Need an Init as first message
if let wire::Message::Init(msg) = message {
- if msg.features.requires_unknown_bits() {
- log_debug!(self.logger, "Peer features required unknown version bits");
+ let our_features = self.init_features(&their_node_id);
+ if msg.features.requires_unknown_bits_from(&our_features) {
+ log_debug!(self.logger, "Peer requires features unknown to us");
+ return Err(PeerHandleError { }.into());
+ }
+
+ if our_features.requires_unknown_bits_from(&msg.features) {
+ log_debug!(self.logger, "We require features unknown to our peer");
return Err(PeerHandleError { }.into());
}
+
if peer_lock.their_features.is_some() {
return Err(PeerHandleError { }.into());
}
wire::Message::OpenChannel(msg) => {
self.message_handler.chan_handler.handle_open_channel(&their_node_id, &msg);
},
+ wire::Message::OpenChannelV2(msg) => {
+ self.message_handler.chan_handler.handle_open_channel_v2(&their_node_id, &msg);
+ },
wire::Message::AcceptChannel(msg) => {
self.message_handler.chan_handler.handle_accept_channel(&their_node_id, &msg);
},
+ wire::Message::AcceptChannelV2(msg) => {
+ self.message_handler.chan_handler.handle_accept_channel_v2(&their_node_id, &msg);
+ },
wire::Message::FundingCreated(msg) => {
self.message_handler.chan_handler.handle_funding_created(&their_node_id, &msg);
self.message_handler.chan_handler.handle_channel_ready(&their_node_id, &msg);
},
+ // Interactive transaction construction messages:
+ wire::Message::TxAddInput(msg) => {
+ self.message_handler.chan_handler.handle_tx_add_input(&their_node_id, &msg);
+ },
+ wire::Message::TxAddOutput(msg) => {
+ self.message_handler.chan_handler.handle_tx_add_output(&their_node_id, &msg);
+ },
+ wire::Message::TxRemoveInput(msg) => {
+ self.message_handler.chan_handler.handle_tx_remove_input(&their_node_id, &msg);
+ },
+ wire::Message::TxRemoveOutput(msg) => {
+ self.message_handler.chan_handler.handle_tx_remove_output(&their_node_id, &msg);
+ },
+ wire::Message::TxComplete(msg) => {
+ self.message_handler.chan_handler.handle_tx_complete(&their_node_id, &msg);
+ },
+ wire::Message::TxSignatures(msg) => {
+ self.message_handler.chan_handler.handle_tx_signatures(&their_node_id, &msg);
+ },
+ wire::Message::TxInitRbf(msg) => {
+ self.message_handler.chan_handler.handle_tx_init_rbf(&their_node_id, &msg);
+ },
+ wire::Message::TxAckRbf(msg) => {
+ self.message_handler.chan_handler.handle_tx_ack_rbf(&their_node_id, &msg);
+ },
+ wire::Message::TxAbort(msg) => {
+ self.message_handler.chan_handler.handle_tx_abort(&their_node_id, &msg);
+ }
+
wire::Message::Shutdown(msg) => {
self.message_handler.chan_handler.handle_shutdown(&their_node_id, &msg);
},
log_trace!(self.logger, "Received unknown odd message of type {}, ignoring", type_id);
},
wire::Message::Custom(custom) => {
- self.custom_message_handler.handle_custom_message(custom, &their_node_id)?;
+ self.message_handler.custom_message_handler.handle_custom_message(custom, &their_node_id)?;
},
};
Ok(should_forward)
log_bytes!(msg.temporary_channel_id));
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
},
+ MessageSendEvent::SendAcceptChannelV2 { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendAcceptChannelV2 event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.temporary_channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
MessageSendEvent::SendOpenChannel { ref node_id, ref msg } => {
log_debug!(self.logger, "Handling SendOpenChannel event in peer_handler for node {} for channel {}",
log_pubkey!(node_id),
log_bytes!(msg.temporary_channel_id));
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
},
+ MessageSendEvent::SendOpenChannelV2 { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendOpenChannelV2 event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.temporary_channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
MessageSendEvent::SendFundingCreated { ref node_id, ref msg } => {
log_debug!(self.logger, "Handling SendFundingCreated event in peer_handler for node {} for channel {} (which becomes {})",
log_pubkey!(node_id),
log_bytes!(msg.channel_id));
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
},
+ MessageSendEvent::SendTxAddInput { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxAddInput event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxAddOutput { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxAddOutput event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxRemoveInput { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxRemoveInput event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxRemoveOutput { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxRemoveOutput event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxComplete { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxComplete event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxSignatures event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxInitRbf { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxInitRbf event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxAckRbf { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxAckRbf event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
+ MessageSendEvent::SendTxAbort { ref node_id, ref msg } => {
+ log_debug!(self.logger, "Handling SendTxAbort event in peer_handler for node {} for channel {}",
+ log_pubkey!(node_id),
+ log_bytes!(msg.channel_id));
+ self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
+ },
MessageSendEvent::SendAnnouncementSignatures { ref node_id, ref msg } => {
log_debug!(self.logger, "Handling SendAnnouncementSignatures event in peer_handler for node {} for channel {})",
log_pubkey!(node_id),
}
}
- for (node_id, msg) in self.custom_message_handler.get_and_clear_pending_msg() {
+ for (node_id, msg) in self.message_handler.custom_message_handler.get_and_clear_pending_msg() {
if peers_to_disconnect.get(&node_id).is_some() { continue; }
self.enqueue_message(&mut *get_peer_for_forwarding!(&node_id), &msg);
}
addresses.sort_by_key(|addr| addr.get_id());
let features = self.message_handler.chan_handler.provided_node_features()
- .or(self.message_handler.route_handler.provided_node_features())
- .or(self.message_handler.onion_message_handler.provided_node_features());
+ | self.message_handler.route_handler.provided_node_features()
+ | self.message_handler.onion_message_handler.provided_node_features()
+ | self.message_handler.custom_message_handler.provided_node_features();
let announcement = msgs::UnsignedNodeAnnouncement {
features,
timestamp: self.last_node_announcement_serial.fetch_add(1, Ordering::AcqRel),
#[cfg(test)]
mod tests {
- use crate::chain::keysinterface::{NodeSigner, Recipient};
+ use crate::sign::{NodeSigner, Recipient};
use crate::events;
+ use crate::io;
+ use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::peer_channel_encryptor::PeerChannelEncryptor;
- use crate::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler, filter_addresses};
+ use crate::ln::peer_handler::{CustomMessageHandler, PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler, filter_addresses};
use crate::ln::{msgs, wire};
- use crate::ln::msgs::NetAddress;
+ use crate::ln::msgs::{LightningError, NetAddress};
use crate::util::test_utils;
- use bitcoin::secp256k1::SecretKey;
+ use bitcoin::secp256k1::{PublicKey, SecretKey};
use crate::prelude::*;
use crate::sync::{Arc, Mutex};
+ use core::convert::Infallible;
use core::sync::atomic::{AtomicBool, Ordering};
#[derive(Clone)]
struct PeerManagerCfg {
chan_handler: test_utils::TestChannelMessageHandler,
routing_handler: test_utils::TestRoutingMessageHandler,
+ custom_handler: TestCustomMessageHandler,
logger: test_utils::TestLogger,
node_signer: test_utils::TestNodeSigner,
}
+ struct TestCustomMessageHandler {
+ features: InitFeatures,
+ }
+
+ impl wire::CustomMessageReader for TestCustomMessageHandler {
+ type CustomMessage = Infallible;
+ fn read<R: io::Read>(&self, _: u16, _: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError> {
+ Ok(None)
+ }
+ }
+
+ impl CustomMessageHandler for TestCustomMessageHandler {
+ fn handle_custom_message(&self, _: Infallible, _: &PublicKey) -> Result<(), LightningError> {
+ unreachable!();
+ }
+
+ fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> { Vec::new() }
+
+ fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
+
+ fn provided_init_features(&self, _: &PublicKey) -> InitFeatures {
+ self.features.clone()
+ }
+ }
+
fn create_peermgr_cfgs(peer_count: usize) -> Vec<PeerManagerCfg> {
let mut cfgs = Vec::new();
for i in 0..peer_count {
let node_secret = SecretKey::from_slice(&[42 + i as u8; 32]).unwrap();
+ let features = {
+ let mut feature_bits = vec![0u8; 33];
+ feature_bits[32] = 0b00000001;
+ InitFeatures::from_le_bytes(feature_bits)
+ };
+ cfgs.push(
+ PeerManagerCfg{
+ chan_handler: test_utils::TestChannelMessageHandler::new(),
+ logger: test_utils::TestLogger::new(),
+ routing_handler: test_utils::TestRoutingMessageHandler::new(),
+ custom_handler: TestCustomMessageHandler { features },
+ node_signer: test_utils::TestNodeSigner::new(node_secret),
+ }
+ );
+ }
+
+ cfgs
+ }
+
+ fn create_incompatible_peermgr_cfgs(peer_count: usize) -> Vec<PeerManagerCfg> {
+ let mut cfgs = Vec::new();
+ for i in 0..peer_count {
+ let node_secret = SecretKey::from_slice(&[42 + i as u8; 32]).unwrap();
+ let features = {
+ let mut feature_bits = vec![0u8; 33 + i + 1];
+ feature_bits[33 + i] = 0b00000001;
+ InitFeatures::from_le_bytes(feature_bits)
+ };
cfgs.push(
PeerManagerCfg{
chan_handler: test_utils::TestChannelMessageHandler::new(),
logger: test_utils::TestLogger::new(),
routing_handler: test_utils::TestRoutingMessageHandler::new(),
+ custom_handler: TestCustomMessageHandler { features },
node_signer: test_utils::TestNodeSigner::new(node_secret),
}
);
cfgs
}
- fn create_network<'a>(peer_count: usize, cfgs: &'a Vec<PeerManagerCfg>) -> Vec<PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, IgnoringMessageHandler, &'a test_utils::TestNodeSigner>> {
+ fn create_network<'a>(peer_count: usize, cfgs: &'a Vec<PeerManagerCfg>) -> Vec<PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, &'a TestCustomMessageHandler, &'a test_utils::TestNodeSigner>> {
let mut peers = Vec::new();
for i in 0..peer_count {
let ephemeral_bytes = [i as u8; 32];
- let msg_handler = MessageHandler { chan_handler: &cfgs[i].chan_handler, route_handler: &cfgs[i].routing_handler, onion_message_handler: IgnoringMessageHandler {} };
- let peer = PeerManager::new(msg_handler, 0, &ephemeral_bytes, &cfgs[i].logger, IgnoringMessageHandler {}, &cfgs[i].node_signer);
+ let msg_handler = MessageHandler {
+ chan_handler: &cfgs[i].chan_handler, route_handler: &cfgs[i].routing_handler,
+ onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: &cfgs[i].custom_handler
+ };
+ let peer = PeerManager::new(msg_handler, 0, &ephemeral_bytes, &cfgs[i].logger, &cfgs[i].node_signer);
peers.push(peer);
}
peers
}
- fn establish_connection<'a>(peer_a: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, IgnoringMessageHandler, &'a test_utils::TestNodeSigner>, peer_b: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, IgnoringMessageHandler, &'a test_utils::TestNodeSigner>) -> (FileDescriptor, FileDescriptor) {
+ fn establish_connection<'a>(peer_a: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, &'a TestCustomMessageHandler, &'a test_utils::TestNodeSigner>, peer_b: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, &'a TestCustomMessageHandler, &'a test_utils::TestNodeSigner>) -> (FileDescriptor, FileDescriptor) {
let id_a = peer_a.node_signer.get_node_id(Recipient::Node).unwrap();
let mut fd_a = FileDescriptor {
fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())),
thrd_b.join().unwrap();
}
+ #[test]
+ fn test_incompatible_peers() {
+ let cfgs = create_peermgr_cfgs(2);
+ let incompatible_cfgs = create_incompatible_peermgr_cfgs(2);
+
+ let peers = create_network(2, &cfgs);
+ let incompatible_peers = create_network(2, &incompatible_cfgs);
+ let peer_pairs = [(&peers[0], &incompatible_peers[0]), (&incompatible_peers[1], &peers[1])];
+ for (peer_a, peer_b) in peer_pairs.iter() {
+ let id_a = peer_a.node_signer.get_node_id(Recipient::Node).unwrap();
+ let mut fd_a = FileDescriptor {
+ fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())),
+ disconnect: Arc::new(AtomicBool::new(false)),
+ };
+ let addr_a = NetAddress::IPv4{addr: [127, 0, 0, 1], port: 1000};
+ let mut fd_b = FileDescriptor {
+ fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())),
+ disconnect: Arc::new(AtomicBool::new(false)),
+ };
+ let addr_b = NetAddress::IPv4{addr: [127, 0, 0, 1], port: 1001};
+ let initial_data = peer_b.new_outbound_connection(id_a, fd_b.clone(), Some(addr_a.clone())).unwrap();
+ peer_a.new_inbound_connection(fd_a.clone(), Some(addr_b.clone())).unwrap();
+ assert_eq!(peer_a.read_event(&mut fd_a, &initial_data).unwrap(), false);
+ peer_a.process_events();
+
+ let a_data = fd_a.outbound_data.lock().unwrap().split_off(0);
+ assert_eq!(peer_b.read_event(&mut fd_b, &a_data).unwrap(), false);
+
+ peer_b.process_events();
+ let b_data = fd_b.outbound_data.lock().unwrap().split_off(0);
+
+ // Should fail because of unknown required features
+ assert!(peer_a.read_event(&mut fd_a, &b_data).is_err());
+ }
+ }
+
#[test]
fn test_disconnect_peer() {
// Simple test which builds a network of PeerManager, connects and brings them to NoiseState::Finished and
//! LSP).
use crate::chain::ChannelMonitorUpdateStatus;
-use crate::chain::keysinterface::NodeSigner;
+use crate::sign::NodeSigner;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
use crate::ln::channelmanager::{ChannelManager, MIN_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields};
use crate::routing::gossip::RoutingFees;
}]);
let last_hops = vec![route_hint];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_features(nodes[2].node.invoice_features())
- .with_route_hints(last_hops);
- let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000, TEST_FINAL_CLTV);
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_route_hints(last_hops).unwrap();
+ let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
nodes[0].node.send_payment_with_route(&route, our_payment_hash,
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)).unwrap();
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_features(nodes[2].node.invoice_features())
- .with_route_hints(hop_hints);
- let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000, 42);
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_route_hints(hop_hints).unwrap();
+ let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
nodes[0].node.send_payment_with_route(&route, payment_hash,
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_features(nodes[2].node.invoice_features())
- .with_route_hints(hop_hints.clone());
- let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000, 42);
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_route_hints(hop_hints.clone()).unwrap();
+ let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
nodes[0].node.send_payment_with_route(&route, payment_hash,
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
hop_hints[0].0[0].short_channel_id = last_hop[0].short_channel_id.unwrap();
let payment_params_2 = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_features(nodes[2].node.invoice_features())
- .with_route_hints(hop_hints);
- let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000, 42);
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_route_hints(hop_hints).unwrap();
+ let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000);
assert_eq!(route_2.paths[0].hops[1].short_channel_id, last_hop[0].short_channel_id.unwrap());
nodes[0].node.send_payment_with_route(&route_2, payment_hash_2,
RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap();
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_features(nodes[2].node.invoice_features())
- .with_route_hints(hop_hints);
- let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000, 42);
+ .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_route_hints(hop_hints).unwrap();
+ let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap());
route.paths[0].hops[1].fee_msat = 10_000_000; // Overshoot the last channel's value
use crate::chain::{ChannelMonitorUpdateStatus, Watch};
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
use crate::chain::channelmonitor::ChannelMonitor;
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use crate::chain::transaction::OutPoint;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
use crate::ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, PaymentId, RecipientOnionFields};
use crate::util::ser::Writeable;
use crate::util::string::UntrustedString;
-use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::opcodes;
use bitcoin::secp256k1::Secp256k1;
use crate::prelude::*;
-use bitcoin::hashes::Hash;
-use bitcoin::TxMerkleNode;
use crate::ln::functional_test_utils::*;
check_added_monitors!(nodes[2], 1);
get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
- let mut header = BlockHeader { version: 0x2000_0000, prev_blockhash: nodes[2].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 };
let claim_txn = if local_commitment {
// Broadcast node 1 commitment txn to broadcast the HTLC-Timeout
let node_1_commitment_txn = get_local_commitment_txn!(nodes[1], chan_2.2);
check_spends!(node_1_commitment_txn[1], node_1_commitment_txn[0]);
// Give node 2 node 1's transactions and get its response (claiming the HTLC instead).
- connect_block(&nodes[2], &Block { header, txdata: node_1_commitment_txn.clone() });
+ connect_block(&nodes[2], &create_dummy_block(nodes[2].best_block_hash(), 42, node_1_commitment_txn.clone()));
check_added_monitors!(nodes[2], 1);
check_closed_broadcast!(nodes[2], true); // We should get a BroadcastChannelUpdate (and *only* a BroadcstChannelUpdate)
check_closed_event!(nodes[2], 1, ClosureReason::CommitmentTxConfirmed);
// Make sure node 1's height is the same as the !local_commitment case
connect_blocks(&nodes[1], 1);
// Confirm node 1's commitment txn (and HTLC-Timeout) on node 1
- header.prev_blockhash = nodes[1].best_block_hash();
- connect_block(&nodes[1], &Block { header, txdata: node_1_commitment_txn.clone() });
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, node_1_commitment_txn.clone()));
// ...but return node 1's commitment tx in case claim is set and we're preparing to reorg
vec![node_1_commitment_txn[0].clone(), node_2_commitment_txn[0].clone()]
// Disconnect Node 1's HTLC-Timeout which was connected above
disconnect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
- let block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: claim_txn,
- };
- connect_block(&nodes[1], &block);
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, claim_txn));
// ChannelManager only polls chain::Watch::release_pending_monitor_events when we
// probe it for events, so we probe non-message events here (which should just be the
expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), true, true);
} else {
// Confirm the timeout tx and check that we fail the HTLC backwards
- let block = Block {
- header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
- txdata: vec![],
- };
- connect_block(&nodes[1], &block);
+ connect_block(&nodes[1], &create_dummy_block(nodes[1].best_block_hash(), 42, Vec::new()));
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id: chan_2.2 }]);
}
if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
- Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
check_spends!(spend_tx, remote_txn_b[0]);
}
if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
- Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
check_spends!(spend_tx, remote_txn_a[0]);
}
}
//! Tests of our shutdown and closing_signed negotiation logic.
-use crate::chain::keysinterface::{EntropySource, SignerProvider};
+use crate::sign::{EntropySource, SignerProvider};
use crate::chain::transaction::OutPoint;
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
use crate::ln::channelmanager::{self, PaymentSendFailure, PaymentId, RecipientOnionFields};
use regex;
use core::default::Default;
+use std::convert::TryFrom;
use crate::ln::functional_test_utils::*;
-use crate::ln::msgs::OptionalField::Present;
#[test]
fn pre_funding_lock_shutdown_test() {
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[0]);
- let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_features(nodes[1].node.invoice_features());
- let route_1 = get_route(&nodes[0].node.get_our_node_id(), &payment_params_1, &nodes[0].network_graph.read_only(), None, 100000, TEST_FINAL_CLTV, &logger, &scorer, &random_seed_bytes).unwrap();
- let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), TEST_FINAL_CLTV).with_features(nodes[0].node.invoice_features());
- let route_2 = get_route(&nodes[1].node.get_our_node_id(), &payment_params_2, &nodes[1].network_graph.read_only(), None, 100000, TEST_FINAL_CLTV, &logger, &scorer, &random_seed_bytes).unwrap();
+ let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let route_1 = get_route(&nodes[0].node.get_our_node_id(), &payment_params_1, &nodes[0].network_graph.read_only(), None, 100000, &logger, &scorer, &(), &random_seed_bytes).unwrap();
+ let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ let route_2 = get_route(&nodes[1].node.get_our_node_id(), &payment_params_2, &nodes[1].network_graph.read_only(), None, 100000, &logger, &scorer, &(), &random_seed_bytes).unwrap();
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route_1, payment_hash,
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
), true, APIError::ChannelUnavailable {..}, {});
// Check script when handling an open_channel message
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
let mut open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
- open_channel.shutdown_scriptpubkey = Present(anysegwit_shutdown_script.clone());
+ open_channel.shutdown_scriptpubkey = Some(anysegwit_shutdown_script.clone());
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel);
let events = nodes[1].node.get_and_clear_pending_msg_events();
let open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel);
let mut accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
- accept_channel.shutdown_scriptpubkey = Present(anysegwit_shutdown_script.clone());
+ accept_channel.shutdown_scriptpubkey = Some(anysegwit_shutdown_script.clone());
nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel);
let events = nodes[0].node.get_and_clear_pending_msg_events();
// Use a segwit v0 script with an unsupported witness program
let mut open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
- open_channel.shutdown_scriptpubkey = Present(Builder::new().push_int(0)
+ open_channel.shutdown_scriptpubkey = Some(Builder::new().push_int(0)
.push_slice(&[0, 0])
.into_script());
nodes[0].node.handle_open_channel(&nodes[1].node.get_our_node_id(), &open_channel);
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
// Check that using an unsupported shutdown script fails and a supported one succeeds.
- let supported_shutdown_script = chanmon_cfgs[1].keys_manager.get_shutdown_scriptpubkey();
+ let supported_shutdown_script = chanmon_cfgs[1].keys_manager.get_shutdown_scriptpubkey().unwrap();
let unsupported_shutdown_script =
ShutdownScript::new_witness_program(WitnessVersion::V16, &[0, 40]).unwrap();
chanmon_cfgs[1].keys_manager
"Got a nonstandard scriptpubkey (00020000) from remote peer");
}
+#[test]
+fn test_user_shutdown_script() {
+ let mut config = test_default_channel_config();
+ config.channel_handshake_config.announced_channel = true;
+ config.channel_handshake_limits.force_announced_channel_preference = false;
+ config.channel_handshake_config.commit_upfront_shutdown_pubkey = false;
+ let user_cfgs = [None, Some(config), None];
+ 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, &user_cfgs);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ // Segwit v0 script of the form OP_0 <20-byte hash>
+ let script = Builder::new().push_int(0)
+ .push_slice(&[0; 20])
+ .into_script();
+
+ let shutdown_script = ShutdownScript::try_from(script.clone()).unwrap();
+
+ let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
+ nodes[1].node.close_channel_with_feerate_and_script(&OutPoint { txid: chan.3.txid(), index: 0 }.to_channel_id(), &nodes[0].node.get_our_node_id(), None, Some(shutdown_script)).unwrap();
+ check_added_monitors!(nodes[1], 1);
+
+ let mut node_0_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
+
+ assert_eq!(node_0_shutdown.scriptpubkey, script);
+}
+
+#[test]
+fn test_already_set_user_shutdown_script() {
+ let mut config = test_default_channel_config();
+ config.channel_handshake_config.announced_channel = true;
+ config.channel_handshake_limits.force_announced_channel_preference = false;
+ let user_cfgs = [None, Some(config), None];
+ 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, &user_cfgs);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ // Segwit v0 script of the form OP_0 <20-byte hash>
+ let script = Builder::new().push_int(0)
+ .push_slice(&[0; 20])
+ .into_script();
+
+ let shutdown_script = ShutdownScript::try_from(script).unwrap();
+
+ let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
+ let result = nodes[1].node.close_channel_with_feerate_and_script(&OutPoint { txid: chan.3.txid(), index: 0 }.to_channel_id(), &nodes[0].node.get_our_node_id(), None, Some(shutdown_script));
+
+ assert_eq!(result, Err(APIError::APIMisuseError { err: "Cannot override shutdown script for a channel with one already set".to_string() }));
+}
+
#[derive(PartialEq)]
enum TimeoutStep {
AfterShutdown,
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
let chan_id = OutPoint { txid: chan.3.txid(), index: 0 }.to_channel_id();
- nodes[0].node.close_channel_with_target_feerate(&chan_id, &nodes[1].node.get_our_node_id(), 253 * 10).unwrap();
+ nodes[0].node.close_channel_with_feerate_and_script(&chan_id, &nodes[1].node.get_our_node_id(), Some(253 * 10), None).unwrap();
let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id());
- nodes[1].node.close_channel_with_target_feerate(&chan_id, &nodes[0].node.get_our_node_id(), 253 * 5).unwrap();
+ nodes[1].node.close_channel_with_feerate_and_script(&chan_id, &nodes[0].node.get_our_node_id(), Some(253 * 5), None).unwrap();
let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &node_0_shutdown);
Ping(msgs::Ping),
Pong(msgs::Pong),
OpenChannel(msgs::OpenChannel),
+ OpenChannelV2(msgs::OpenChannelV2),
AcceptChannel(msgs::AcceptChannel),
+ AcceptChannelV2(msgs::AcceptChannelV2),
FundingCreated(msgs::FundingCreated),
FundingSigned(msgs::FundingSigned),
+ TxAddInput(msgs::TxAddInput),
+ TxAddOutput(msgs::TxAddOutput),
+ TxRemoveInput(msgs::TxRemoveInput),
+ TxRemoveOutput(msgs::TxRemoveOutput),
+ TxComplete(msgs::TxComplete),
+ TxSignatures(msgs::TxSignatures),
+ TxInitRbf(msgs::TxInitRbf),
+ TxAckRbf(msgs::TxAckRbf),
+ TxAbort(msgs::TxAbort),
ChannelReady(msgs::ChannelReady),
Shutdown(msgs::Shutdown),
ClosingSigned(msgs::ClosingSigned),
&Message::Ping(ref msg) => msg.type_id(),
&Message::Pong(ref msg) => msg.type_id(),
&Message::OpenChannel(ref msg) => msg.type_id(),
+ &Message::OpenChannelV2(ref msg) => msg.type_id(),
&Message::AcceptChannel(ref msg) => msg.type_id(),
+ &Message::AcceptChannelV2(ref msg) => msg.type_id(),
&Message::FundingCreated(ref msg) => msg.type_id(),
&Message::FundingSigned(ref msg) => msg.type_id(),
+ &Message::TxAddInput(ref msg) => msg.type_id(),
+ &Message::TxAddOutput(ref msg) => msg.type_id(),
+ &Message::TxRemoveInput(ref msg) => msg.type_id(),
+ &Message::TxRemoveOutput(ref msg) => msg.type_id(),
+ &Message::TxComplete(ref msg) => msg.type_id(),
+ &Message::TxSignatures(ref msg) => msg.type_id(),
+ &Message::TxInitRbf(ref msg) => msg.type_id(),
+ &Message::TxAckRbf(ref msg) => msg.type_id(),
+ &Message::TxAbort(ref msg) => msg.type_id(),
&Message::ChannelReady(ref msg) => msg.type_id(),
&Message::Shutdown(ref msg) => msg.type_id(),
&Message::ClosingSigned(ref msg) => msg.type_id(),
///
/// # Errors
///
-/// Returns an error if the message payload code not be decoded as the specified type.
+/// Returns an error if the message payload could not be decoded as the specified type.
pub(crate) fn read<R: io::Read, T, H: core::ops::Deref>(buffer: &mut R, custom_reader: H)
-> Result<Message<T>, (msgs::DecodeError, Option<u16>)> where
T: core::fmt::Debug + Type + Writeable,
msgs::OpenChannel::TYPE => {
Ok(Message::OpenChannel(Readable::read(buffer)?))
},
+ msgs::OpenChannelV2::TYPE => {
+ Ok(Message::OpenChannelV2(Readable::read(buffer)?))
+ },
msgs::AcceptChannel::TYPE => {
Ok(Message::AcceptChannel(Readable::read(buffer)?))
},
+ msgs::AcceptChannelV2::TYPE => {
+ Ok(Message::AcceptChannelV2(Readable::read(buffer)?))
+ },
msgs::FundingCreated::TYPE => {
Ok(Message::FundingCreated(Readable::read(buffer)?))
},
msgs::FundingSigned::TYPE => {
Ok(Message::FundingSigned(Readable::read(buffer)?))
},
+ msgs::TxAddInput::TYPE => {
+ Ok(Message::TxAddInput(Readable::read(buffer)?))
+ },
+ msgs::TxAddOutput::TYPE => {
+ Ok(Message::TxAddOutput(Readable::read(buffer)?))
+ },
+ msgs::TxRemoveInput::TYPE => {
+ Ok(Message::TxRemoveInput(Readable::read(buffer)?))
+ },
+ msgs::TxRemoveOutput::TYPE => {
+ Ok(Message::TxRemoveOutput(Readable::read(buffer)?))
+ },
+ msgs::TxComplete::TYPE => {
+ Ok(Message::TxComplete(Readable::read(buffer)?))
+ },
+ msgs::TxSignatures::TYPE => {
+ Ok(Message::TxSignatures(Readable::read(buffer)?))
+ },
+ msgs::TxInitRbf::TYPE => {
+ Ok(Message::TxInitRbf(Readable::read(buffer)?))
+ },
+ msgs::TxAckRbf::TYPE => {
+ Ok(Message::TxAckRbf(Readable::read(buffer)?))
+ },
+ msgs::TxAbort::TYPE => {
+ Ok(Message::TxAbort(Readable::read(buffer)?))
+ },
msgs::ChannelReady::TYPE => {
Ok(Message::ChannelReady(Readable::read(buffer)?))
},
const TYPE: u16 = 39;
}
+impl Encode for msgs::OpenChannelV2 {
+ const TYPE: u16 = 64;
+}
+
+impl Encode for msgs::AcceptChannelV2 {
+ const TYPE: u16 = 65;
+}
+
+impl Encode for msgs::TxAddInput {
+ const TYPE: u16 = 66;
+}
+
+impl Encode for msgs::TxAddOutput {
+ const TYPE: u16 = 67;
+}
+
+impl Encode for msgs::TxRemoveInput {
+ const TYPE: u16 = 68;
+}
+
+impl Encode for msgs::TxRemoveOutput {
+ const TYPE: u16 = 69;
+}
+
+impl Encode for msgs::TxComplete {
+ const TYPE: u16 = 70;
+}
+
+impl Encode for msgs::TxSignatures {
+ const TYPE: u16 = 71;
+}
+
+impl Encode for msgs::TxInitRbf {
+ const TYPE: u16 = 72;
+}
+
+impl Encode for msgs::TxAckRbf {
+ const TYPE: u16 = 73;
+}
+
+impl Encode for msgs::TxAbort {
+ const TYPE: u16 = 74;
+}
+
impl Encode for msgs::OnionMessage {
const TYPE: u16 = 513;
}
///
/// See [module-level documentation] for usage.
///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Refund`]: crate::offers::refund::Refund
/// [module-level documentation]: self
}
/// Indicates how [`Invoice::signing_pubkey`] was set.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub trait SigningPubkeyStrategy {}
/// [`Invoice::signing_pubkey`] was explicitly set.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub struct ExplicitSigningPubkey {}
/// [`Invoice::signing_pubkey`] was derived.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub struct DerivedSigningPubkey {}
impl SigningPubkeyStrategy for ExplicitSigningPubkey {}
}
/// Signs the invoice using the given function.
+ ///
+ /// This is not exported to bindings users as functions aren't currently mapped.
pub fn sign<F, E>(self, sign: F) -> Result<Invoice, SignError<E>>
where
F: FnOnce(&Message) -> Result<Signature, E>
/// An invoice may be sent in response to an [`InvoiceRequest`] in the case of an offer or sent
/// directly after scanning a refund. It includes all the information needed to pay a recipient.
///
+/// This is not exported to bindings users as its name conflicts with the BOLT 11 Invoice type.
+///
/// [`Offer`]: crate::offers::offer::Offer
/// [`Refund`]: crate::offers::refund::Refund
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
use core::convert::TryFrom;
use core::time::Duration;
use crate::blinded_path::{BlindedHop, BlindedPath};
- use crate::chain::keysinterface::KeyMaterial;
+ use crate::sign::KeyMaterial;
use crate::ln::features::Bolt12InvoiceFeatures;
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::DecodeError;
use bitcoin::secp256k1::schnorr::Signature;
use core::convert::{Infallible, TryFrom};
use core::ops::Deref;
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use crate::io;
use crate::blinded_path::BlindedPath;
use crate::ln::PaymentHash;
///
/// See [module-level documentation] for usage.
///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+///
/// [module-level documentation]: self
pub struct InvoiceRequestBuilder<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> {
offer: &'a Offer,
}
/// Indicates how [`InvoiceRequest::payer_id`] will be set.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub trait PayerIdStrategy {}
/// [`InvoiceRequest::payer_id`] will be explicitly set.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub struct ExplicitPayerId {}
/// [`InvoiceRequest::payer_id`] will be derived.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub struct DerivedPayerId {}
impl PayerIdStrategy for ExplicitPayerId {}
impl<'a> UnsignedInvoiceRequest<'a> {
/// Signs the invoice request using the given function.
+ ///
+ /// This is not exported to bindings users as functions are not yet mapped.
pub fn sign<F, E>(self, sign: F) -> Result<InvoiceRequest, SignError<E>>
where
F: FnOnce(&Message) -> Result<Signature, E>
/// See [`InvoiceRequest::respond_with_no_std`] for further details where the aforementioned
/// creation time is used for the `created_at` parameter.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Duration`]: core::time::Duration
#[cfg(feature = "std")]
pub fn respond_with(
///
/// Errors if the request contains unknown required features.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Invoice::created_at`]: crate::offers::invoice::Invoice::created_at
pub fn respond_with_no_std(
&self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash,
///
/// See [`InvoiceRequest::respond_with`] for further details.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Invoice`]: crate::offers::invoice::Invoice
#[cfg(feature = "std")]
pub fn verify_and_respond_using_derived_keys<T: secp256k1::Signing>(
///
/// See [`InvoiceRequest::respond_with_no_std`] for further details.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Invoice`]: crate::offers::invoice::Invoice
pub fn verify_and_respond_using_derived_keys_no_std<T: secp256k1::Signing>(
&self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash,
use core::num::NonZeroU64;
#[cfg(feature = "std")]
use core::time::Duration;
- use crate::chain::keysinterface::KeyMaterial;
+ use crate::sign::KeyMaterial;
use crate::ln::features::InvoiceRequestFeatures;
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
use core::ops::Deref;
use core::str::FromStr;
use core::time::Duration;
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use crate::io;
use crate::blinded_path::BlindedPath;
use crate::ln::features::OfferFeatures;
///
/// See [module-level documentation] for usage.
///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+///
/// [module-level documentation]: self
pub struct OfferBuilder<'a, M: MetadataStrategy, T: secp256k1::Signing> {
offer: OfferContents,
}
/// Indicates how [`Offer::metadata`] may be set.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub trait MetadataStrategy {}
/// [`Offer::metadata`] may be explicitly set or left empty.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub struct ExplicitMetadata {}
/// [`Offer::metadata`] will be derived.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
pub struct DerivedMetadata {}
impl MetadataStrategy for ExplicitMetadata {}
///
/// Useful to protect the sender's privacy.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
/// [`InvoiceRequest::metadata`]: crate::offers::invoice_request::InvoiceRequest::metadata
/// [`Invoice::verify`]: crate::offers::invoice::Invoice::verify
///
/// Useful for recurring payments using the same `payer_id` with different invoices.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
pub fn request_invoice_deriving_metadata<ES: Deref>(
&self, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES
///
/// Errors if the offer contains unknown required features.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
pub fn request_invoice(
&self, metadata: Vec<u8>, payer_id: PublicKey
use core::num::NonZeroU64;
use core::time::Duration;
use crate::blinded_path::{BlindedHop, BlindedPath};
- use crate::chain::keysinterface::KeyMaterial;
+ use crate::sign::KeyMaterial;
use crate::ln::features::OfferFeatures;
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
}
/// Error when parsing a bech32 encoded message using [`str::parse`].
+///
+/// This is not exported to bindings users as its name conflicts with the BOLT 11 ParseError type.
#[derive(Debug, PartialEq)]
pub enum ParseError {
/// The bech32 encoding does not conform to the BOLT 12 requirements for continuing messages
}
/// Error when interpreting a TLV stream as a specific type.
+///
+/// This is not exported to bindings users as its name conflicts with the BOLT 11 SemanticError type.
#[derive(Debug, PartialEq)]
pub enum SemanticError {
/// The current [`std::time::SystemTime`] is past the offer or invoice's expiration.
use core::ops::Deref;
use core::str::FromStr;
use core::time::Duration;
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use crate::io;
use crate::blinded_path::BlindedPath;
use crate::ln::PaymentHash;
///
/// See [module-level documentation] for usage.
///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+///
/// [module-level documentation]: self
pub struct RefundBuilder<'a, T: secp256k1::Signing> {
refund: RefundContents,
/// See [`Refund::respond_with_no_std`] for further details where the aforementioned creation
/// time is used for the `created_at` parameter.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Duration`]: core::time::Duration
#[cfg(feature = "std")]
pub fn respond_with(
///
/// Errors if the request contains unknown required features.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Invoice::created_at`]: crate::offers::invoice::Invoice::created_at
pub fn respond_with_no_std(
&self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash,
///
/// See [`Refund::respond_with`] for further details.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Invoice`]: crate::offers::invoice::Invoice
#[cfg(feature = "std")]
pub fn respond_using_derived_keys<ES: Deref>(
///
/// See [`Refund::respond_with_no_std`] for further details.
///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
/// [`Invoice`]: crate::offers::invoice::Invoice
pub fn respond_using_derived_keys_no_std<ES: Deref>(
&self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash,
use core::convert::TryFrom;
use core::time::Duration;
use crate::blinded_path::{BlindedHop, BlindedPath};
- use crate::chain::keysinterface::KeyMaterial;
+ use crate::sign::KeyMaterial;
use crate::ln::features::{InvoiceRequestFeatures, OfferFeatures};
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
use core::convert::Infallible;
use core::time::Duration;
use crate::blinded_path::{BlindedHop, BlindedPath};
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use crate::ln::PaymentHash;
use crate::ln::features::BlindedHopFeatures;
use crate::offers::invoice::BlindedPayInfo;
//! Onion message testing and test utilities live here.
use crate::blinded_path::BlindedPath;
-use crate::chain::keysinterface::{NodeSigner, Recipient};
+use crate::sign::{NodeSigner, Recipient};
use crate::ln::features::InitFeatures;
use crate::ln::msgs::{self, DecodeError, OnionMessageHandler};
use super::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessageContents, OnionMessenger, SendError};
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1};
+use core::sync::atomic::{AtomicU16, Ordering};
use crate::io;
use crate::io_extras::read_to_end;
use crate::sync::Arc;
struct MessengerNode {
keys_manager: Arc<test_utils::TestKeysInterface>,
messenger: OnionMessenger<Arc<test_utils::TestKeysInterface>, Arc<test_utils::TestKeysInterface>, Arc<test_utils::TestLogger>, Arc<TestCustomMessageHandler>>,
+ custom_message_handler: Arc<TestCustomMessageHandler>,
logger: Arc<test_utils::TestLogger>,
}
}
}
-struct TestCustomMessageHandler {}
+struct TestCustomMessageHandler {
+ num_messages_expected: AtomicU16,
+}
+
+impl TestCustomMessageHandler {
+ fn new() -> Self {
+ Self { num_messages_expected: AtomicU16::new(0) }
+ }
+}
+
+impl Drop for TestCustomMessageHandler {
+ fn drop(&mut self) {
+ #[cfg(feature = "std")] {
+ if std::thread::panicking() {
+ return;
+ }
+ }
+ assert_eq!(self.num_messages_expected.load(Ordering::SeqCst), 0);
+ }
+}
impl CustomOnionMessageHandler for TestCustomMessageHandler {
type CustomMessage = TestCustomMessage;
- fn handle_custom_message(&self, _msg: Self::CustomMessage) {}
+ fn handle_custom_message(&self, _msg: Self::CustomMessage) {
+ self.num_messages_expected.fetch_sub(1, Ordering::SeqCst);
+ }
fn read_custom_message<R: io::Read>(&self, message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, DecodeError> where Self: Sized {
if message_type == CUSTOM_MESSAGE_TYPE {
let buf = read_to_end(buffer)?;
let logger = Arc::new(test_utils::TestLogger::with_id(format!("node {}", i)));
let seed = [i as u8; 32];
let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet));
+ let custom_message_handler = Arc::new(TestCustomMessageHandler::new());
nodes.push(MessengerNode {
keys_manager: keys_manager.clone(),
- messenger: OnionMessenger::new(keys_manager.clone(), keys_manager.clone(), logger.clone(), Arc::new(TestCustomMessageHandler {})),
+ messenger: OnionMessenger::new(keys_manager.clone(), keys_manager.clone(), logger.clone(), custom_message_handler.clone()),
+ custom_message_handler,
logger,
});
}
nodes
}
-fn pass_along_path(path: &Vec<MessengerNode>, expected_path_id: Option<[u8; 32]>) {
+fn pass_along_path(path: &Vec<MessengerNode>) {
+ path[path.len() - 1].custom_message_handler.num_messages_expected.fetch_add(1, Ordering::SeqCst);
let mut prev_node = &path[0];
- let num_nodes = path.len();
- for (idx, node) in path.into_iter().skip(1).enumerate() {
+ for node in path.into_iter().skip(1) {
let events = prev_node.messenger.release_pending_msgs();
let onion_msg = {
let msgs = events.get(&node.get_node_pk()).unwrap();
msgs[0].clone()
};
node.messenger.handle_onion_message(&prev_node.get_node_pk(), &onion_msg);
- if idx == num_nodes - 1 {
- node.logger.assert_log_contains(
- "lightning::onion_message::messenger",
- &format!("Received an onion message with path_id: {:02x?}", expected_path_id), 1);
- }
prev_node = node;
}
}
let test_msg = OnionMessageContents::Custom(TestCustomMessage {});
nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), test_msg, None).unwrap();
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
}
#[test]
let test_msg = OnionMessageContents::Custom(TestCustomMessage {});
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk()), test_msg, None).unwrap();
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
}
#[test]
let blinded_path = BlindedPath::new_for_message(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap();
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedPath(blinded_path), test_msg, None).unwrap();
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
}
#[test]
let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), test_msg, None).unwrap();
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
}
#[test]
let blinded_path = BlindedPath::new_for_message(&[nodes[0].get_node_pk(), nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg.clone()), None).unwrap();
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
// Try with a two-hop blinded path where we are the introduction node.
let blinded_path = BlindedPath::new_for_message(&[nodes[0].get_node_pk(), nodes[1].get_node_pk()], &*nodes[1].keys_manager, &secp_ctx).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg), None).unwrap();
nodes.remove(2);
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
}
#[test]
// Destination::Node
let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::Node(nodes[3].get_node_pk()), OnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap();
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
// Make sure the last node successfully decoded the reply path.
nodes[3].logger.assert_log_contains(
"lightning::onion_message::messenger",
let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap();
- pass_along_path(&nodes, None);
+ pass_along_path(&nodes);
nodes[3].logger.assert_log_contains(
"lightning::onion_message::messenger",
&format!("Received an onion message with path_id None and a reply_path"), 2);
let err = nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), OnionMessageContents::Custom(test_msg), None).unwrap_err();
assert_eq!(err, SendError::BufferFull);
}
+
+#[test]
+fn many_hops() {
+ // Check we can send over a route with many hops. This will exercise our logic for onion messages
+ // of size [`crate::onion_message::packet::BIG_PACKET_HOP_DATA_LEN`].
+ let num_nodes: usize = 25;
+ let nodes = create_nodes(num_nodes as u8);
+ let test_msg = OnionMessageContents::Custom(TestCustomMessage {});
+
+ let mut intermediates = vec![];
+ for i in 1..(num_nodes-1) {
+ intermediates.push(nodes[i].get_node_pk());
+ }
+
+ nodes[0].messenger.send_onion_message(&intermediates, Destination::Node(nodes[num_nodes-1].get_node_pk()), test_msg, None).unwrap();
+ pass_along_path(&nodes);
+}
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
use crate::blinded_path::{BlindedPath, ForwardTlvs, ReceiveTlvs, utils};
-use crate::chain::keysinterface::{EntropySource, KeysManager, NodeSigner, Recipient};
+use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient};
use crate::events::OnionMessageProvider;
use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::msgs::{self, OnionMessageHandler};
/// # use bitcoin::hashes::_export::_core::time::Duration;
/// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
/// # use lightning::blinded_path::BlindedPath;
-/// # use lightning::chain::keysinterface::KeysManager;
+/// # use lightning::sign::KeysManager;
/// # use lightning::ln::peer_handler::IgnoringMessageHandler;
/// # use lightning::onion_message::{CustomOnionMessageContents, Destination, OnionMessageContents, OnionMessenger};
/// # use lightning::util::logger::{Logger, Record};
BufferFull,
/// Failed to retrieve our node id from the provided [`NodeSigner`].
///
- /// [`NodeSigner`]: crate::chain::keysinterface::NodeSigner
+ /// [`NodeSigner`]: crate::sign::NodeSigner
GetNodeIdFailed,
/// We attempted to send to a blinded path where we are the introduction node, and failed to
/// advance the blinded path to make the second hop the new introduction node. Either
BIG_PACKET_HOP_DATA_LEN
} else { return Err(()) };
- Ok(onion_utils::construct_onion_message_packet::<_, _>(
- payloads, onion_keys, prng_seed, hop_data_len))
+ onion_utils::construct_onion_message_packet::<_, _>(
+ payloads, onion_keys, prng_seed, hop_data_len)
}
use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, GossipTimestampFilter};
use crate::ln::msgs::{QueryChannelRange, ReplyChannelRange, QueryShortChannelIds, ReplyShortChannelIdsEnd};
use crate::ln::msgs;
-use crate::routing::utxo::{self, UtxoLookup};
+use crate::routing::utxo::{self, UtxoLookup, UtxoResolver};
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, MaybeReadable};
use crate::util::logger::{Logger, Level};
use crate::util::scid_utils::{block_from_scid, scid_from_parts, MAX_SCID_BLOCK};
}
/// Fees for routing via a given channel or a node
-#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash, Ord, PartialOrd)]
pub struct RoutingFees {
/// Flat routing fee in millisatoshis.
pub base_msat: u32,
/// Store or update channel info from a channel announcement.
///
- /// You probably don't want to call this directly, instead relying on a P2PGossipSync's
- /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
+ /// You probably don't want to call this directly, instead relying on a [`P2PGossipSync`]'s
+ /// [`RoutingMessageHandler`] implementation to call it indirectly. This may be useful to accept
/// routing messages from a source using a protocol other than the lightning P2P protocol.
///
/// If a [`UtxoLookup`] object is provided via `utxo_lookup`, it will be called to verify
self.update_channel_from_unsigned_announcement_intern(&msg.contents, Some(msg), utxo_lookup)
}
+ /// Store or update channel info from a channel announcement.
+ ///
+ /// You probably don't want to call this directly, instead relying on a [`P2PGossipSync`]'s
+ /// [`RoutingMessageHandler`] implementation to call it indirectly. This may be useful to accept
+ /// routing messages from a source using a protocol other than the lightning P2P protocol.
+ ///
+ /// This will skip verification of if the channel is actually on-chain.
+ pub fn update_channel_from_announcement_no_lookup(
+ &self, msg: &ChannelAnnouncement
+ ) -> Result<(), LightningError> {
+ self.update_channel_from_announcement::<&UtxoResolver>(msg, &None)
+ }
+
/// Store or update channel info from a channel announcement without verifying the associated
/// signatures. Because we aren't given the associated signatures here we cannot relay the
/// channel announcement to any of our peers.
return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
}
+ if msg.chain_hash != self.genesis_hash {
+ return Err(LightningError {
+ err: "Channel announcement chain hash does not match genesis hash".to_owned(),
+ action: ErrorAction::IgnoreAndLog(Level::Debug),
+ });
+ }
+
{
let channels = self.channels.read().unwrap();
fn update_channel_intern(&self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig: Option<&secp256k1::ecdsa::Signature>) -> Result<(), LightningError> {
let chan_enabled = msg.flags & (1 << 1) != (1 << 1);
+ if msg.chain_hash != self.genesis_hash {
+ return Err(LightningError {
+ err: "Channel update chain hash does not match genesis hash".to_owned(),
+ action: ErrorAction::IgnoreAndLog(Level::Debug),
+ });
+ }
+
#[cfg(all(feature = "std", not(test), not(feature = "_test_utils")))]
{
// Note that many tests rely on being able to set arbitrarily old timestamps, thus we
Ok(_) => panic!(),
Err(e) => assert_eq!(e.err, "Channel announcement node had a channel with itself")
};
+
+ // Test that channel announcements with the wrong chain hash are ignored (network graph is testnet,
+ // announcement is mainnet).
+ let incorrect_chain_announcement = get_signed_channel_announcement(|unsigned_announcement| {
+ unsigned_announcement.chain_hash = genesis_block(Network::Bitcoin).header.block_hash();
+ }, node_1_privkey, node_2_privkey, &secp_ctx);
+ match gossip_sync.handle_channel_announcement(&incorrect_chain_announcement) {
+ Ok(_) => panic!(),
+ Err(e) => assert_eq!(e.err, "Channel announcement chain hash does not match genesis hash")
+ };
}
#[test]
Ok(_) => panic!(),
Err(e) => assert_eq!(e.err, "Invalid signature on channel_update message")
};
+
+ // Test that channel updates with the wrong chain hash are ignored (network graph is testnet, channel
+ // update is mainet).
+ let incorrect_chain_update = get_signed_channel_update(|unsigned_channel_update| {
+ unsigned_channel_update.chain_hash = genesis_block(Network::Bitcoin).header.block_hash();
+ }, node_1_privkey, &secp_ctx);
+
+ match gossip_sync.handle_channel_update(&incorrect_chain_update) {
+ Ok(_) => panic!(),
+ Err(e) => assert_eq!(e.err, "Channel update chain hash does not match genesis hash")
+ };
}
#[test]
use crate::blinded_path::{BlindedHop, BlindedPath};
use crate::ln::PaymentHash;
use crate::ln::channelmanager::{ChannelDetails, PaymentId};
-use crate::ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
+use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, InvoiceFeatures, NodeFeatures};
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use crate::offers::invoice::BlindedPayInfo;
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
use crate::io;
use crate::prelude::*;
-use crate::sync::Mutex;
+use crate::sync::{Mutex, MutexGuard};
use alloc::collections::BinaryHeap;
-use core::cmp;
+use core::{cmp, fmt};
use core::ops::Deref;
/// A [`Router`] implemented using [`find_route`].
-pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> where
+pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: Score<ScoreParams = SP>> where
L::Target: Logger,
- S::Target: for <'a> LockableScore<'a>,
+ S::Target: for <'a> LockableScore<'a, Locked = MutexGuard<'a, Sc>>,
{
network_graph: G,
logger: L,
random_seed_bytes: Mutex<[u8; 32]>,
- scorer: S
+ scorer: S,
+ score_params: SP
}
-impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> DefaultRouter<G, L, S> where
+impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: Score<ScoreParams = SP>> DefaultRouter<G, L, S, SP, Sc> where
L::Target: Logger,
- S::Target: for <'a> LockableScore<'a>,
+ S::Target: for <'a> LockableScore<'a, Locked = MutexGuard<'a, Sc>>,
{
/// Creates a new router.
- pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32], scorer: S) -> 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);
- Self { network_graph, logger, random_seed_bytes, scorer }
+ Self { network_graph, logger, random_seed_bytes, scorer, score_params }
}
}
-impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> Router for DefaultRouter<G, L, S> where
+impl< G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: Score<ScoreParams = SP>> Router for DefaultRouter<G, L, S, SP, Sc> where
L::Target: Logger,
- S::Target: for <'a> LockableScore<'a>,
+ S::Target: for <'a> LockableScore<'a, Locked = MutexGuard<'a, Sc>>,
{
fn find_route(
- &self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>,
+ &self,
+ payer: &PublicKey,
+ params: &RouteParameters,
+ first_hops: Option<&[&ChannelDetails]>,
inflight_htlcs: &InFlightHtlcs
) -> Result<Route, LightningError> {
let random_seed_bytes = {
*locked_random_seed_bytes = Sha256::hash(&*locked_random_seed_bytes).into_inner();
*locked_random_seed_bytes
};
-
find_route(
payer, params, &self.network_graph, first_hops, &*self.logger,
&ScorerAccountingForInFlightHtlcs::new(self.scorer.lock(), inflight_htlcs),
+ &self.score_params,
&random_seed_bytes
)
}
}
impl<'a, S: Score> Score for ScorerAccountingForInFlightHtlcs<'a, S> {
- fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage) -> u64 {
+ type ScoreParams = S::ScoreParams;
+ fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams) -> u64 {
if let Some(used_liquidity) = self.inflight_htlcs.used_liquidity_msat(
source, target, short_channel_id
) {
..usage
};
- self.scorer.channel_penalty_msat(short_channel_id, source, target, usage)
+ self.scorer.channel_penalty_msat(short_channel_id, source, target, usage, score_params)
} else {
- self.scorer.channel_penalty_msat(short_channel_id, source, target, usage)
+ self.scorer.channel_penalty_msat(short_channel_id, source, target, usage, score_params)
}
}
(2, self.final_value_msat, required),
// LDK versions prior to 0.0.114 had the `final_cltv_expiry_delta` parameter in
// `RouteParameters` directly. For compatibility, we write it here.
- (4, self.payment_params.final_cltv_expiry_delta, required),
+ (4, self.payment_params.payee.final_cltv_expiry_delta(), option),
});
Ok(())
}
_init_and_read_tlv_fields!(reader, {
(0, payment_params, (required: ReadableArgs, 0)),
(2, final_value_msat, required),
- (4, final_cltv_expiry_delta, required),
+ (4, final_cltv_delta, option),
});
let mut payment_params: PaymentParameters = payment_params.0.unwrap();
- if payment_params.final_cltv_expiry_delta == 0 {
- payment_params.final_cltv_expiry_delta = final_cltv_expiry_delta.0.unwrap();
+ if let Payee::Clear { ref mut final_cltv_expiry_delta, .. } = payment_params.payee {
+ if final_cltv_expiry_delta == &0 {
+ *final_cltv_expiry_delta = final_cltv_delta.ok_or(DecodeError::InvalidValue)?;
+ }
}
Ok(Self {
payment_params,
// down from (1300-93) / 61 = 19.78... to arrive at a conservative estimate of 19.
const MAX_PATH_LENGTH_ESTIMATE: u8 = 19;
-/// The recipient of a payment.
+/// Information used to route a payment.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct PaymentParameters {
- /// The node id of the payee.
- pub payee_pubkey: PublicKey,
-
- /// Features supported by the payee.
- ///
- /// May be set from the payee's invoice or via [`for_keysend`]. May be `None` if the invoice
- /// does not contain any features.
- ///
- /// [`for_keysend`]: Self::for_keysend
- pub features: Option<InvoiceFeatures>,
-
- /// Hints for routing to the payee, containing channels connecting the payee to public nodes.
- pub route_hints: Hints,
+ /// Information about the payee, such as their features and route hints for their channels.
+ pub payee: Payee,
/// Expiration of a payment to the payee, in seconds relative to the UNIX epoch.
pub expiry_time: Option<u64>,
/// payment to fail. Future attempts for the same payment shouldn't be relayed through any of
/// these SCIDs.
pub previously_failed_channels: Vec<u64>,
-
- /// The minimum CLTV delta at the end of the route. This value must not be zero.
- pub final_cltv_expiry_delta: u32,
}
impl Writeable for PaymentParameters {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
let mut clear_hints = &vec![];
let mut blinded_hints = &vec![];
- match &self.route_hints {
- Hints::Clear(hints) => clear_hints = hints,
- Hints::Blinded(hints) => blinded_hints = hints,
+ match &self.payee {
+ Payee::Clear { route_hints, .. } => clear_hints = route_hints,
+ Payee::Blinded { route_hints, .. } => blinded_hints = route_hints,
}
write_tlv_fields!(writer, {
- (0, self.payee_pubkey, required),
+ (0, self.payee.node_id(), option),
(1, self.max_total_cltv_expiry_delta, required),
- (2, self.features, option),
+ (2, self.payee.features(), option),
(3, self.max_path_count, required),
(4, *clear_hints, vec_type),
(5, self.max_channel_saturation_power_of_half, required),
(6, self.expiry_time, option),
(7, self.previously_failed_channels, vec_type),
(8, *blinded_hints, optional_vec),
- (9, self.final_cltv_expiry_delta, required),
+ (9, self.payee.final_cltv_expiry_delta(), option),
});
Ok(())
}
impl ReadableArgs<u32> for PaymentParameters {
fn read<R: io::Read>(reader: &mut R, default_final_cltv_expiry_delta: u32) -> Result<Self, DecodeError> {
_init_and_read_tlv_fields!(reader, {
- (0, payee_pubkey, required),
+ (0, payee_pubkey, option),
(1, max_total_cltv_expiry_delta, (default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA)),
- (2, features, option),
+ (2, features, (option: ReadableArgs, payee_pubkey.is_some())),
(3, max_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)),
(4, route_hints, vec_type),
(5, max_channel_saturation_power_of_half, (default_value, 2)),
});
let clear_route_hints = route_hints.unwrap_or(vec![]);
let blinded_route_hints = blinded_route_hints.unwrap_or(vec![]);
- let route_hints = if blinded_route_hints.len() != 0 {
- if clear_route_hints.len() != 0 { return Err(DecodeError::InvalidValue) }
- Hints::Blinded(blinded_route_hints)
+ let payee = if blinded_route_hints.len() != 0 {
+ if clear_route_hints.len() != 0 || payee_pubkey.is_some() { return Err(DecodeError::InvalidValue) }
+ Payee::Blinded {
+ route_hints: blinded_route_hints,
+ features: features.and_then(|f: Features| f.bolt12()),
+ }
} else {
- Hints::Clear(clear_route_hints)
+ Payee::Clear {
+ route_hints: clear_route_hints,
+ node_id: payee_pubkey.ok_or(DecodeError::InvalidValue)?,
+ features: features.and_then(|f| f.bolt11()),
+ final_cltv_expiry_delta: final_cltv_expiry_delta.0.unwrap(),
+ }
};
Ok(Self {
- payee_pubkey: _init_tlv_based_struct_field!(payee_pubkey, required),
max_total_cltv_expiry_delta: _init_tlv_based_struct_field!(max_total_cltv_expiry_delta, (default_value, unused)),
- features,
max_path_count: _init_tlv_based_struct_field!(max_path_count, (default_value, unused)),
- route_hints,
+ payee,
max_channel_saturation_power_of_half: _init_tlv_based_struct_field!(max_channel_saturation_power_of_half, (default_value, unused)),
expiry_time,
previously_failed_channels: previously_failed_channels.unwrap_or(Vec::new()),
- final_cltv_expiry_delta: _init_tlv_based_struct_field!(final_cltv_expiry_delta, (default_value, unused)),
})
}
}
/// provided.
pub fn from_node_id(payee_pubkey: PublicKey, final_cltv_expiry_delta: u32) -> Self {
Self {
- payee_pubkey,
- features: None,
- route_hints: Hints::Clear(vec![]),
+ payee: Payee::Clear { node_id: payee_pubkey, route_hints: vec![], features: None, final_cltv_expiry_delta },
expiry_time: None,
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
max_path_count: DEFAULT_MAX_PATH_COUNT,
max_channel_saturation_power_of_half: 2,
previously_failed_channels: Vec::new(),
- final_cltv_expiry_delta,
}
}
/// The `final_cltv_expiry_delta` should match the expected final CLTV delta the recipient has
/// provided.
pub fn for_keysend(payee_pubkey: PublicKey, final_cltv_expiry_delta: u32) -> Self {
- Self::from_node_id(payee_pubkey, final_cltv_expiry_delta).with_features(InvoiceFeatures::for_keysend())
+ Self::from_node_id(payee_pubkey, final_cltv_expiry_delta).with_bolt11_features(InvoiceFeatures::for_keysend()).expect("PaymentParameters::from_node_id should always initialize the payee as unblinded")
}
- /// Includes the payee's features.
+ /// Includes the payee's features. Errors if the parameters were initialized with blinded payment
+ /// paths.
///
/// This is not exported to bindings users since bindings don't support move semantics
- pub fn with_features(self, features: InvoiceFeatures) -> Self {
- Self { features: Some(features), ..self }
+ pub fn with_bolt11_features(self, features: InvoiceFeatures) -> Result<Self, ()> {
+ match self.payee {
+ Payee::Blinded { .. } => Err(()),
+ Payee::Clear { route_hints, node_id, final_cltv_expiry_delta, .. } =>
+ Ok(Self {
+ payee: Payee::Clear {
+ route_hints, node_id, features: Some(features), final_cltv_expiry_delta
+ }, ..self
+ })
+ }
}
- /// Includes hints for routing to the payee.
+ /// Includes hints for routing to the payee. Errors if the parameters were initialized with
+ /// blinded payment paths.
///
/// This is not exported to bindings users since bindings don't support move semantics
- pub fn with_route_hints(self, route_hints: Vec<RouteHint>) -> Self {
- Self { route_hints: Hints::Clear(route_hints), ..self }
+ pub fn with_route_hints(self, route_hints: Vec<RouteHint>) -> Result<Self, ()> {
+ match self.payee {
+ Payee::Blinded { .. } => Err(()),
+ Payee::Clear { node_id, features, final_cltv_expiry_delta, .. } =>
+ Ok(Self {
+ payee: Payee::Clear {
+ route_hints, node_id, features, final_cltv_expiry_delta,
+ }, ..self
+ })
+ }
}
/// Includes a payment expiration in seconds relative to the UNIX epoch.
}
}
-/// Routing hints for the tail of the route.
+/// The recipient of a payment, differing based on whether they've hidden their identity with route
+/// blinding.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
-pub enum Hints {
+pub enum Payee {
/// The recipient provided blinded paths and payinfo to reach them. The blinded paths themselves
/// will be included in the final [`Route`].
- Blinded(Vec<(BlindedPayInfo, BlindedPath)>),
+ Blinded {
+ /// Aggregated routing info and blinded paths, for routing to the payee without knowing their
+ /// node id.
+ route_hints: Vec<(BlindedPayInfo, BlindedPath)>,
+ /// Features supported by the payee.
+ ///
+ /// May be set from the payee's invoice. May be `None` if the invoice does not contain any
+ /// features.
+ features: Option<Bolt12InvoiceFeatures>,
+ },
/// The recipient included these route hints in their BOLT11 invoice.
- Clear(Vec<RouteHint>),
+ Clear {
+ /// The node id of the payee.
+ node_id: PublicKey,
+ /// Hints for routing to the payee, containing channels connecting the payee to public nodes.
+ route_hints: Vec<RouteHint>,
+ /// Features supported by the payee.
+ ///
+ /// May be set from the payee's invoice or via [`for_keysend`]. May be `None` if the invoice
+ /// does not contain any features.
+ ///
+ /// [`for_keysend`]: PaymentParameters::for_keysend
+ features: Option<InvoiceFeatures>,
+ /// The minimum CLTV delta at the end of the route. This value must not be zero.
+ final_cltv_expiry_delta: u32,
+ },
+}
+
+impl Payee {
+ fn node_id(&self) -> Option<PublicKey> {
+ match self {
+ Self::Clear { node_id, .. } => Some(*node_id),
+ _ => None,
+ }
+ }
+ fn node_features(&self) -> Option<NodeFeatures> {
+ match self {
+ Self::Clear { features, .. } => features.as_ref().map(|f| f.to_context()),
+ Self::Blinded { features, .. } => features.as_ref().map(|f| f.to_context()),
+ }
+ }
+ fn supports_basic_mpp(&self) -> bool {
+ match self {
+ Self::Clear { features, .. } => features.as_ref().map_or(false, |f| f.supports_basic_mpp()),
+ Self::Blinded { features, .. } => features.as_ref().map_or(false, |f| f.supports_basic_mpp()),
+ }
+ }
+ fn features(&self) -> Option<FeaturesRef> {
+ match self {
+ Self::Clear { features, .. } => features.as_ref().map(|f| FeaturesRef::Bolt11(f)),
+ Self::Blinded { features, .. } => features.as_ref().map(|f| FeaturesRef::Bolt12(f)),
+ }
+ }
+ fn final_cltv_expiry_delta(&self) -> Option<u32> {
+ match self {
+ Self::Clear { final_cltv_expiry_delta, .. } => Some(*final_cltv_expiry_delta),
+ _ => None,
+ }
+ }
+}
+
+enum FeaturesRef<'a> {
+ Bolt11(&'a InvoiceFeatures),
+ Bolt12(&'a Bolt12InvoiceFeatures),
+}
+enum Features {
+ Bolt11(InvoiceFeatures),
+ Bolt12(Bolt12InvoiceFeatures),
+}
+
+impl Features {
+ fn bolt12(self) -> Option<Bolt12InvoiceFeatures> {
+ match self {
+ Self::Bolt12(f) => Some(f),
+ _ => None,
+ }
+ }
+ fn bolt11(self) -> Option<InvoiceFeatures> {
+ match self {
+ Self::Bolt11(f) => Some(f),
+ _ => None,
+ }
+ }
+}
+
+impl<'a> Writeable for FeaturesRef<'a> {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ match self {
+ Self::Bolt11(f) => Ok(f.write(w)?),
+ Self::Bolt12(f) => Ok(f.write(w)?),
+ }
+ }
+}
+
+impl ReadableArgs<bool> for Features {
+ fn read<R: io::Read>(reader: &mut R, bolt11: bool) -> Result<Self, DecodeError> {
+ if bolt11 { return Ok(Self::Bolt11(Readable::read(reader)?)) }
+ Ok(Self::Bolt12(Readable::read(reader)?))
+ }
}
/// A list of hops along a payment path terminating with a channel to the recipient.
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct RouteHint(pub Vec<RouteHintHop>);
impl Writeable for RouteHint {
}
/// A channel descriptor for a hop along a payment path.
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+#[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,
features
}
+struct LoggedPayeePubkey(Option<PublicKey>);
+impl fmt::Display for LoggedPayeePubkey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {
+ Some(pk) => {
+ "payee node id ".fmt(f)?;
+ pk.fmt(f)
+ },
+ None => {
+ "blinded payee".fmt(f)
+ },
+ }
+ }
+}
+
/// Finds a route from us (payer) to the given target node (payee).
///
/// If the payee provided features in their invoice, they should be provided via `params.payee`.
pub fn find_route<L: Deref, GL: Deref, S: Score>(
our_node_pubkey: &PublicKey, route_params: &RouteParameters,
network_graph: &NetworkGraph<GL>, first_hops: Option<&[&ChannelDetails]>, logger: L,
- scorer: &S, random_seed_bytes: &[u8; 32]
+ scorer: &S, score_params: &S::ScoreParams, random_seed_bytes: &[u8; 32]
) -> Result<Route, LightningError>
where L::Target: Logger, GL::Target: Logger {
let graph_lock = network_graph.read_only();
- let final_cltv_expiry_delta = route_params.payment_params.final_cltv_expiry_delta;
let mut route = get_route(our_node_pubkey, &route_params.payment_params, &graph_lock, first_hops,
- route_params.final_value_msat, final_cltv_expiry_delta, logger, scorer,
+ route_params.final_value_msat, logger, scorer, score_params,
random_seed_bytes)?;
add_random_cltv_offset(&mut route, &route_params.payment_params, &graph_lock, random_seed_bytes);
Ok(route)
pub(crate) fn get_route<L: Deref, S: Score>(
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
- first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
- logger: L, scorer: &S, _random_seed_bytes: &[u8; 32]
+ first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, logger: L, scorer: &S, score_params: &S::ScoreParams,
+ _random_seed_bytes: &[u8; 32]
) -> Result<Route, LightningError>
where L::Target: Logger {
- let payee_node_id = NodeId::from_pubkey(&payment_params.payee_pubkey);
+ // If we're routing to a blinded recipient, we won't have their node id. Therefore, keep the
+ // unblinded payee id as an option. We also need a non-optional "payee id" for path construction,
+ // so use a dummy id for this in the blinded case.
+ let payee_node_id_opt = payment_params.payee.node_id().map(|pk| NodeId::from_pubkey(&pk));
+ const DUMMY_BLINDED_PAYEE_ID: [u8; 33] = [42u8; 33];
+ let maybe_dummy_payee_pk = payment_params.payee.node_id().unwrap_or_else(|| PublicKey::from_slice(&DUMMY_BLINDED_PAYEE_ID).unwrap());
+ let maybe_dummy_payee_node_id = NodeId::from_pubkey(&maybe_dummy_payee_pk);
let our_node_id = NodeId::from_pubkey(&our_node_pubkey);
- if payee_node_id == our_node_id {
+ if payee_node_id_opt.map_or(false, |payee| payee == our_node_id) {
return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError});
}
return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError});
}
- match &payment_params.route_hints {
- Hints::Clear(hints) => {
- for route in hints.iter() {
+ match &payment_params.payee {
+ Payee::Clear { route_hints, node_id, .. } => {
+ for route in route_hints.iter() {
for hop in &route.0 {
- if hop.src_node_id == payment_params.payee_pubkey {
+ if hop.src_node_id == *node_id {
return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError});
}
}
_ => return Err(LightningError{err: "Routing to blinded paths isn't supported yet".to_owned(), action: ErrorAction::IgnoreError}),
}
+ let final_cltv_expiry_delta = payment_params.payee.final_cltv_expiry_delta().unwrap_or(0);
if payment_params.max_total_cltv_expiry_delta <= final_cltv_expiry_delta {
return Err(LightningError{err: "Can't find a route where the maximum total CLTV expiry delta is below the final CLTV expiry.".to_owned(), action: ErrorAction::IgnoreError});
}
- // TODO: Remove the explicit final_cltv_expiry_delta parameter
- debug_assert_eq!(final_cltv_expiry_delta, payment_params.final_cltv_expiry_delta);
-
// The general routing idea is the following:
// 1. Fill first/last hops communicated by the caller.
// 2. Attempt to construct a path from payer to payee for transferring
// work reliably.
let allow_mpp = if payment_params.max_path_count == 1 {
false
- } else if let Some(features) = &payment_params.features {
- features.supports_basic_mpp()
- } else if let Some(node) = network_nodes.get(&payee_node_id) {
- if let Some(node_info) = node.announcement_info.as_ref() {
- node_info.features.supports_basic_mpp()
- } else { false }
+ } else if payment_params.payee.supports_basic_mpp() {
+ true
+ } else if let Some(payee) = payee_node_id_opt {
+ network_nodes.get(&payee).map_or(false, |node| node.announcement_info.as_ref().map_or(false,
+ |info| info.features.supports_basic_mpp()))
} else { false };
- log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey,
- payment_params.payee_pubkey, if allow_mpp { "with" } else { "without" },
+ log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey,
+ LoggedPayeePubkey(payment_params.payee.node_id()), if allow_mpp { "with" } else { "without" },
first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " });
// Step (1).
});
}
- log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payment_params.payee_pubkey, our_node_pubkey, final_value_msat);
+ log_trace!(logger, "Building path from {} to payer {} for value {} msat.",
+ LoggedPayeePubkey(payment_params.payee.node_id()), our_node_pubkey, final_value_msat);
macro_rules! add_entry {
// Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop.
effective_capacity,
};
let channel_penalty_msat = scorer.channel_penalty_msat(
- short_channel_id, &$src_node_id, &$dest_node_id, channel_usage
+ short_channel_id, &$src_node_id, &$dest_node_id, channel_usage, score_params
);
let path_penalty_msat = $next_hops_path_penalty_msat
.saturating_add(channel_penalty_msat);
// Entries are added to dist in add_entry!() when there is a channel from a node.
// Because there are no channels from payee, it will not have a dist entry at this point.
// If we're processing any other node, it is always be the result of a channel from it.
- assert_eq!($node_id, payee_node_id);
+ debug_assert_eq!($node_id, maybe_dummy_payee_node_id);
false
};
// If first hop is a private channel and the only way to reach the payee, this is the only
// place where it could be added.
- if let Some(first_channels) = first_hop_targets.get(&payee_node_id) {
+ payee_node_id_opt.map(|payee| first_hop_targets.get(&payee).map(|first_channels| {
for details in first_channels {
let candidate = CandidateRouteHop::FirstHop { details };
- let added = add_entry!(candidate, our_node_id, payee_node_id, 0, path_value_msat,
+ let added = add_entry!(candidate, our_node_id, payee, 0, path_value_msat,
0, 0u64, 0, 0);
log_trace!(logger, "{} direct route to payee via SCID {}",
if added { "Added" } else { "Skipped" }, candidate.short_channel_id());
}
- }
+ }));
// Add the payee as a target, so that the payee-to-payer
// search algorithm knows what to start with.
- match network_nodes.get(&payee_node_id) {
+ payee_node_id_opt.map(|payee| match network_nodes.get(&payee) {
// The payee is not in our network graph, so nothing to add here.
// There is still a chance of reaching them via last_hops though,
// so don't yet fail the payment here.
// If not, targets.pop() will not even let us enter the loop in step 2.
None => {},
Some(node) => {
- add_entries_to_cheapest_to_target_node!(node, payee_node_id, 0, path_value_msat, 0, 0u64, 0, 0);
+ add_entries_to_cheapest_to_target_node!(node, payee, 0, path_value_msat, 0, 0u64, 0, 0);
},
- }
+ });
// Step (2).
// If a caller provided us with last hops, add them to routing targets. Since this happens
// earlier than general path finding, they will be somewhat prioritized, although currently
// it matters only if the fees are exactly the same.
- let route_hints = match &payment_params.route_hints {
- Hints::Clear(hints) => hints,
+ let route_hints = match &payment_params.payee {
+ Payee::Clear { route_hints, .. } => route_hints,
_ => return Err(LightningError{err: "Routing to blinded paths isn't supported yet".to_owned(), action: ErrorAction::IgnoreError}),
};
for route in route_hints.iter().filter(|route| !route.0.is_empty()) {
// We start building the path from reverse, i.e., from payee
// to the first RouteHintHop in the path.
let hop_iter = route.0.iter().rev();
- let prev_hop_iter = core::iter::once(&payment_params.payee_pubkey).chain(
+ let prev_hop_iter = core::iter::once(&maybe_dummy_payee_pk).chain(
route.0.iter().skip(1).rev().map(|hop| &hop.src_node_id));
let mut hop_used = true;
let mut aggregate_next_hops_fee_msat: u64 = 0;
effective_capacity: candidate.effective_capacity(),
};
let channel_penalty_msat = scorer.channel_penalty_msat(
- hop.short_channel_id, &source, &target, channel_usage
+ hop.short_channel_id, &source, &target, channel_usage, score_params
);
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
.saturating_add(channel_penalty_msat);
// save this path for the payment route. Also, update the liquidity
// remaining on the used hops, so that we take them into account
// while looking for more paths.
- if ordered_hops.last().unwrap().0.node_id == payee_node_id {
+ if ordered_hops.last().unwrap().0.node_id == maybe_dummy_payee_node_id {
break 'path_walk;
}
// If we found a path back to the payee, we shouldn't try to process it again. This is
// the equivalent of the `elem.was_processed` check in
// add_entries_to_cheapest_to_target_node!() (see comment there for more info).
- if node_id == payee_node_id { continue 'path_construction; }
+ if node_id == maybe_dummy_payee_node_id { continue 'path_construction; }
// Otherwise, since the current target node is not us,
// keep "unrolling" the payment graph from payee to payer by
// Make sure we would never create a route with more paths than we allow.
debug_assert!(selected_paths.len() <= payment_params.max_path_count.into());
- if let Some(features) = &payment_params.features {
+ if let Some(node_features) = payment_params.payee.node_features() {
for path in selected_paths.iter_mut() {
if let Ok(route_hop) = path.last_mut().unwrap() {
- route_hop.node_features = features.to_context();
+ route_hop.node_features = node_features.clone();
}
}
}
paths,
payment_params: Some(payment_params.clone()),
};
- log_info!(logger, "Got route to {}: {}", payment_params.payee_pubkey, log_route!(route));
+ log_info!(logger, "Got route: {}", log_route!(route));
Ok(route)
}
let graph_lock = network_graph.read_only();
let mut route = build_route_from_hops_internal(
our_node_pubkey, hops, &route_params.payment_params, &graph_lock,
- route_params.final_value_msat, route_params.payment_params.final_cltv_expiry_delta,
- logger, random_seed_bytes)?;
+ route_params.final_value_msat, logger, random_seed_bytes)?;
add_random_cltv_offset(&mut route, &route_params.payment_params, &graph_lock, random_seed_bytes);
Ok(route)
}
fn build_route_from_hops_internal<L: Deref>(
our_node_pubkey: &PublicKey, hops: &[PublicKey], payment_params: &PaymentParameters,
- network_graph: &ReadOnlyNetworkGraph, final_value_msat: u64, final_cltv_expiry_delta: u32,
- logger: L, random_seed_bytes: &[u8; 32]
+ network_graph: &ReadOnlyNetworkGraph, final_value_msat: u64, logger: L,
+ random_seed_bytes: &[u8; 32]
) -> Result<Route, LightningError> where L::Target: Logger {
struct HopScorer {
}
impl Score for HopScorer {
+ type ScoreParams = ();
fn channel_penalty_msat(&self, _short_channel_id: u64, source: &NodeId, target: &NodeId,
- _usage: ChannelUsage) -> u64
+ _usage: ChannelUsage, _score_params: &Self::ScoreParams) -> u64
{
let mut cur_id = self.our_node_id;
for i in 0..self.hop_ids.len() {
let scorer = HopScorer { our_node_id, hop_ids };
get_route(our_node_pubkey, payment_params, network_graph, None, final_value_msat,
- final_cltv_expiry_delta, logger, &scorer, random_seed_bytes)
+ logger, &scorer, &(), random_seed_bytes)
}
#[cfg(test)]
use crate::routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees,
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE};
- use crate::routing::scoring::{ChannelUsage, FixedPenaltyScorer, Score, ProbabilisticScorer, ProbabilisticScoringParameters};
+ use crate::routing::scoring::{ChannelUsage, FixedPenaltyScorer, Score, ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
use crate::routing::test_utils::{add_channel, add_or_update_node, build_graph, build_line_graph, id_to_feature_flags, get_nodes, update_channel};
use crate::chain::transaction::OutPoint;
- use crate::chain::keysinterface::EntropySource;
+ use crate::sign::EntropySource;
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
use crate::ln::channelmanager;
// Simple route to 2 via 1
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 0, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 0, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Cannot send a payment of 0 msat");
} else { panic!(); }
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 2);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
let our_chans = vec![get_channel_details(Some(2), our_id, InitFeatures::from_le_bytes(vec![0b11]), 100000)];
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) =
- get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "First hop cannot have our_node_pubkey as a destination.");
} else { panic!(); }
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 2);
}
});
// Not possible to send 199_999_999, because the minimum on channel=2 is 200_000_000.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 199_999_999, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 199_999_999, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
});
// A payment above the minimum should pass
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 199_999_999, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 199_999_999, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 2);
}
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
excess_data: Vec::new()
});
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
// Overpay fees to hit htlc_minimum_msat.
let overpaid_fees = route.paths[0].hops[0].fee_msat + route.paths[1].hops[0].fee_msat;
// TODO: this could be better balanced to overpay 10k and not 15k.
excess_data: Vec::new()
});
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 60_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
// Fine to overpay for htlc_minimum_msat if it allows us to save fee.
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops[0].short_channel_id, 12);
let fees = route.paths[0].hops[0].fee_msat;
assert_eq!(fees, 5_000);
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
// Not fine to overpay for htlc_minimum_msat if it requires paying more than fee on
// the other channel.
assert_eq!(route.paths.len(), 1);
});
// If all the channels require some features we don't understand, route should fail
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
// If we specify a channel to node7, that overrides our local channel view and that gets used
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 2);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]);
add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[7], unknown_features.clone(), 1);
// If all nodes require some features we don't understand, route should fail
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
// If we specify a channel to node7, that overrides our local channel view and that gets used
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 2);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]);
// Route to 1 via 2 and 3 because our channel to 1 is disabled
let payment_params = PaymentParameters::from_node_id(nodes[0], 42);
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 3);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
// If we specify a channel to node7, that overrides our local channel view and that gets used
let payment_params = PaymentParameters::from_node_id(nodes[2], 42);
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 2);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[7]);
let mut invalid_last_hops = last_hops_multi_private_channels(&nodes);
invalid_last_hops.push(invalid_last_hop);
{
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(invalid_last_hops);
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(invalid_last_hops).unwrap();
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Route hint cannot have the payee as the source.");
} else { panic!(); }
}
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_multi_private_channels(&nodes));
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_multi_private_channels(&nodes)).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 5);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
fn ignores_empty_last_hops_test() {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(empty_last_hop(&nodes));
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(empty_last_hop(&nodes)).unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// Test handling of an empty RouteHint passed in Invoice.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 5);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let last_hops = multi_hop_last_hops_hint([nodes[2], nodes[3]]);
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone());
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone()).unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
excess_data: Vec::new()
});
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 4);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
let non_announced_pubkey = PublicKey::from_secret_key(&secp_ctx, &non_announced_privkey);
let last_hops = multi_hop_last_hops_hint([nodes[2], non_announced_pubkey]);
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone());
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone()).unwrap();
let scorer = ln_test_utils::TestScorer::new();
// Test through channels 2, 3, 0xff00, 0xff01.
// Test shows that multiple hop hints are considered.
excess_data: Vec::new()
});
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &[42u8; 32]).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &[42u8; 32]).unwrap();
assert_eq!(route.paths[0].hops.len(), 4);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
fn last_hops_with_public_channel_test() {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_with_public_channel(&nodes));
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_with_public_channel(&nodes)).unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// This test shows that public routes can be present in the invoice
// which would be handled in the same manner.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 5);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
// Simple test with outbound channel to 4 to test that last_hops and first_hops connect
let our_chans = vec![get_channel_details(Some(42), nodes[3].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
let mut last_hops = last_hops(&nodes);
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone());
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone()).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 2);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[3]);
last_hops[0].0[0].fees.base_msat = 1000;
// Revert to via 6 as the fee on 8 goes up
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops);
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 4);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
// ...but still use 8 for larger payments as 6 has a variable feerate
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 2000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 2000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths[0].hops.len(), 5);
assert_eq!(route.paths[0].hops[0].pubkey, nodes[1]);
htlc_minimum_msat: None,
htlc_maximum_msat: last_hop_htlc_max,
}]);
- let payment_params = PaymentParameters::from_node_id(target_node_id, 42).with_route_hints(vec![last_hops]);
+ let payment_params = PaymentParameters::from_node_id(target_node_id, 42).with_route_hints(vec![last_hops]).unwrap();
let our_chans = vec![get_channel_details(Some(42), middle_node_id, InitFeatures::from_le_bytes(vec![0b11]), outbound_capacity_msat)];
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let logger = ln_test_utils::TestLogger::new();
let network_graph = NetworkGraph::new(Network::Testnet, &logger);
let route = get_route(&source_node_id, &payment_params, &network_graph.read_only(),
- Some(&our_chans.iter().collect::<Vec<_>>()), route_val, 42, &logger, &scorer, &random_seed_bytes);
+ Some(&our_chans.iter().collect::<Vec<_>>()), route_val, &logger, &scorer, &(), &random_seed_bytes);
route
}
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
// We will use a simple single-path route from
// our node to node2 via node0: channels {1, 3}.
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 250_000_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 250_000_001, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 250_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 250_000_000, Arc::clone(&logger), &scorer, &(),&random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.hops.len(), 2);
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_001, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.hops.len(), 2);
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 15_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 15_001, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.hops.len(), 2);
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 15_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 15_001, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 15_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.hops.len(), 2);
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 10_001, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 10_001, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 10_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 10_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.hops.len(), 2);
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
// Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
// {12, 13, 11} have the capacities of 100, {6} has a capacity of 50.
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 60_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 60_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 49 sats (just a bit below the capacity).
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 49_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 49_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Attempt to route an exact amount is also fine
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
});
{
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 50_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
- .with_features(channelmanager::provided_invoice_features(&config));
+ .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
// We need a route consisting of 3 paths:
// From our node to node2 via node0, node7, node1 (three paths one hop each).
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 300_000, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 300_000,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
// Attempt to route while setting max_path_count to 0 results in a failure.
let zero_payment_params = payment_params.clone().with_max_path_count(0);
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &zero_payment_params, &network_graph.read_only(), None, 100, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &zero_payment_params, &network_graph.read_only(), None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Can't find a route with no paths allowed.");
} else { panic!(); }
}
// to account for 1/3 of the total value, which is violated by 2 out of 3 paths.
let fail_payment_params = payment_params.clone().with_max_path_count(3);
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &fail_payment_params, &network_graph.read_only(), None, 250_000, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &fail_payment_params, &network_graph.read_only(), None, 250_000,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
// Now, attempt to route 250 sats (just a bit below the capacity).
// Our algorithm should provide us with these 3 paths.
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None,
- 250_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ 250_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Attempt to route an exact amount is also fine
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None,
- 290_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ 290_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
// We need a route consisting of 3 paths:
// From our node to node3 via {node0, node2}, {node7, node2, node4} and {node7, node2}.
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 350_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 350_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 300 sats (exact amount we can route).
// Our algorithm should provide us with these 3 paths, 100 sats each.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 300_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 300_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
// This test checks that if we have two cheaper paths and one more expensive path,
// so that liquidity-wise any 2 of 3 combination is sufficient,
{
// Now, attempt to route 180 sats.
// Our algorithm should provide us with these 2 paths.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 180_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 180_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 2);
let mut total_value_transferred_msat = 0;
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
// We need a route consisting of 2 paths:
// From our node to node3 via {node0, node2} and {node7, node2, node4}.
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 210_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 210_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 200 sats (exact amount we can route).
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 200_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 200_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 2);
let mut total_amount_paid_msat = 0;
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_features(channelmanager::provided_invoice_features(&config))
+ let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
.with_route_hints(vec![RouteHint(vec![RouteHintHop {
src_node_id: nodes[2],
short_channel_id: 42,
cltv_expiry_delta: 42,
htlc_minimum_msat: None,
htlc_maximum_msat: None,
- }])]).with_max_channel_saturation_power_of_half(0);
+ }])]).unwrap().with_max_channel_saturation_power_of_half(0);
// Keep only two paths from us to nodes[2], both with a 99sat HTLC maximum, with one with
// no fee and one with a 1msat fee. Previously, trying to route 100 sats to nodes[2] here
// Get a route for 100 sats and check that we found the MPP route no problem and didn't
// overpay at all.
- let mut route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let mut route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 2);
route.paths.sort_by_key(|path| path.hops[0].short_channel_id);
// Paths are manually ordered ordered by SCID, so:
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config))
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
.with_max_channel_saturation_power_of_half(0);
// We need a route consisting of 3 paths:
{
// Attempt to route more than available results in a failure.
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 150_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ &our_id, &payment_params, &network_graph.read_only(), None, 150_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 125 sats (just a bit below the capacity of 3 channels).
// Our algorithm should provide us with these 3 paths.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 125_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 125_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Attempt to route without the last small cheap channel
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 2);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Now ensure the route flows simply over nodes 1 and 4 to 6.
- let route = get_route(&our_id, &payment_params, &network.read_only(), None, 10_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network.read_only(), None, 10_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops.len(), 3);
{
// Now, attempt to route 90 sats, which is exactly 90 sats at the last hop, plus the
// 200% fee charged channel 13 in the 1-to-2 direction.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops.len(), 2);
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
// We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
// gets an htlc_maximum_msat of 80_000 and channel 4 an htlc_minimum_msat of 90_000. We
// Now, attempt to route 90 sats, hitting the htlc_minimum on channel 4, but
// overshooting the htlc_maximum on channel 2. Thus, we should pick the (absurdly
// expensive) channels 12-13 path.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 90_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops.len(), 2);
let network_graph = NetworkGraph::new(Network::Testnet, Arc::clone(&logger));
let scorer = ln_test_utils::TestScorer::new();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&[
&get_channel_details(Some(3), nodes[0], channelmanager::provided_init_features(&config), 200_000),
&get_channel_details(Some(2), nodes[0], channelmanager::provided_init_features(&config), 10_000),
- ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ ]), 100_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops.len(), 1);
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&[
&get_channel_details(Some(3), nodes[0], channelmanager::provided_init_features(&config), 50_000),
&get_channel_details(Some(2), nodes[0], channelmanager::provided_init_features(&config), 50_000),
- ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ ]), 100_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 2);
assert_eq!(route.paths[0].hops.len(), 1);
assert_eq!(route.paths[1].hops.len(), 1);
&get_channel_details(Some(8), nodes[0], channelmanager::provided_init_features(&config), 50_000),
&get_channel_details(Some(9), nodes[0], channelmanager::provided_init_features(&config), 50_000),
&get_channel_details(Some(4), nodes[0], channelmanager::provided_init_features(&config), 1_000_000),
- ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ ]), 100_000, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].hops.len(), 1);
fn prefers_shorter_route_with_higher_fees() {
let (secp_ctx, network_graph, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes));
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes)).unwrap();
// Without penalizing each hop 100 msats, a longer path with lower fees is chosen.
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let route = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 100, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes
+ &our_id, &payment_params, &network_graph.read_only(), None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes
).unwrap();
let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
// from nodes[2] rather than channel 6, 11, and 8, even though the longer path is cheaper.
let scorer = FixedPenaltyScorer::with_penalty(100);
let route = get_route(
- &our_id, &payment_params, &network_graph.read_only(), None, 100, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes
+ &our_id, &payment_params, &network_graph.read_only(), None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes
).unwrap();
let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
fn write<W: Writer>(&self, _w: &mut W) -> Result<(), crate::io::Error> { unimplemented!() }
}
impl Score for BadChannelScorer {
- fn channel_penalty_msat(&self, short_channel_id: u64, _: &NodeId, _: &NodeId, _: ChannelUsage) -> u64 {
+ type ScoreParams = ();
+ fn channel_penalty_msat(&self, short_channel_id: u64, _: &NodeId, _: &NodeId, _: ChannelUsage, _score_params:&Self::ScoreParams) -> u64 {
if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
}
}
impl Score for BadNodeScorer {
- fn channel_penalty_msat(&self, _: u64, _: &NodeId, target: &NodeId, _: ChannelUsage) -> u64 {
+ type ScoreParams = ();
+ fn channel_penalty_msat(&self, _: u64, _: &NodeId, target: &NodeId, _: ChannelUsage, _score_params:&Self::ScoreParams) -> u64 {
if *target == self.node_id { u64::max_value() } else { 0 }
}
fn avoids_routing_through_bad_channels_and_nodes() {
let (secp_ctx, network, _, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes));
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes)).unwrap();
let network_graph = network.read_only();
// A path to nodes[6] exists when no penalties are applied to any channel.
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let route = get_route(
- &our_id, &payment_params, &network_graph, None, 100, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes
+ &our_id, &payment_params, &network_graph, None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes
).unwrap();
let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
// A different path to nodes[6] exists if channel 6 cannot be routed over.
let scorer = BadChannelScorer { short_channel_id: 6 };
let route = get_route(
- &our_id, &payment_params, &network_graph, None, 100, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes
+ &our_id, &payment_params, &network_graph, None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes
).unwrap();
let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
// A path to nodes[6] does not exist if nodes[2] cannot be routed through.
let scorer = BadNodeScorer { node_id: NodeId::from_pubkey(&nodes[2]) };
match get_route(
- &our_id, &payment_params, &network_graph, None, 100, 42,
- Arc::clone(&logger), &scorer, &random_seed_bytes
+ &our_id, &payment_params, &network_graph, None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes
) {
Err(LightningError { err, .. } ) => {
assert_eq!(err, "Failed to find a path to the given destination");
// Make sure that generally there is at least one route available
let feasible_max_total_cltv_delta = 1008;
- let feasible_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes))
+ let feasible_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes)).unwrap()
.with_max_total_cltv_expiry_delta(feasible_max_total_cltv_delta);
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
- let route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert_ne!(path.len(), 0);
// But not if we exclude all paths on the basis of their accumulated CLTV delta
let fail_max_total_cltv_delta = 23;
- let fail_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes))
+ let fail_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes)).unwrap()
.with_max_total_cltv_expiry_delta(fail_max_total_cltv_delta);
- match get_route(&our_id, &fail_payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes)
+ match get_route(&our_id, &fail_payment_params, &network_graph, None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes)
{
Err(LightningError { err, .. } ) => {
assert_eq!(err, "Failed to find a path to the given destination");
let network_graph = network.read_only();
let scorer = ln_test_utils::TestScorer::new();
- let mut payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes))
+ let mut payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes)).unwrap()
.with_max_path_count(1);
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// We should be able to find a route initially, and then after we fail a few random
// channels eventually we won't be able to any longer.
- assert!(get_route(&our_id, &payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes).is_ok());
+ assert!(get_route(&our_id, &payment_params, &network_graph, None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).is_ok());
loop {
- if let Ok(route) = get_route(&our_id, &payment_params, &network_graph, None, 100, 0, Arc::clone(&logger), &scorer, &random_seed_bytes) {
+ if let Ok(route) = get_route(&our_id, &payment_params, &network_graph, None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes) {
for chan in route.paths[0].hops.iter() {
assert!(!payment_params.previously_failed_channels.contains(&chan.short_channel_id));
}
// First check we can actually create a long route on this graph.
let feasible_payment_params = PaymentParameters::from_node_id(nodes[18], 0);
- let route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100, 0,
- Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &feasible_payment_params, &network_graph, None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
let path = route.paths[0].hops.iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert!(path.len() == MAX_PATH_LENGTH_ESTIMATE.into());
// But we can't create a path surpassing the MAX_PATH_LENGTH_ESTIMATE limit.
let fail_payment_params = PaymentParameters::from_node_id(nodes[19], 0);
- match get_route(&our_id, &fail_payment_params, &network_graph, None, 100, 0,
- Arc::clone(&logger), &scorer, &random_seed_bytes)
+ match get_route(&our_id, &fail_payment_params, &network_graph, None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes)
{
Err(LightningError { err, .. } ) => {
assert_eq!(err, "Failed to find a path to the given destination");
let scorer = ln_test_utils::TestScorer::new();
- let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes));
+ let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes)).unwrap();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 1);
let cltv_expiry_deltas_before = route.paths[0].hops.iter().map(|h| h.cltv_expiry_delta).collect::<Vec<u32>>();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[4u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
- let mut route = get_route(&our_id, &payment_params, &network_graph, None, 100, 0,
- Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let mut route = get_route(&our_id, &payment_params, &network_graph, None, 100,
+ Arc::clone(&logger), &scorer, &(), &random_seed_bytes).unwrap();
add_random_cltv_offset(&mut route, &payment_params, &network_graph, &random_seed_bytes);
let mut path_plausibility = vec![];
let payment_params = PaymentParameters::from_node_id(nodes[3], 0);
let hops = [nodes[1], nodes[2], nodes[4], nodes[3]];
let route = build_route_from_hops_internal(&our_id, &hops, &payment_params,
- &network_graph, 100, 0, Arc::clone(&logger), &random_seed_bytes).unwrap();
+ &network_graph, 100, Arc::clone(&logger), &random_seed_bytes).unwrap();
let route_hop_pubkeys = route.paths[0].hops.iter().map(|hop| hop.pubkey).collect::<Vec<_>>();
assert_eq!(hops.len(), route.paths[0].hops.len());
for (idx, hop_pubkey) in hops.iter().enumerate() {
fn avoids_saturating_channels() {
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
-
- let scorer = ProbabilisticScorer::new(Default::default(), &*network_graph, Arc::clone(&logger));
+ let decay_params = ProbabilisticScoringDecayParameters::default();
+ let scorer = ProbabilisticScorer::new(decay_params, &*network_graph, Arc::clone(&logger));
// Set the fee on channel 13 to 100% to match channel 4 giving us two equivalent paths (us
// -> node 7 -> node2 and us -> node 1 -> node 2) which we should balance over.
});
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// 100,000 sats is less than the available liquidity on each channel, set above.
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000_000, Arc::clone(&logger), &scorer, &ProbabilisticScoringFeeParameters::default(), &random_seed_bytes).unwrap();
assert_eq!(route.paths.len(), 2);
assert!((route.paths[0].hops[1].short_channel_id == 4 && route.paths[1].hops[1].short_channel_id == 13) ||
(route.paths[1].hops[1].short_channel_id == 4 && route.paths[0].hops[1].short_channel_id == 13));
#[test]
#[cfg(not(feature = "no-std"))]
fn generate_routes() {
- use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters};
+ use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
let mut d = match super::bench_utils::get_route_file() {
Ok(f) => f,
let dst = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
let payment_params = PaymentParameters::from_node_id(dst, 42);
let amt = seed as u64 % 200_000_000;
- let params = ProbabilisticScoringParameters::default();
- let scorer = ProbabilisticScorer::new(params, &graph, &logger);
- if get_route(src, &payment_params, &graph.read_only(), None, amt, 42, &logger, &scorer, &random_seed_bytes).is_ok() {
+ let params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger);
+ if get_route(src, &payment_params, &graph.read_only(), None, amt, &logger, &scorer, ¶ms, &random_seed_bytes).is_ok() {
continue 'load_endpoints;
}
}
#[test]
#[cfg(not(feature = "no-std"))]
fn generate_routes_mpp() {
- use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters};
+ use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
let mut d = match super::bench_utils::get_route_file() {
Ok(f) => f,
let src = &PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
seed = seed.overflowing_mul(0xdeadbeef).0;
let dst = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
- let payment_params = PaymentParameters::from_node_id(dst, 42).with_features(channelmanager::provided_invoice_features(&config));
+ let payment_params = PaymentParameters::from_node_id(dst, 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
let amt = seed as u64 % 200_000_000;
- let params = ProbabilisticScoringParameters::default();
- let scorer = ProbabilisticScorer::new(params, &graph, &logger);
- if get_route(src, &payment_params, &graph.read_only(), None, amt, 42, &logger, &scorer, &random_seed_bytes).is_ok() {
+ let params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger);
+ if get_route(src, &payment_params, &graph.read_only(), None, amt, &logger, &scorer, ¶ms, &random_seed_bytes).is_ok() {
continue 'load_endpoints;
}
}
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
- let scorer_params = ProbabilisticScoringParameters::default();
- let mut scorer = ProbabilisticScorer::new(scorer_params, Arc::clone(&network_graph), Arc::clone(&logger));
+ let mut scorer_params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), Arc::clone(&network_graph), Arc::clone(&logger));
// First check set manual penalties are returned by the scorer.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
};
- scorer.set_manual_penalty(&NodeId::from_pubkey(&nodes[3]), 123);
- scorer.set_manual_penalty(&NodeId::from_pubkey(&nodes[4]), 456);
- assert_eq!(scorer.channel_penalty_msat(42, &NodeId::from_pubkey(&nodes[3]), &NodeId::from_pubkey(&nodes[4]), usage), 456);
+ scorer_params.set_manual_penalty(&NodeId::from_pubkey(&nodes[3]), 123);
+ scorer_params.set_manual_penalty(&NodeId::from_pubkey(&nodes[4]), 456);
+ assert_eq!(scorer.channel_penalty_msat(42, &NodeId::from_pubkey(&nodes[3]), &NodeId::from_pubkey(&nodes[4]), usage, &scorer_params), 456);
// Then check we can get a normal route
let payment_params = PaymentParameters::from_node_id(nodes[10], 42);
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes);
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
assert!(route.is_ok());
// Then check that we can't get a route if we ban an intermediate node.
- scorer.add_banned(&NodeId::from_pubkey(&nodes[3]));
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes);
+ scorer_params.add_banned(&NodeId::from_pubkey(&nodes[3]));
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
assert!(route.is_err());
// Finally make sure we can route again, when we remove the ban.
- scorer.remove_banned(&NodeId::from_pubkey(&nodes[3]));
- let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, 42, Arc::clone(&logger), &scorer, &random_seed_bytes);
+ scorer_params.remove_banned(&NodeId::from_pubkey(&nodes[3]));
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
assert!(route.is_ok());
}
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use crate::chain::transaction::OutPoint;
- use crate::chain::keysinterface::{EntropySource, KeysManager};
+ use crate::sign::{EntropySource, KeysManager};
use crate::ln::channelmanager::{self, ChannelCounterparty, ChannelDetails};
use crate::ln::features::InvoiceFeatures;
use crate::routing::gossip::NetworkGraph;
- use crate::routing::scoring::{FixedPenaltyScorer, ProbabilisticScorer, ProbabilisticScoringParameters};
+ use crate::routing::scoring::{FixedPenaltyScorer, ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
use crate::util::config::UserConfig;
use crate::util::logger::{Logger, Record};
use crate::util::ser::ReadableArgs;
let logger = DummyLogger {};
let network_graph = read_network_graph(&logger);
let scorer = FixedPenaltyScorer::with_penalty(0);
- generate_routes(bench, &network_graph, scorer, InvoiceFeatures::empty());
+ generate_routes(bench, &network_graph, scorer, &(), InvoiceFeatures::empty());
}
#[bench]
let logger = DummyLogger {};
let network_graph = read_network_graph(&logger);
let scorer = FixedPenaltyScorer::with_penalty(0);
- generate_routes(bench, &network_graph, scorer, channelmanager::provided_invoice_features(&UserConfig::default()));
+ generate_routes(bench, &network_graph, scorer, &(), channelmanager::provided_invoice_features(&UserConfig::default()));
}
#[bench]
fn generate_routes_with_probabilistic_scorer(bench: &mut Bencher) {
let logger = DummyLogger {};
let network_graph = read_network_graph(&logger);
- let params = ProbabilisticScoringParameters::default();
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- generate_routes(bench, &network_graph, scorer, InvoiceFeatures::empty());
+ let params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+ generate_routes(bench, &network_graph, scorer, ¶ms, InvoiceFeatures::empty());
}
#[bench]
fn generate_mpp_routes_with_probabilistic_scorer(bench: &mut Bencher) {
let logger = DummyLogger {};
let network_graph = read_network_graph(&logger);
- let params = ProbabilisticScoringParameters::default();
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- generate_routes(bench, &network_graph, scorer, channelmanager::provided_invoice_features(&UserConfig::default()));
+ let params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+ generate_routes(bench, &network_graph, scorer, ¶ms, channelmanager::provided_invoice_features(&UserConfig::default()));
}
fn generate_routes<S: Score>(
- bench: &mut Bencher, graph: &NetworkGraph<&DummyLogger>, mut scorer: S,
+ bench: &mut Bencher, graph: &NetworkGraph<&DummyLogger>, mut scorer: S, score_params: &S::ScoreParams,
features: InvoiceFeatures
) {
let nodes = graph.read_only().nodes().clone();
let src = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
seed *= 0xdeadbeef;
let dst = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
- let params = PaymentParameters::from_node_id(dst, 42).with_features(features.clone());
+ let params = PaymentParameters::from_node_id(dst, 42).with_bolt11_features(features.clone()).unwrap();
let first_hop = first_hop(src);
let amt = seed as u64 % 1_000_000;
- if let Ok(route) = get_route(&payer, ¶ms, &graph.read_only(), Some(&[&first_hop]), amt, 42, &DummyLogger{}, &scorer, &random_seed_bytes) {
+ if let Ok(route) = get_route(&payer, ¶ms, &graph.read_only(), Some(&[&first_hop]), amt, &DummyLogger{}, &scorer, score_params, &random_seed_bytes) {
routes.push(route);
route_endpoints.push((first_hop, params, amt));
continue 'load_endpoints;
// selected destinations, possibly causing us to fail because, eg, the newly-selected path
// requires a too-high CLTV delta.
route_endpoints.retain(|(first_hop, params, amt)| {
- get_route(&payer, params, &graph.read_only(), Some(&[first_hop]), *amt, 42, &DummyLogger{}, &scorer, &random_seed_bytes).is_ok()
+ get_route(&payer, params, &graph.read_only(), Some(&[first_hop]), *amt, &DummyLogger{}, &scorer, score_params, &random_seed_bytes).is_ok()
});
route_endpoints.truncate(100);
assert_eq!(route_endpoints.len(), 100);
let mut idx = 0;
bench.iter(|| {
let (first_hop, params, amt) = &route_endpoints[idx % route_endpoints.len()];
- assert!(get_route(&payer, params, &graph.read_only(), Some(&[first_hop]), *amt, 42, &DummyLogger{}, &scorer, &random_seed_bytes).is_ok());
+ assert!(get_route(&payer, params, &graph.read_only(), Some(&[first_hop]), *amt, &DummyLogger{}, &scorer, score_params, &random_seed_bytes).is_ok());
idx += 1;
});
}
//! #
//! # use lightning::routing::gossip::NetworkGraph;
//! # use lightning::routing::router::{RouteParameters, find_route};
-//! # use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters};
-//! # use lightning::chain::keysinterface::KeysManager;
+//! # use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
+//! # use lightning::sign::KeysManager;
//! # use lightning::util::logger::{Logger, Record};
//! # use bitcoin::secp256k1::PublicKey;
//! #
//! # let logger = FakeLogger {};
//! #
//! // Use the default channel penalties.
-//! let params = ProbabilisticScoringParameters::default();
-//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+//! let params = ProbabilisticScoringFeeParameters::default();
+//! let decay_params = ProbabilisticScoringDecayParameters::default();
+//! let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
//!
//! // Or use custom channel penalties.
-//! let params = ProbabilisticScoringParameters {
-//! liquidity_penalty_multiplier_msat: 2 * 1000,
-//! ..ProbabilisticScoringParameters::default()
+//! let params = ProbabilisticScoringFeeParameters {
+//! liquidity_penalty_multiplier_msat: 2 * 1000,
+//! ..ProbabilisticScoringFeeParameters::default()
//! };
-//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+//! let decay_params = ProbabilisticScoringDecayParameters::default();
+//! let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
//! # let random_seed_bytes = [42u8; 32];
//!
-//! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer, &random_seed_bytes);
+//! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer, ¶ms, &random_seed_bytes);
//! # }
//! ```
//!
///
/// Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
pub trait Score $(: $supertrait)* {
+ /// A configurable type which should contain various passed-in parameters for configuring the scorer,
+ /// on a per-routefinding-call basis through to the scorer methods,
+ /// which are used to determine the parameters for the suitability of channels for use.
+ type ScoreParams;
/// Returns the fee in msats willing to be paid to avoid routing `send_amt_msat` through the
/// given channel in the direction from `source` to `target`.
///
/// [`u64::max_value`] is given to indicate sufficient capacity for the invoice's full amount.
/// Thus, implementations should be overflow-safe.
fn channel_penalty_msat(
- &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
+ &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
) -> u64;
/// Handles updating channel penalties after failing to route through a channel.
}
impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
+ type ScoreParams = S::ScoreParams;
fn channel_penalty_msat(
- &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
+ &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
) -> u64 {
- self.deref().channel_penalty_msat(short_channel_id, source, target, usage)
+ self.deref().channel_penalty_msat(short_channel_id, source, target, usage, score_params)
}
fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) {
pub struct MultiThreadedScoreLock<'a, S: Score>(MutexGuard<'a, S>);
#[cfg(c_bindings)]
impl<'a, T: Score + 'a> Score for MultiThreadedScoreLock<'a, T> {
- fn channel_penalty_msat(&self, scid: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage) -> u64 {
- self.0.channel_penalty_msat(scid, source, target, usage)
+ type ScoreParams = <T as Score>::ScoreParams;
+ fn channel_penalty_msat(&self, scid: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams) -> u64 {
+ self.0.channel_penalty_msat(scid, source, target, usage, score_params)
}
fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) {
self.0.payment_path_failed(path, short_channel_id)
}
impl Score for FixedPenaltyScorer {
- fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage) -> u64 {
+ type ScoreParams = ();
+ fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage, _score_params: &Self::ScoreParams) -> u64 {
self.penalty_msat
}
/// behavior.
///
/// [1]: https://arxiv.org/abs/2107.05322
-/// [`liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_multiplier_msat
-/// [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_amount_multiplier_msat
-/// [`liquidity_offset_half_life`]: ProbabilisticScoringParameters::liquidity_offset_half_life
-/// [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_multiplier_msat
-/// [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_amount_multiplier_msat
+/// [`liquidity_penalty_multiplier_msat`]: ProbabilisticScoringFeeParameters::liquidity_penalty_multiplier_msat
+/// [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::liquidity_penalty_amount_multiplier_msat
+/// [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life
+/// [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_multiplier_msat
+/// [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_amount_multiplier_msat
pub type ProbabilisticScorer<G, L> = ProbabilisticScorerUsingTime::<G, L, ConfiguredTime>;
/// Probabilistic [`Score`] implementation.
/// This is not exported to bindings users generally all users should use the [`ProbabilisticScorer`] type alias.
pub struct ProbabilisticScorerUsingTime<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time>
where L::Target: Logger {
- params: ProbabilisticScoringParameters,
+ decay_params: ProbabilisticScoringDecayParameters,
network_graph: G,
logger: L,
// TODO: Remove entries of closed channels.
/// The penalty applied to any channel by the [`ProbabilisticScorer`] is the sum of each of the
/// parameters here.
#[derive(Clone)]
-pub struct ProbabilisticScoringParameters {
+pub struct ProbabilisticScoringFeeParameters {
/// A fixed penalty in msats to apply to each channel.
///
/// Default value: 500 msat
///
/// Default value: 30,000 msat
///
- /// [`liquidity_offset_half_life`]: Self::liquidity_offset_half_life
+ /// [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life
pub liquidity_penalty_multiplier_msat: u64,
- /// Whenever this amount of time elapses since the last update to a channel's liquidity bounds,
- /// the distance from the bounds to "zero" is cut in half. In other words, the lower-bound on
- /// the available liquidity is halved and the upper-bound moves half-way to the channel's total
- /// capacity.
- ///
- /// Because halving the liquidity bounds grows the uncertainty on the channel's liquidity,
- /// the penalty for an amount within the new bounds may change. See the [`ProbabilisticScorer`]
- /// struct documentation for more info on the way the liquidity bounds are used.
- ///
- /// For example, if the channel's capacity is 1 million sats, and the current upper and lower
- /// liquidity bounds are 200,000 sats and 600,000 sats, after this amount of time the upper
- /// and lower liquidity bounds will be decayed to 100,000 and 800,000 sats.
- ///
- /// Default value: 6 hours
- ///
- /// # Note
- ///
- /// When built with the `no-std` feature, time will never elapse. Therefore, the channel
- /// liquidity knowledge will never decay except when the bounds cross.
- pub liquidity_offset_half_life: Duration,
-
/// A multiplier used in conjunction with a payment amount and the negative `log10` of the
/// channel's success probability for the payment, as determined by our latest estimates of the
/// channel's liquidity, to determine the amount penalty.
/// [`liquidity_penalty_amount_multiplier_msat`]: Self::liquidity_penalty_amount_multiplier_msat
pub historical_liquidity_penalty_amount_multiplier_msat: u64,
- /// If we aren't learning any new datapoints for a channel, the historical liquidity bounds
- /// tracking can simply live on with increasingly stale data. Instead, when a channel has not
- /// seen a liquidity estimate update for this amount of time, the historical datapoints are
- /// decayed by half.
- ///
- /// Note that after 16 or more half lives all historical data will be completely gone.
- ///
- /// Default value: 14 days
- pub historical_no_updates_half_life: Duration,
-
/// Manual penalties used for the given nodes. Allows to set a particular penalty for a given
/// node. Note that a manual penalty of `u64::max_value()` means the node would not ever be
/// considered during path finding.
pub manual_node_penalties: HashMap<NodeId, u64>,
/// This penalty is applied when `htlc_maximum_msat` is equal to or larger than half of the
- /// channel's capacity, which makes us prefer nodes with a smaller `htlc_maximum_msat`. We
- /// treat such nodes preferentially as this makes balance discovery attacks harder to execute,
- /// thereby creating an incentive to restrict `htlc_maximum_msat` and improve privacy.
+ /// channel's capacity, (ie. htlc_maximum_msat ≥ 0.5 * channel_capacity) which makes us
+ /// prefer nodes with a smaller `htlc_maximum_msat`. We treat such nodes preferentially
+ /// as this makes balance discovery attacks harder to execute, thereby creating an incentive
+ /// to restrict `htlc_maximum_msat` and improve privacy.
///
/// Default value: 250 msat
pub anti_probing_penalty_msat: u64,
pub considered_impossible_penalty_msat: u64,
}
+impl Default for ProbabilisticScoringFeeParameters {
+ fn default() -> Self {
+ Self {
+ base_penalty_msat: 500,
+ base_penalty_amount_multiplier_msat: 8192,
+ liquidity_penalty_multiplier_msat: 30_000,
+ liquidity_penalty_amount_multiplier_msat: 192,
+ manual_node_penalties: HashMap::new(),
+ anti_probing_penalty_msat: 250,
+ considered_impossible_penalty_msat: 1_0000_0000_000,
+ historical_liquidity_penalty_multiplier_msat: 10_000,
+ historical_liquidity_penalty_amount_multiplier_msat: 64,
+ }
+ }
+}
+
+impl ProbabilisticScoringFeeParameters {
+ /// Marks the node with the given `node_id` as banned,
+ /// i.e it will be avoided during path finding.
+ pub fn add_banned(&mut self, node_id: &NodeId) {
+ self.manual_node_penalties.insert(*node_id, u64::max_value());
+ }
+
+ /// Marks all nodes in the given list as banned, i.e.,
+ /// they will be avoided during path finding.
+ pub fn add_banned_from_list(&mut self, node_ids: Vec<NodeId>) {
+ for id in node_ids {
+ self.manual_node_penalties.insert(id, u64::max_value());
+ }
+ }
+
+ /// Removes the node with the given `node_id` from the list of nodes to avoid.
+ pub fn remove_banned(&mut self, node_id: &NodeId) {
+ self.manual_node_penalties.remove(node_id);
+ }
+
+ /// Sets a manual penalty for the given node.
+ pub fn set_manual_penalty(&mut self, node_id: &NodeId, penalty: u64) {
+ self.manual_node_penalties.insert(*node_id, penalty);
+ }
+
+ /// Removes the node with the given `node_id` from the list of manual penalties.
+ pub fn remove_manual_penalty(&mut self, node_id: &NodeId) {
+ self.manual_node_penalties.remove(node_id);
+ }
+
+ /// Clears the list of manual penalties that are applied during path finding.
+ pub fn clear_manual_penalties(&mut self) {
+ self.manual_node_penalties = HashMap::new();
+ }
+}
+
+#[cfg(test)]
+impl ProbabilisticScoringFeeParameters {
+ fn zero_penalty() -> Self {
+ Self {
+ base_penalty_msat: 0,
+ base_penalty_amount_multiplier_msat: 0,
+ liquidity_penalty_multiplier_msat: 0,
+ liquidity_penalty_amount_multiplier_msat: 0,
+ historical_liquidity_penalty_multiplier_msat: 0,
+ historical_liquidity_penalty_amount_multiplier_msat: 0,
+ manual_node_penalties: HashMap::new(),
+ anti_probing_penalty_msat: 0,
+ considered_impossible_penalty_msat: 0,
+ }
+ }
+}
+
+/// Parameters for configuring [`ProbabilisticScorer`].
+///
+/// Used to configure decay parameters that are static throughout the lifetime of the scorer.
+/// these decay parameters affect the score of the channel penalty and are not changed on a
+/// per-route penalty cost call.
+#[derive(Copy, Clone)]
+pub struct ProbabilisticScoringDecayParameters {
+ /// If we aren't learning any new datapoints for a channel, the historical liquidity bounds
+ /// tracking can simply live on with increasingly stale data. Instead, when a channel has not
+ /// seen a liquidity estimate update for this amount of time, the historical datapoints are
+ /// decayed by half.
+ /// For an example of historical_no_updates_half_life being used see [`historical_estimated_channel_liquidity_probabilities`]
+ ///
+ /// Note that after 16 or more half lives all historical data will be completely gone.
+ ///
+ /// Default value: 14 days
+ ///
+ /// [`historical_estimated_channel_liquidity_probabilities`]: ProbabilisticScorerUsingTime::historical_estimated_channel_liquidity_probabilities
+ pub historical_no_updates_half_life: Duration,
+
+ /// Whenever this amount of time elapses since the last update to a channel's liquidity bounds,
+ /// the distance from the bounds to "zero" is cut in half. In other words, the lower-bound on
+ /// the available liquidity is halved and the upper-bound moves half-way to the channel's total
+ /// capacity.
+ ///
+ /// Because halving the liquidity bounds grows the uncertainty on the channel's liquidity,
+ /// the penalty for an amount within the new bounds may change. See the [`ProbabilisticScorer`]
+ /// struct documentation for more info on the way the liquidity bounds are used.
+ ///
+ /// For example, if the channel's capacity is 1 million sats, and the current upper and lower
+ /// liquidity bounds are 200,000 sats and 600,000 sats, after this amount of time the upper
+ /// and lower liquidity bounds will be decayed to 100,000 and 800,000 sats.
+ ///
+ /// Default value: 6 hours
+ ///
+ /// # Note
+ ///
+ /// When built with the `no-std` feature, time will never elapse. Therefore, the channel
+ /// liquidity knowledge will never decay except when the bounds cross.
+ pub liquidity_offset_half_life: Duration,
+}
+
+impl Default for ProbabilisticScoringDecayParameters {
+ fn default() -> Self {
+ Self {
+ liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
+ historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
+ }
+ }
+}
+
+#[cfg(test)]
+impl ProbabilisticScoringDecayParameters {
+ fn zero_penalty() -> Self {
+ Self {
+ liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
+ historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
+ }
+ }
+}
+
/// Tracks the historical state of a distribution as a weighted average of how much time was spent
/// in each of 8 buckets.
#[derive(Clone, Copy)]
/// A snapshot of [`ChannelLiquidity`] in one direction assuming a certain channel capacity and
/// decayed with a given half life.
-struct DirectedChannelLiquidity<'a, L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> {
+struct DirectedChannelLiquidity<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> {
min_liquidity_offset_msat: L,
max_liquidity_offset_msat: L,
min_liquidity_offset_history: BRT,
capacity_msat: u64,
last_updated: U,
now: T,
- params: &'a ProbabilisticScoringParameters,
+ decay_params: ProbabilisticScoringDecayParameters,
}
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
/// Creates a new scorer using the given scoring parameters for sending payments from a node
/// through a network graph.
- pub fn new(params: ProbabilisticScoringParameters, network_graph: G, logger: L) -> Self {
+ pub fn new(decay_params: ProbabilisticScoringDecayParameters, network_graph: G, logger: L) -> Self {
Self {
- params,
+ decay_params,
network_graph,
logger,
channel_liquidities: HashMap::new(),
let log_direction = |source, target| {
if let Some((directed_info, _)) = chan_debug.as_directed_to(target) {
let amt = directed_info.effective_capacity().as_msat();
- let dir_liq = liq.as_directed(source, target, 0, amt, &self.params);
+ let dir_liq = liq.as_directed(source, target, 0, amt, self.decay_params);
let buckets = HistoricalMinMaxBuckets {
min_liquidity_offset_history: &dir_liq.min_liquidity_offset_history,
max_liquidity_offset_history: &dir_liq.max_liquidity_offset_history,
};
let (min_buckets, max_buckets, _) = buckets.get_decayed_buckets(now,
- *dir_liq.last_updated, self.params.historical_no_updates_half_life);
+ *dir_liq.last_updated, self.decay_params.historical_no_updates_half_life);
log_debug!(self.logger, core::concat!(
"Liquidity from {} to {} via {} is in the range ({}, {}).\n",
if let Some(liq) = self.channel_liquidities.get(&scid) {
if let Some((directed_info, source)) = chan.as_directed_to(target) {
let amt = directed_info.effective_capacity().as_msat();
- let dir_liq = liq.as_directed(source, target, 0, amt, &self.params);
+ let dir_liq = liq.as_directed(source, target, 0, amt, self.decay_params);
return Some((dir_liq.min_liquidity_msat(), dir_liq.max_liquidity_msat()));
}
}
if let Some(liq) = self.channel_liquidities.get(&scid) {
if let Some((directed_info, source)) = chan.as_directed_to(target) {
let amt = directed_info.effective_capacity().as_msat();
- let dir_liq = liq.as_directed(source, target, 0, amt, &self.params);
+ let dir_liq = liq.as_directed(source, target, 0, amt, self.decay_params);
let buckets = HistoricalMinMaxBuckets {
min_liquidity_offset_history: &dir_liq.min_liquidity_offset_history,
max_liquidity_offset_history: &dir_liq.max_liquidity_offset_history,
};
let (min_buckets, mut max_buckets, _) = buckets.get_decayed_buckets(T::now(),
- *dir_liq.last_updated, self.params.historical_no_updates_half_life);
+ *dir_liq.last_updated, self.decay_params.historical_no_updates_half_life);
// Note that the liquidity buckets are an offset from the edge, so we inverse
// the max order to get the probabilities from zero.
max_buckets.reverse();
}
None
}
-
- /// Marks the node with the given `node_id` as banned, i.e.,
- /// it will be avoided during path finding.
- pub fn add_banned(&mut self, node_id: &NodeId) {
- self.params.manual_node_penalties.insert(*node_id, u64::max_value());
- }
-
- /// Removes the node with the given `node_id` from the list of nodes to avoid.
- pub fn remove_banned(&mut self, node_id: &NodeId) {
- self.params.manual_node_penalties.remove(node_id);
- }
-
- /// Sets a manual penalty for the given node.
- pub fn set_manual_penalty(&mut self, node_id: &NodeId, penalty: u64) {
- self.params.manual_node_penalties.insert(*node_id, penalty);
- }
-
- /// Removes the node with the given `node_id` from the list of manual penalties.
- pub fn remove_manual_penalty(&mut self, node_id: &NodeId) {
- self.params.manual_node_penalties.remove(node_id);
- }
-
- /// Clears the list of manual penalties that are applied during path finding.
- pub fn clear_manual_penalties(&mut self) {
- self.params.manual_node_penalties = HashMap::new();
- }
-}
-
-impl ProbabilisticScoringParameters {
- #[cfg(test)]
- fn zero_penalty() -> Self {
- Self {
- base_penalty_msat: 0,
- base_penalty_amount_multiplier_msat: 0,
- liquidity_penalty_multiplier_msat: 0,
- liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
- liquidity_penalty_amount_multiplier_msat: 0,
- historical_liquidity_penalty_multiplier_msat: 0,
- historical_liquidity_penalty_amount_multiplier_msat: 0,
- historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
- manual_node_penalties: HashMap::new(),
- anti_probing_penalty_msat: 0,
- considered_impossible_penalty_msat: 0,
- }
- }
-
- /// Marks all nodes in the given list as banned, i.e.,
- /// they will be avoided during path finding.
- pub fn add_banned_from_list(&mut self, node_ids: Vec<NodeId>) {
- for id in node_ids {
- self.manual_node_penalties.insert(id, u64::max_value());
- }
- }
-}
-
-impl Default for ProbabilisticScoringParameters {
- fn default() -> Self {
- Self {
- base_penalty_msat: 500,
- base_penalty_amount_multiplier_msat: 8192,
- liquidity_penalty_multiplier_msat: 30_000,
- liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
- liquidity_penalty_amount_multiplier_msat: 192,
- historical_liquidity_penalty_multiplier_msat: 10_000,
- historical_liquidity_penalty_amount_multiplier_msat: 64,
- historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
- manual_node_penalties: HashMap::new(),
- anti_probing_penalty_msat: 250,
- considered_impossible_penalty_msat: 1_0000_0000_000,
- }
- }
}
impl<T: Time> ChannelLiquidity<T> {
/// Returns a view of the channel liquidity directed from `source` to `target` assuming
/// `capacity_msat`.
- fn as_directed<'a>(
- &self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64,
- params: &'a ProbabilisticScoringParameters
- ) -> DirectedChannelLiquidity<'a, &u64, &HistoricalBucketRangeTracker, T, &T> {
+ fn as_directed(
+ &self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
+ ) -> DirectedChannelLiquidity<&u64, &HistoricalBucketRangeTracker, T, &T> {
let (min_liquidity_offset_msat, max_liquidity_offset_msat, min_liquidity_offset_history, max_liquidity_offset_history) =
if source < target {
(&self.min_liquidity_offset_msat, &self.max_liquidity_offset_msat,
capacity_msat,
last_updated: &self.last_updated,
now: T::now(),
- params,
+ decay_params: decay_params,
}
}
/// Returns a mutable view of the channel liquidity directed from `source` to `target` assuming
/// `capacity_msat`.
- fn as_directed_mut<'a>(
- &mut self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64,
- params: &'a ProbabilisticScoringParameters
- ) -> DirectedChannelLiquidity<'a, &mut u64, &mut HistoricalBucketRangeTracker, T, &mut T> {
+ fn as_directed_mut(
+ &mut self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
+ ) -> DirectedChannelLiquidity<&mut u64, &mut HistoricalBucketRangeTracker, T, &mut T> {
let (min_liquidity_offset_msat, max_liquidity_offset_msat, min_liquidity_offset_history, max_liquidity_offset_history) =
if source < target {
(&mut self.min_liquidity_offset_msat, &mut self.max_liquidity_offset_msat,
capacity_msat,
last_updated: &mut self.last_updated,
now: T::now(),
- params,
+ decay_params: decay_params,
}
}
}
const AMOUNT_PENALTY_DIVISOR: u64 = 1 << 20;
const BASE_AMOUNT_PENALTY_DIVISOR: u64 = 1 << 30;
-impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> DirectedChannelLiquidity<'_, L, BRT, T, U> {
+impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> DirectedChannelLiquidity< L, BRT, T, U> {
/// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
/// this direction.
- fn penalty_msat(&self, amount_msat: u64, params: &ProbabilisticScoringParameters) -> u64 {
+ fn penalty_msat(&self, amount_msat: u64, score_params: &ProbabilisticScoringFeeParameters) -> u64 {
let max_liquidity_msat = self.max_liquidity_msat();
let min_liquidity_msat = core::cmp::min(self.min_liquidity_msat(), max_liquidity_msat);
// impossibility penalty.
let negative_log10_times_2048 = NEGATIVE_LOG10_UPPER_BOUND * 2048;
Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
- params.liquidity_penalty_multiplier_msat,
- params.liquidity_penalty_amount_multiplier_msat)
- .saturating_add(params.considered_impossible_penalty_msat)
+ score_params.liquidity_penalty_multiplier_msat,
+ score_params.liquidity_penalty_amount_multiplier_msat)
+ .saturating_add(score_params.considered_impossible_penalty_msat)
} else {
let numerator = (max_liquidity_msat - amount_msat).saturating_add(1);
let denominator = (max_liquidity_msat - min_liquidity_msat).saturating_add(1);
let negative_log10_times_2048 =
approx::negative_log10_times_2048(numerator, denominator);
Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
- params.liquidity_penalty_multiplier_msat,
- params.liquidity_penalty_amount_multiplier_msat)
+ score_params.liquidity_penalty_multiplier_msat,
+ score_params.liquidity_penalty_amount_multiplier_msat)
}
};
- if params.historical_liquidity_penalty_multiplier_msat != 0 ||
- params.historical_liquidity_penalty_amount_multiplier_msat != 0 {
+ if score_params.historical_liquidity_penalty_multiplier_msat != 0 ||
+ score_params.historical_liquidity_penalty_amount_multiplier_msat != 0 {
let payment_amt_64th_bucket = if amount_msat < u64::max_value() / 64 {
amount_msat * 64 / self.capacity_msat.saturating_add(1)
} else {
};
if let Some(cumulative_success_prob_times_billion) = buckets
.calculate_success_probability_times_billion(self.now, *self.last_updated,
- params.historical_no_updates_half_life, payment_amt_64th_bucket as u8)
+ self.decay_params.historical_no_updates_half_life, payment_amt_64th_bucket as u8)
{
let historical_negative_log10_times_2048 = approx::negative_log10_times_2048(cumulative_success_prob_times_billion + 1, 1024 * 1024 * 1024);
res = res.saturating_add(Self::combined_penalty_msat(amount_msat,
- historical_negative_log10_times_2048, params.historical_liquidity_penalty_multiplier_msat,
- params.historical_liquidity_penalty_amount_multiplier_msat));
+ historical_negative_log10_times_2048, score_params.historical_liquidity_penalty_multiplier_msat,
+ score_params.historical_liquidity_penalty_amount_multiplier_msat));
} else {
// If we don't have any valid points (or, once decayed, we have less than a full
// point), redo the non-historical calculation with no liquidity bounds tracked and
let negative_log10_times_2048 =
approx::negative_log10_times_2048(numerator, denominator);
res = res.saturating_add(Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
- params.historical_liquidity_penalty_multiplier_msat,
- params.historical_liquidity_penalty_amount_multiplier_msat));
+ score_params.historical_liquidity_penalty_multiplier_msat,
+ score_params.historical_liquidity_penalty_amount_multiplier_msat));
}
}
fn decayed_offset_msat(&self, offset_msat: u64) -> u64 {
self.now.duration_since(*self.last_updated).as_secs()
- .checked_div(self.params.liquidity_offset_half_life.as_secs())
+ .checked_div(self.decay_params.liquidity_offset_half_life.as_secs())
.and_then(|decays| offset_msat.checked_shr(decays as u32))
.unwrap_or(0)
}
}
-impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTracker>, T: Time, U: DerefMut<Target = T>> DirectedChannelLiquidity<'_, L, BRT, T, U> {
+impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTracker>, T: Time, U: DerefMut<Target = T>> DirectedChannelLiquidity<L, BRT, T, U> {
/// Adjusts the channel liquidity balance bounds when failing to route `amount_msat`.
fn failed_at_channel<Log: Deref>(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger {
let existing_max_msat = self.max_liquidity_msat();
fn update_history_buckets(&mut self) {
let half_lives = self.now.duration_since(*self.last_updated).as_secs()
- .checked_div(self.params.historical_no_updates_half_life.as_secs())
+ .checked_div(self.decay_params.historical_no_updates_half_life.as_secs())
.map(|v| v.try_into().unwrap_or(u32::max_value())).unwrap_or(u32::max_value());
self.min_liquidity_offset_history.time_decay_data(half_lives);
self.max_liquidity_offset_history.time_decay_data(half_lives);
}
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
+ type ScoreParams = ProbabilisticScoringFeeParameters;
fn channel_penalty_msat(
- &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
+ &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &ProbabilisticScoringFeeParameters
) -> u64 {
- if let Some(penalty) = self.params.manual_node_penalties.get(target) {
+ if let Some(penalty) = score_params.manual_node_penalties.get(target) {
return *penalty;
}
- let base_penalty_msat = self.params.base_penalty_msat.saturating_add(
- self.params.base_penalty_amount_multiplier_msat
+ let base_penalty_msat = score_params.base_penalty_msat.saturating_add(
+ score_params.base_penalty_amount_multiplier_msat
.saturating_mul(usage.amount_msat) / BASE_AMOUNT_PENALTY_DIVISOR);
let mut anti_probing_penalty_msat = 0;
},
EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat } => {
if htlc_maximum_msat >= capacity_msat/2 {
- anti_probing_penalty_msat = self.params.anti_probing_penalty_msat;
+ anti_probing_penalty_msat = score_params.anti_probing_penalty_msat;
}
},
_ => {},
self.channel_liquidities
.get(&short_channel_id)
.unwrap_or(&ChannelLiquidity::new())
- .as_directed(source, target, inflight_htlc_msat, capacity_msat, &self.params)
- .penalty_msat(amount_msat, &self.params)
+ .as_directed(source, target, inflight_htlc_msat, capacity_msat, self.decay_params)
+ .penalty_msat(amount_msat, score_params)
.saturating_add(anti_probing_penalty_msat)
.saturating_add(base_penalty_msat)
}
self.channel_liquidities
.entry(hop.short_channel_id)
.or_insert_with(ChannelLiquidity::new)
- .as_directed_mut(source, &target, 0, capacity_msat, &self.params)
+ .as_directed_mut(source, &target, 0, capacity_msat, self.decay_params)
.failed_at_channel(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
} else {
self.channel_liquidities
.entry(hop.short_channel_id)
.or_insert_with(ChannelLiquidity::new)
- .as_directed_mut(source, &target, 0, capacity_msat, &self.params)
+ .as_directed_mut(source, &target, 0, capacity_msat, self.decay_params)
.failed_downstream(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
}
} else {
self.channel_liquidities
.entry(hop.short_channel_id)
.or_insert_with(ChannelLiquidity::new)
- .as_directed_mut(source, &target, 0, capacity_msat, &self.params)
+ .as_directed_mut(source, &target, 0, capacity_msat, self.decay_params)
.successful(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
} else {
log_debug!(self.logger, "Not able to learn for channel with SCID {} as we do not have graph info for it (likely a route-hint last-hop).",
}
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time>
-ReadableArgs<(ProbabilisticScoringParameters, G, L)> for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
+ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
#[inline]
fn read<R: Read>(
- r: &mut R, args: (ProbabilisticScoringParameters, G, L)
+ r: &mut R, args: (ProbabilisticScoringDecayParameters, G, L)
) -> Result<Self, DecodeError> {
- let (params, network_graph, logger) = args;
+ let (decay_params, network_graph, logger) = args;
let mut channel_liquidities = HashMap::new();
read_tlv_fields!(r, {
(0, channel_liquidities, required),
});
Ok(Self {
- params,
+ decay_params,
network_graph,
logger,
channel_liquidities,
#[cfg(test)]
mod tests {
- use super::{ChannelLiquidity, HistoricalBucketRangeTracker, ProbabilisticScoringParameters, ProbabilisticScorerUsingTime};
+ use super::{ChannelLiquidity, HistoricalBucketRangeTracker, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters, ProbabilisticScorerUsingTime};
use crate::blinded_path::{BlindedHop, BlindedPath};
use crate::util::config::UserConfig;
use crate::util::time::Time;
let logger = TestLogger::new();
let last_updated = SinceEpoch::now();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters::default();
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+ let decay_params = ProbabilisticScoringDecayParameters::default();
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
.with_channel(42,
ChannelLiquidity {
min_liquidity_offset_msat: 700, max_liquidity_offset_msat: 100, last_updated,
// Update minimum liquidity.
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 100);
assert_eq!(liquidity.max_liquidity_msat(), 300);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 700);
assert_eq!(liquidity.max_liquidity_msat(), 900);
scorer.channel_liquidities.get_mut(&42).unwrap()
- .as_directed_mut(&source, &target, 0, 1_000, &scorer.params)
+ .as_directed_mut(&source, &target, 0, 1_000, decay_params)
.set_min_liquidity_msat(200);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 200);
assert_eq!(liquidity.max_liquidity_msat(), 300);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 700);
assert_eq!(liquidity.max_liquidity_msat(), 800);
// Update maximum liquidity.
let liquidity = scorer.channel_liquidities.get(&43).unwrap()
- .as_directed(&target, &recipient, 0, 1_000, &scorer.params);
+ .as_directed(&target, &recipient, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 700);
assert_eq!(liquidity.max_liquidity_msat(), 900);
let liquidity = scorer.channel_liquidities.get(&43).unwrap()
- .as_directed(&recipient, &target, 0, 1_000, &scorer.params);
+ .as_directed(&recipient, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 100);
assert_eq!(liquidity.max_liquidity_msat(), 300);
scorer.channel_liquidities.get_mut(&43).unwrap()
- .as_directed_mut(&target, &recipient, 0, 1_000, &scorer.params)
+ .as_directed_mut(&target, &recipient, 0, 1_000, decay_params)
.set_max_liquidity_msat(200);
let liquidity = scorer.channel_liquidities.get(&43).unwrap()
- .as_directed(&target, &recipient, 0, 1_000, &scorer.params);
+ .as_directed(&target, &recipient, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 0);
assert_eq!(liquidity.max_liquidity_msat(), 200);
let liquidity = scorer.channel_liquidities.get(&43).unwrap()
- .as_directed(&recipient, &target, 0, 1_000, &scorer.params);
+ .as_directed(&recipient, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 800);
assert_eq!(liquidity.max_liquidity_msat(), 1000);
}
let logger = TestLogger::new();
let last_updated = SinceEpoch::now();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters::default();
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+ let decay_params = ProbabilisticScoringDecayParameters::default();
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
.with_channel(42,
ChannelLiquidity {
min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated,
// Check initial bounds.
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 400);
assert_eq!(liquidity.max_liquidity_msat(), 800);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 200);
assert_eq!(liquidity.max_liquidity_msat(), 600);
// Reset from source to target.
scorer.channel_liquidities.get_mut(&42).unwrap()
- .as_directed_mut(&source, &target, 0, 1_000, &scorer.params)
+ .as_directed_mut(&source, &target, 0, 1_000, decay_params)
.set_min_liquidity_msat(900);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 900);
assert_eq!(liquidity.max_liquidity_msat(), 1_000);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 0);
assert_eq!(liquidity.max_liquidity_msat(), 100);
// Reset from target to source.
scorer.channel_liquidities.get_mut(&42).unwrap()
- .as_directed_mut(&target, &source, 0, 1_000, &scorer.params)
+ .as_directed_mut(&target, &source, 0, 1_000, decay_params)
.set_min_liquidity_msat(400);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 0);
assert_eq!(liquidity.max_liquidity_msat(), 600);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 400);
assert_eq!(liquidity.max_liquidity_msat(), 1_000);
}
let logger = TestLogger::new();
let last_updated = SinceEpoch::now();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters::default();
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+ let decay_params = ProbabilisticScoringDecayParameters::default();
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
.with_channel(42,
ChannelLiquidity {
min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated,
// Check initial bounds.
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 400);
assert_eq!(liquidity.max_liquidity_msat(), 800);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 200);
assert_eq!(liquidity.max_liquidity_msat(), 600);
// Reset from source to target.
scorer.channel_liquidities.get_mut(&42).unwrap()
- .as_directed_mut(&source, &target, 0, 1_000, &scorer.params)
+ .as_directed_mut(&source, &target, 0, 1_000, decay_params)
.set_max_liquidity_msat(300);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 0);
assert_eq!(liquidity.max_liquidity_msat(), 300);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 700);
assert_eq!(liquidity.max_liquidity_msat(), 1_000);
// Reset from target to source.
scorer.channel_liquidities.get_mut(&42).unwrap()
- .as_directed_mut(&target, &source, 0, 1_000, &scorer.params)
+ .as_directed_mut(&target, &source, 0, 1_000, decay_params)
.set_max_liquidity_msat(600);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 400);
assert_eq!(liquidity.max_liquidity_msat(), 1_000);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&target, &source, 0, 1_000, &scorer.params);
+ .as_directed(&target, &source, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 0);
assert_eq!(liquidity.max_liquidity_msat(), 600);
}
fn increased_penalty_nearing_liquidity_upper_bound() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let decay_params = ProbabilisticScoringDecayParameters::default();
+ let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 10_240, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 102_400, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 47);
let usage = ChannelUsage { amount_msat: 1_023_999, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2_000);
let usage = ChannelUsage {
amount_msat: 128,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 58);
let usage = ChannelUsage { amount_msat: 256, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
let usage = ChannelUsage { amount_msat: 374, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 198);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 198);
let usage = ChannelUsage { amount_msat: 512, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
let usage = ChannelUsage { amount_msat: 640, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 425);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 425);
let usage = ChannelUsage { amount_msat: 768, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 602);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 902);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 902);
}
#[test]
let logger = TestLogger::new();
let last_updated = SinceEpoch::now();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
considered_impossible_penalty_msat: u64::max_value(),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+ let decay_params = ProbabilisticScoringDecayParameters {
+ ..ProbabilisticScoringDecayParameters::zero_penalty()
+ };
+ let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
.with_channel(42,
ChannelLiquidity {
min_liquidity_offset_msat: 40, max_liquidity_offset_msat: 40, last_updated,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 100, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 50, ..usage };
- assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
- assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
let usage = ChannelUsage { amount_msat: 61, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
}
#[test]
fn does_not_further_penalize_own_channel() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let sender = sender_node_id();
let source = source_node_id();
let usage = ChannelUsage {
let failed_path = payment_path_for_amount(500);
let successful_path = payment_path_for_amount(200);
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
+ assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 301);
scorer.payment_path_failed(&failed_path, 41);
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
+ assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 301);
scorer.payment_path_successful(&successful_path);
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
+ assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 301);
}
#[test]
fn sets_liquidity_lower_bound_on_downstream_failure() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
let path = payment_path_for_amount(500);
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 128);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 301);
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 602);
scorer.payment_path_failed(&path, 43);
let usage = ChannelUsage { amount_msat: 250, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
}
#[test]
fn sets_liquidity_upper_bound_on_failure() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
considered_impossible_penalty_msat: u64::max_value(),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
let path = payment_path_for_amount(500);
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 128);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 301);
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 602);
scorer.payment_path_failed(&path, 42);
let usage = ChannelUsage { amount_msat: 250, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
}
#[test]
let node_c = NodeId::from_pubkey(&pub_c);
let node_d = NodeId::from_pubkey(&pub_d);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let usage = ChannelUsage {
amount_msat: 250,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage), 128);
+ assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage, ¶ms), 128);
// Note that a default liquidity bound is used for B -> C as no channel exists
- assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage), 128);
- assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage), 128);
+ assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, ¶ms), 128);
scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 43);
- assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage), 80);
+ assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage, ¶ms), 80);
// Note that a default liquidity bound is used for B -> C as no channel exists
- assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage), 128);
- assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage), 128);
+ assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, ¶ms), 128);
}
#[test]
fn reduces_liquidity_upper_bound_along_path_on_success() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let sender = sender_node_id();
let source = source_node_id();
let target = target_node_id();
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
- assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage), 128);
+ assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage, ¶ms), 128);
scorer.payment_path_successful(&payment_path_for_amount(500));
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
- assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage, ¶ms), 300);
}
#[test]
fn decays_liquidity_bounds_over_time() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- liquidity_offset_half_life: Duration::from_secs(10),
considered_impossible_penalty_msat: u64::max_value(),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let decay_params = ProbabilisticScoringDecayParameters {
+ liquidity_offset_half_life: Duration::from_secs(10),
+ ..ProbabilisticScoringDecayParameters::zero_penalty()
+ };
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_023, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2_000);
scorer.payment_path_failed(&payment_path_for_amount(768), 42);
scorer.payment_path_failed(&payment_path_for_amount(128), 43);
let usage = ChannelUsage { amount_msat: 128, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 256, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 93);
let usage = ChannelUsage { amount_msat: 768, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1_479);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
SinceEpoch::advance(Duration::from_secs(9));
let usage = ChannelUsage { amount_msat: 128, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 256, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 93);
let usage = ChannelUsage { amount_msat: 768, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1_479);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
SinceEpoch::advance(Duration::from_secs(1));
let usage = ChannelUsage { amount_msat: 64, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 128, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 34);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 34);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_970);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1_970);
let usage = ChannelUsage { amount_msat: 960, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
// Fully decay liquidity lower bound.
SinceEpoch::advance(Duration::from_secs(10 * 7));
let usage = ChannelUsage { amount_msat: 0, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_023, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2_000);
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
// Fully decay liquidity upper bound.
SinceEpoch::advance(Duration::from_secs(10));
let usage = ChannelUsage { amount_msat: 0, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
SinceEpoch::advance(Duration::from_secs(10));
let usage = ChannelUsage { amount_msat: 0, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
}
#[test]
fn decays_liquidity_bounds_without_shift_overflow() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
+ };
+ let decay_params = ProbabilisticScoringDecayParameters {
liquidity_offset_half_life: Duration::from_secs(10),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringDecayParameters::default()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
scorer.payment_path_failed(&payment_path_for_amount(512), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 281);
// An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
// would cause an overflow.
SinceEpoch::advance(Duration::from_secs(10 * 64));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
}
#[test]
fn restricts_liquidity_bounds_after_decay() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
+ };
+ let decay_params = ProbabilisticScoringDecayParameters {
liquidity_offset_half_life: Duration::from_secs(10),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringDecayParameters::default()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
// More knowledge gives higher confidence (256, 768), meaning a lower penalty.
scorer.payment_path_failed(&payment_path_for_amount(768), 42);
scorer.payment_path_failed(&payment_path_for_amount(256), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 281);
// Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 291);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 291);
// Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
// is closer to the upper bound, meaning a higher penalty.
scorer.payment_path_successful(&payment_path_for_amount(64));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 331);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 331);
// Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
// is closer to the lower bound, meaning a lower penalty.
scorer.payment_path_failed(&payment_path_for_amount(256), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 245);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 245);
// Further decaying affects the lower bound more than the upper bound (128, 928).
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 280);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 280);
}
#[test]
fn restores_persisted_liquidity_bounds() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- liquidity_offset_half_life: Duration::from_secs(10),
considered_impossible_penalty_msat: u64::max_value(),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
+ let decay_params = ProbabilisticScoringDecayParameters {
+ liquidity_offset_half_life: Duration::from_secs(10),
+ ..ProbabilisticScoringDecayParameters::default()
+ };
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
let usage = ChannelUsage {
};
scorer.payment_path_failed(&payment_path_for_amount(500), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 473);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 473);
scorer.payment_path_failed(&payment_path_for_amount(250), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
let mut serialized_scorer = Vec::new();
scorer.write(&mut serialized_scorer).unwrap();
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
let deserialized_scorer =
- <ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap();
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ <ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
+ assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
}
#[test]
fn decays_persisted_liquidity_bounds() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- liquidity_offset_half_life: Duration::from_secs(10),
considered_impossible_penalty_msat: u64::max_value(),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
+ let decay_params = ProbabilisticScoringDecayParameters {
+ liquidity_offset_half_life: Duration::from_secs(10),
+ ..ProbabilisticScoringDecayParameters::zero_penalty()
+ };
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
let usage = ChannelUsage {
};
scorer.payment_path_failed(&payment_path_for_amount(500), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
let mut serialized_scorer = Vec::new();
scorer.write(&mut serialized_scorer).unwrap();
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
let deserialized_scorer =
- <ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap();
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 473);
+ <ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
+ assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 473);
scorer.payment_path_failed(&payment_path_for_amount(250), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 365);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 365);
}
#[test]
// 50k sat reserve).
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters::default();
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 4375);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4375);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2739);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2739);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2236);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2236);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1983);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1983);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1637);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1637);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1606);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1606);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1331);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1331);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1387);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1387);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1379);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1379);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1363);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1363);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1355);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1355);
}
#[test]
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 58);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
- anti_probing_penalty_msat: 0, ..ProbabilisticScoringParameters::zero_penalty()
+ anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 558);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
base_penalty_amount_multiplier_msat: (1 << 30),
- anti_probing_penalty_msat: 0, ..ProbabilisticScoringParameters::zero_penalty()
+ anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558 + 128);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 558 + 128);
}
#[test]
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
};
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
liquidity_penalty_amount_multiplier_msat: 0,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
liquidity_penalty_amount_multiplier_msat: 256,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 337);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 337);
}
#[test]
effective_capacity: EffectiveCapacity::Infinite,
};
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 40_000,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 80_000);
+ let decay_params = ProbabilisticScoringDecayParameters::zero_penalty();
+ let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 80_000);
}
#[test]
fn accounts_for_inflight_htlc_usage() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
considered_impossible_penalty_msat: u64::max_value(),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
}
#[test]
fn removes_uncertainity_when_exact_liquidity_known() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters::default();
- let scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
+ let params = ProbabilisticScoringFeeParameters::default();
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), base_penalty_msat);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), base_penalty_msat);
let usage = ChannelUsage { amount_msat: 1_000, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), base_penalty_msat);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), base_penalty_msat);
let usage = ChannelUsage { amount_msat: 1_001, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
}
#[test]
fn remembers_historical_failures() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
- liquidity_offset_half_life: Duration::from_secs(60 * 60),
+ let params = ProbabilisticScoringFeeParameters {
historical_liquidity_penalty_multiplier_msat: 1024,
historical_liquidity_penalty_amount_multiplier_msat: 1024,
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
+ };
+ let decay_params = ProbabilisticScoringDecayParameters {
+ liquidity_offset_half_life: Duration::from_secs(60 * 60),
historical_no_updates_half_life: Duration::from_secs(10),
- ..ProbabilisticScoringParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
};
// With no historical data the normal liquidity penalty calculation is used.
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 47);
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
None);
scorer.payment_path_failed(&payment_path_for_amount(1), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2048);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2048);
// The "it failed" increment is 32, where the probability should lie fully in the first
// octile.
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
// Even after we tell the scorer we definitely have enough available liquidity, it will
// still remember that there was some failure in the past, and assign a non-0 penalty.
scorer.payment_path_failed(&payment_path_for_amount(1000), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 198);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 198);
// The first octile should be decayed just slightly and the last octile has a new point.
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
Some(([31, 0, 0, 0, 0, 0, 0, 32], [31, 0, 0, 0, 0, 0, 0, 32])));
// Advance the time forward 16 half-lives (which the docs claim will ensure all data is
// gone), and check that we're back to where we started.
SinceEpoch::advance(Duration::from_secs(10 * 16));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 47);
// Once fully decayed we still have data, but its all-0s. In the future we may remove the
// data entirely instead.
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
};
scorer.payment_path_failed(&payment_path_for_amount(1), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 409);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 409);
let usage = ChannelUsage {
amount_msat: 1,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::MaximumHTLC { amount_msat: 0 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &target, &source, usage), 2048);
+ assert_eq!(scorer.channel_penalty_msat(42, &target, &source, usage, ¶ms), 2048);
// Advance to decay all liquidity offsets to zero.
SinceEpoch::advance(Duration::from_secs(60 * 60 * 10));
let network_graph = network_graph(&logger);
let source = source_node_id();
let target = target_node_id();
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
anti_probing_penalty_msat: 500,
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
// Check we receive no penalty for a low htlc_maximum_msat.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_024_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 500);
// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity/2.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 512_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 500);
// Check we receive no anti-probing penalty for htlc_maximum_msat == channel_capacity/2 - 1.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 511_999 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
}
#[test]
// Make sure we'll account for a blinded path's final_value_msat in scoring
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
- let params = ProbabilisticScoringParameters {
+ let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
- liquidity_offset_half_life: Duration::from_secs(10),
- ..ProbabilisticScoringParameters::zero_penalty()
+ ..ProbabilisticScoringFeeParameters::zero_penalty()
};
- let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+ let decay_params = ProbabilisticScoringDecayParameters::default();
+ let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
let target = target_node_id();
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+ assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
let mut path = payment_path_for_amount(768);
let recipient_hop = path.hops.pop().unwrap();
scorer.payment_path_failed(&path, 43);
let liquidity = scorer.channel_liquidities.get(&42).unwrap()
- .as_directed(&source, &target, 0, 1_000, &scorer.params);
+ .as_directed(&source, &target, 0, 1_000, decay_params);
assert_eq!(liquidity.min_liquidity_msat(), 256);
assert_eq!(liquidity.max_liquidity_msat(), 768);
}
/// A trivial implementation of [`UtxoLookup`] which is used to call back into the network graph
/// once we have a concrete resolution of a request.
-struct UtxoResolver(Result<TxOut, UtxoLookupError>);
+pub(crate) struct UtxoResolver(Result<TxOut, UtxoLookupError>);
impl UtxoLookup for UtxoResolver {
fn get_utxo(&self, _genesis_hash: &BlockHash, _short_channel_id: u64) -> UtxoResult {
UtxoResult::Sync(self.0.clone())
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Provides keys to LDK and defines some useful objects describing spendable on-chain outputs.
+//!
+//! The provided output descriptors follow a custom LDK data format and are currently not fully
+//! compatible with Bitcoin Core output descriptors.
+
+use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, EcdsaSighashType};
+use bitcoin::blockdata::script::{Script, Builder};
+use bitcoin::blockdata::opcodes;
+use bitcoin::network::constants::Network;
+use bitcoin::psbt::PartiallySignedTransaction;
+use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber};
+use bitcoin::util::sighash;
+
+use bitcoin::bech32::u5;
+use bitcoin::hashes::{Hash, HashEngine};
+use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::hashes::sha256d::Hash as Sha256dHash;
+use bitcoin::hash_types::WPubkeyHash;
+
+use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
+use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Signing};
+use bitcoin::secp256k1::ecdh::SharedSecret;
+use bitcoin::secp256k1::ecdsa::RecoverableSignature;
+use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
+
+use crate::util::transaction_utils;
+use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
+use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
+use crate::chain::transaction::OutPoint;
+#[cfg(anchors)]
+use crate::events::bump_transaction::HTLCDescriptor;
+use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
+use crate::ln::{chan_utils, PaymentPreimage};
+use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
+use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
+use crate::ln::script::ShutdownScript;
+
+use crate::prelude::*;
+use core::convert::TryInto;
+use core::ops::Deref;
+use core::sync::atomic::{AtomicUsize, Ordering};
+use crate::io::{self, Error};
+use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+use crate::util::atomic_counter::AtomicCounter;
+use crate::util::chacha20::ChaCha20;
+use crate::util::invoice::construct_invoice_preimage;
+
+/// Used as initial key material, to be expanded into multiple secret keys (but not to be used
+/// directly). This is used within LDK to encrypt/decrypt inbound payment data.
+///
+/// This is not exported to bindings users as we just use `[u8; 32]` directly
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
+pub struct KeyMaterial(pub [u8; 32]);
+
+/// Information about a spendable output to a P2WSH script.
+///
+/// See [`SpendableOutputDescriptor::DelayedPaymentOutput`] for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct DelayedPaymentOutputDescriptor {
+ /// The outpoint which is spendable.
+ pub outpoint: OutPoint,
+ /// Per commitment point to derive the delayed payment key by key holder.
+ pub per_commitment_point: PublicKey,
+ /// The `nSequence` value which must be set in the spending input to satisfy the `OP_CSV` in
+ /// the witness_script.
+ pub to_self_delay: u16,
+ /// The output which is referenced by the given outpoint.
+ pub output: TxOut,
+ /// The revocation point specific to the commitment transaction which was broadcast. Used to
+ /// derive the witnessScript for this output.
+ pub revocation_pubkey: PublicKey,
+ /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`].
+ /// This may be useful in re-deriving keys used in the channel to spend the output.
+ pub channel_keys_id: [u8; 32],
+ /// The value of the channel which this output originated from, possibly indirectly.
+ pub channel_value_satoshis: u64,
+}
+impl DelayedPaymentOutputDescriptor {
+ /// The maximum length a well-formed witness spending one of these should have.
+ /// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte
+ /// shorter.
+ // Calculated as 1 byte length + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+ // redeemscript push length.
+ pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1;
+}
+
+impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
+ (0, outpoint, required),
+ (2, per_commitment_point, required),
+ (4, to_self_delay, required),
+ (6, output, required),
+ (8, revocation_pubkey, required),
+ (10, channel_keys_id, required),
+ (12, channel_value_satoshis, required),
+});
+
+/// Information about a spendable output to our "payment key".
+///
+/// See [`SpendableOutputDescriptor::StaticPaymentOutput`] for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct StaticPaymentOutputDescriptor {
+ /// The outpoint which is spendable.
+ pub outpoint: OutPoint,
+ /// The output which is referenced by the given outpoint.
+ pub output: TxOut,
+ /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`].
+ /// This may be useful in re-deriving keys used in the channel to spend the output.
+ pub channel_keys_id: [u8; 32],
+ /// The value of the channel which this transactions spends.
+ pub channel_value_satoshis: u64,
+}
+impl StaticPaymentOutputDescriptor {
+ /// The maximum length a well-formed witness spending one of these should have.
+ /// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte
+ /// shorter.
+ // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+ // redeemscript push length.
+ pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34;
+}
+impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, {
+ (0, outpoint, required),
+ (2, output, required),
+ (4, channel_keys_id, required),
+ (6, channel_value_satoshis, required),
+});
+
+/// Describes the necessary information to spend a spendable output.
+///
+/// When on-chain outputs are created by LDK (which our counterparty is not able to claim at any
+/// point in the future) a [`SpendableOutputs`] event is generated which you must track and be able
+/// to spend on-chain. The information needed to do this is provided in this enum, including the
+/// outpoint describing which `txid` and output `index` is available, the full output which exists
+/// at that `txid`/`index`, and any keys or other information required to sign.
+///
+/// [`SpendableOutputs`]: crate::events::Event::SpendableOutputs
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum SpendableOutputDescriptor {
+ /// An output to a script which was provided via [`SignerProvider`] directly, either from
+ /// [`get_destination_script`] or [`get_shutdown_scriptpubkey`], thus you should already
+ /// know how to spend it. No secret keys are provided as LDK was never given any key.
+ /// These may include outputs from a transaction punishing our counterparty or claiming an HTLC
+ /// on-chain using the payment preimage or after it has timed out.
+ ///
+ /// [`get_shutdown_scriptpubkey`]: SignerProvider::get_shutdown_scriptpubkey
+ /// [`get_destination_script`]: SignerProvider::get_shutdown_scriptpubkey
+ StaticOutput {
+ /// The outpoint which is spendable.
+ outpoint: OutPoint,
+ /// The output which is referenced by the given outpoint.
+ output: TxOut,
+ },
+ /// An output to a P2WSH script which can be spent with a single signature after an `OP_CSV`
+ /// delay.
+ ///
+ /// The witness in the spending input should be:
+ /// ```bitcoin
+ /// <BIP 143 signature> <empty vector> (MINIMALIF standard rule) <provided witnessScript>
+ /// ```
+ ///
+ /// Note that the `nSequence` field in the spending input must be set to
+ /// [`DelayedPaymentOutputDescriptor::to_self_delay`] (which means the transaction is not
+ /// broadcastable until at least [`DelayedPaymentOutputDescriptor::to_self_delay`] blocks after
+ /// the outpoint confirms, see [BIP
+ /// 68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki)). Also note that LDK
+ /// won't generate a [`SpendableOutputDescriptor`] until the corresponding block height
+ /// is reached.
+ ///
+ /// These are generally the result of a "revocable" output to us, spendable only by us unless
+ /// it is an output from an old state which we broadcast (which should never happen).
+ ///
+ /// To derive the delayed payment key which is used to sign this input, you must pass the
+ /// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which corresponds to the
+ /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`ChannelSigner::pubkeys`]) and the provided
+ /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The public key can be
+ /// generated without the secret key using [`chan_utils::derive_public_key`] and only the
+ /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`ChannelSigner::pubkeys`].
+ ///
+ /// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is
+ /// used in the witness script generation), you must pass the counterparty
+ /// [`ChannelPublicKeys::revocation_basepoint`] (which appears in the call to
+ /// [`ChannelSigner::provide_channel_parameters`]) and the provided
+ /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to
+ /// [`chan_utils::derive_public_revocation_key`].
+ ///
+ /// The witness script which is hashed and included in the output `script_pubkey` may be
+ /// regenerated by passing the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] (derived
+ /// as explained above), our delayed payment pubkey (derived as explained above), and the
+ /// [`DelayedPaymentOutputDescriptor::to_self_delay`] contained here to
+ /// [`chan_utils::get_revokeable_redeemscript`].
+ DelayedPaymentOutput(DelayedPaymentOutputDescriptor),
+ /// An output to a P2WPKH, spendable exclusively by our payment key (i.e., the private key
+ /// which corresponds to the `payment_point` in [`ChannelSigner::pubkeys`]). The witness
+ /// in the spending input is, thus, simply:
+ /// ```bitcoin
+ /// <BIP 143 signature> <payment key>
+ /// ```
+ ///
+ /// These are generally the result of our counterparty having broadcast the current state,
+ /// allowing us to claim the non-HTLC-encumbered outputs immediately.
+ StaticPaymentOutput(StaticPaymentOutputDescriptor),
+}
+
+impl_writeable_tlv_based_enum!(SpendableOutputDescriptor,
+ (0, StaticOutput) => {
+ (0, outpoint, required),
+ (2, output, required),
+ },
+;
+ (1, DelayedPaymentOutput),
+ (2, StaticPaymentOutput),
+);
+
+impl SpendableOutputDescriptor {
+ /// Turns this into a [`bitcoin::psbt::Input`] which can be used to create a
+ /// [`PartiallySignedTransaction`] which spends the given descriptor.
+ ///
+ /// Note that this does not include any signatures, just the information required to
+ /// construct the transaction and sign it.
+ pub fn to_psbt_input(&self) -> bitcoin::psbt::Input {
+ match self {
+ SpendableOutputDescriptor::StaticOutput { output, .. } => {
+ // Is a standard P2WPKH, no need for witness script
+ bitcoin::psbt::Input {
+ witness_utxo: Some(output.clone()),
+ ..Default::default()
+ }
+ },
+ SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
+ // TODO we could add the witness script as well
+ bitcoin::psbt::Input {
+ witness_utxo: Some(descriptor.output.clone()),
+ ..Default::default()
+ }
+ },
+ SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
+ // TODO we could add the witness script as well
+ bitcoin::psbt::Input {
+ witness_utxo: Some(descriptor.output.clone()),
+ ..Default::default()
+ }
+ },
+ }
+ }
+
+ /// Creates an unsigned [`PartiallySignedTransaction`] which spends the given descriptors to
+ /// the given outputs, plus an output to the given change destination (if sufficient
+ /// change value remains). The PSBT will have a feerate, at least, of the given value.
+ ///
+ /// The `locktime` argument is used to set the transaction's locktime. If `None`, the
+ /// transaction will have a locktime of 0. It it recommended to set this to the current block
+ /// height to avoid fee sniping, unless you have some specific reason to use a different
+ /// locktime.
+ ///
+ /// Returns the PSBT and expected max transaction weight.
+ ///
+ /// Returns `Err(())` if the output value is greater than the input value minus required fee,
+ /// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
+ /// does not match the one we can spend.
+ ///
+ /// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
+ pub fn create_spendable_outputs_psbt(descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, locktime: Option<PackedLockTime>) -> Result<(PartiallySignedTransaction, usize), ()> {
+ let mut input = Vec::with_capacity(descriptors.len());
+ let mut input_value = 0;
+ let mut witness_weight = 0;
+ let mut output_set = HashSet::with_capacity(descriptors.len());
+ for outp in descriptors {
+ match outp {
+ SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
+ if !output_set.insert(descriptor.outpoint) { return Err(()); }
+ input.push(TxIn {
+ previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: Sequence::ZERO,
+ witness: Witness::new(),
+ });
+ witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
+ #[cfg(feature = "grind_signatures")]
+ { witness_weight -= 1; } // Guarantees a low R signature
+ input_value += descriptor.output.value;
+ },
+ SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
+ if !output_set.insert(descriptor.outpoint) { return Err(()); }
+ input.push(TxIn {
+ previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: Sequence(descriptor.to_self_delay as u32),
+ witness: Witness::new(),
+ });
+ witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
+ #[cfg(feature = "grind_signatures")]
+ { witness_weight -= 1; } // Guarantees a low R signature
+ input_value += descriptor.output.value;
+ },
+ SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
+ if !output_set.insert(*outpoint) { return Err(()); }
+ input.push(TxIn {
+ previous_output: outpoint.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: Sequence::ZERO,
+ witness: Witness::new(),
+ });
+ witness_weight += 1 + 73 + 34;
+ #[cfg(feature = "grind_signatures")]
+ { witness_weight -= 1; } // Guarantees a low R signature
+ input_value += output.value;
+ }
+ }
+ if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
+ }
+ let mut tx = Transaction {
+ version: 2,
+ lock_time: locktime.unwrap_or(PackedLockTime::ZERO),
+ input,
+ output: outputs,
+ };
+ let expected_max_weight =
+ transaction_utils::maybe_add_change_output(&mut tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;
+
+ let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input()).collect::<Vec<_>>();
+ let psbt = PartiallySignedTransaction {
+ inputs: psbt_inputs,
+ outputs: vec![Default::default(); tx.output.len()],
+ unsigned_tx: tx,
+ xpub: Default::default(),
+ version: 0,
+ proprietary: Default::default(),
+ unknown: Default::default(),
+ };
+ Ok((psbt, expected_max_weight))
+ }
+}
+
+/// A trait to handle Lightning channel key material without concretizing the channel type or
+/// the signature mechanism.
+pub trait ChannelSigner {
+ /// Gets the per-commitment point for a specific commitment number
+ ///
+ /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
+ fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey;
+
+ /// Gets the commitment secret for a specific commitment number as part of the revocation process
+ ///
+ /// An external signer implementation should error here if the commitment was already signed
+ /// and should refuse to sign it in the future.
+ ///
+ /// May be called more than once for the same index.
+ ///
+ /// Note that the commitment number starts at `(1 << 48) - 1` and counts backwards.
+ // TODO: return a Result so we can signal a validation error
+ fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
+
+ /// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
+ ///
+ /// This is required in order for the signer to make sure that releasing a commitment
+ /// secret won't leave us without a broadcastable holder transaction.
+ /// Policy checks should be implemented in this function, including checking the amount
+ /// sent to us and checking the HTLCs.
+ ///
+ /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
+ /// A validating signer should ensure that an HTLC output is removed only when the matching
+ /// preimage is provided, or when the value to holder is restored.
+ ///
+ /// Note that all the relevant preimages will be provided, but there may also be additional
+ /// irrelevant or duplicate preimages.
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction,
+ preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
+
+ /// Returns the holder's channel public keys and basepoints.
+ fn pubkeys(&self) -> &ChannelPublicKeys;
+
+ /// Returns an arbitrary identifier describing the set of keys which are provided back to you in
+ /// some [`SpendableOutputDescriptor`] types. This should be sufficient to identify this
+ /// [`EcdsaChannelSigner`] object uniquely and lookup or re-derive its keys.
+ fn channel_keys_id(&self) -> [u8; 32];
+
+ /// Set the counterparty static channel data, including basepoints,
+ /// `counterparty_selected`/`holder_selected_contest_delay` and funding outpoint.
+ ///
+ /// This data is static, and will never change for a channel once set. For a given [`ChannelSigner`]
+ /// instance, LDK will call this method exactly once - either immediately after construction
+ /// (not including if done via [`SignerProvider::read_chan_signer`]) or when the funding
+ /// information has been generated.
+ ///
+ /// channel_parameters.is_populated() MUST be true.
+ fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters);
+}
+
+/// A trait to sign Lightning channel transactions as described in
+/// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md).
+///
+/// Signing services could be implemented on a hardware wallet and should implement signing
+/// policies in order to be secure. Please refer to the [VLS Policy
+/// Controls](https://gitlab.com/lightning-signer/validating-lightning-signer/-/blob/main/docs/policy-controls.md)
+/// for an example of such policies.
+pub trait EcdsaChannelSigner: ChannelSigner {
+ /// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
+ ///
+ /// Note that if signing fails or is rejected, the channel will be force-closed.
+ ///
+ /// Policy checks should be implemented in this function, including checking the amount
+ /// sent to us and checking the HTLCs.
+ ///
+ /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
+ /// A validating signer should ensure that an HTLC output is removed only when the matching
+ /// preimage is provided, or when the value to holder is restored.
+ ///
+ /// Note that all the relevant preimages will be provided, but there may also be additional
+ /// irrelevant or duplicate preimages.
+ //
+ // TODO: Document the things someone using this interface should enforce before signing.
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction,
+ preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<(Signature, Vec<Signature>), ()>;
+ /// Validate the counterparty's revocation.
+ ///
+ /// This is required in order for the signer to make sure that the state has moved
+ /// forward and it is safe to sign the next counterparty commitment.
+ fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>;
+ /// Creates a signature for a holder's commitment transaction and its claiming HTLC transactions.
+ ///
+ /// This will be called
+ /// - with a non-revoked `commitment_tx`.
+ /// - with the latest `commitment_tx` when we initiate a force-close.
+ /// - with the previous `commitment_tx`, just to get claiming HTLC
+ /// signatures, if we are reacting to a [`ChannelMonitor`]
+ /// [replica](https://github.com/lightningdevkit/rust-lightning/blob/main/GLOSSARY.md#monitor-replicas)
+ /// that decided to broadcast before it had been updated to the latest `commitment_tx`.
+ ///
+ /// This may be called multiple times for the same transaction.
+ ///
+ /// An external signer implementation should check that the commitment has not been revoked.
+ ///
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+ // TODO: Document the things someone using this interface should enforce before signing.
+ fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ /// Same as [`sign_holder_commitment_and_htlcs`], but exists only for tests to get access to
+ /// holder commitment transactions which will be broadcasted later, after the channel has moved
+ /// on to a newer state. Thus, needs its own method as [`sign_holder_commitment_and_htlcs`] may
+ /// enforce that we only ever get called once.
+ #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
+ fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ /// Create a signature for the given input in a transaction spending an HTLC transaction output
+ /// or a commitment transaction `to_local` output when our counterparty broadcasts an old state.
+ ///
+ /// A justice transaction may claim multiple outputs at the same time if timelocks are
+ /// similar, but only a signature for the input at index `input` should be signed for here.
+ /// It may be called multiple times for same output(s) if a fee-bump is needed with regards
+ /// to an upcoming timelock expiration.
+ ///
+ /// Amount is value of the output spent by this input, committed to in the BIP 143 signature.
+ ///
+ /// `per_commitment_key` is revocation secret which was provided by our counterparty when they
+ /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does
+ /// not allow the spending of any funds by itself (you need our holder `revocation_secret` to do
+ /// so).
+ fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64,
+ per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()>;
+ /// Create a signature for the given input in a transaction spending a commitment transaction
+ /// HTLC output when our counterparty broadcasts an old state.
+ ///
+ /// A justice transaction may claim multiple outputs at the same time if timelocks are
+ /// similar, but only a signature for the input at index `input` should be signed for here.
+ /// It may be called multiple times for same output(s) if a fee-bump is needed with regards
+ /// to an upcoming timelock expiration.
+ ///
+ /// `amount` is the value of the output spent by this input, committed to in the BIP 143
+ /// signature.
+ ///
+ /// `per_commitment_key` is revocation secret which was provided by our counterparty when they
+ /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does
+ /// not allow the spending of any funds by itself (you need our holder revocation_secret to do
+ /// so).
+ ///
+ /// `htlc` holds HTLC elements (hash, timelock), thus changing the format of the witness script
+ /// (which is committed to in the BIP 143 signatures).
+ fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64,
+ per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+ #[cfg(anchors)]
+ /// Computes the signature for a commitment transaction's HTLC output used as an input within
+ /// `htlc_tx`, which spends the commitment transaction at index `input`. The signature returned
+ /// must be be computed using [`EcdsaSighashType::All`]. Note that this should only be used to
+ /// sign HTLC transactions from channels supporting anchor outputs after all additional
+ /// inputs/outputs have been added to the transaction.
+ ///
+ /// [`EcdsaSighashType::All`]: bitcoin::blockdata::transaction::EcdsaSighashType::All
+ fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize,
+ htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()>;
+ /// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment
+ /// transaction, either offered or received.
+ ///
+ /// Such a transaction may claim multiples offered outputs at same time if we know the
+ /// preimage for each when we create it, but only the input at index `input` should be
+ /// signed for here. It may be called multiple times for same output(s) if a fee-bump is
+ /// needed with regards to an upcoming timelock expiration.
+ ///
+ /// `witness_script` is either an offered or received script as defined in BOLT3 for HTLC
+ /// outputs.
+ ///
+ /// `amount` is value of the output spent by this input, committed to in the BIP 143 signature.
+ ///
+ /// `per_commitment_point` is the dynamic point corresponding to the channel state
+ /// detected onchain. It has been generated by our counterparty and is used to derive
+ /// channel state keys, which are then included in the witness script and committed to in the
+ /// BIP 143 signature.
+ fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64,
+ per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+ /// Create a signature for a (proposed) closing transaction.
+ ///
+ /// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
+ /// chosen to forgo their output as dust.
+ fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction,
+ secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+ /// Computes the signature for a commitment transaction's anchor output used as an
+ /// input within `anchor_tx`, which spends the commitment transaction, at index `input`.
+ fn sign_holder_anchor_input(
+ &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
+ ) -> Result<Signature, ()>;
+ /// Signs a channel announcement message with our funding key proving it comes from one of the
+ /// channel participants.
+ ///
+ /// Channel announcements also require a signature from each node's network key. Our node
+ /// signature is computed through [`NodeSigner::sign_gossip_message`].
+ ///
+ /// Note that if this fails or is rejected, the channel will not be publicly announced and
+ /// our counterparty may (though likely will not) close the channel on us for violating the
+ /// protocol.
+ fn sign_channel_announcement_with_funding_key(
+ &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()>;
+}
+
+/// A writeable signer.
+///
+/// There will always be two instances of a signer per channel, one occupied by the
+/// [`ChannelManager`] and another by the channel's [`ChannelMonitor`].
+///
+/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+/// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+pub trait WriteableEcdsaChannelSigner: EcdsaChannelSigner + Writeable {}
+
+/// Specifies the recipient of an invoice.
+///
+/// This indicates to [`NodeSigner::sign_invoice`] what node secret key should be used to sign
+/// the invoice.
+pub enum Recipient {
+ /// The invoice should be signed with the local node secret key.
+ Node,
+ /// The invoice should be signed with the phantom node secret key. This secret key must be the
+ /// same for all nodes participating in the [phantom node payment].
+ ///
+ /// [phantom node payment]: PhantomKeysManager
+ PhantomNode,
+}
+
+/// A trait that describes a source of entropy.
+pub trait EntropySource {
+ /// Gets a unique, cryptographically-secure, random 32-byte value. This method must return a
+ /// different value each time it is called.
+ fn get_secure_random_bytes(&self) -> [u8; 32];
+}
+
+/// A trait that can handle cryptographic operations at the scope level of a node.
+pub trait NodeSigner {
+ /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data.
+ ///
+ /// If the implementor of this trait supports [phantom node payments], then every node that is
+ /// intended to be included in the phantom invoice route hints must return the same value from
+ /// this method.
+ // This is because LDK avoids storing inbound payment data by encrypting payment data in the
+ // payment hash and/or payment secret, therefore for a payment to be receivable by multiple
+ // nodes, they must share the key that encrypts this payment data.
+ ///
+ /// This method must return the same value each time it is called.
+ ///
+ /// [phantom node payments]: PhantomKeysManager
+ fn get_inbound_payment_key_material(&self) -> KeyMaterial;
+
+ /// Get node id based on the provided [`Recipient`].
+ ///
+ /// This method must return the same value each time it is called with a given [`Recipient`]
+ /// parameter.
+ ///
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
+ fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()>;
+
+ /// Gets the ECDH shared secret of our node secret and `other_key`, multiplying by `tweak` if
+ /// one is provided. Note that this tweak can be applied to `other_key` instead of our node
+ /// secret, though this is less efficient.
+ ///
+ /// Note that if this fails while attempting to forward an HTLC, LDK will panic. The error
+ /// should be resolved to allow LDK to resume forwarding HTLCs.
+ ///
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()>;
+
+ /// Sign an invoice.
+ ///
+ /// By parameterizing by the raw invoice bytes instead of the hash, we allow implementors of
+ /// this trait to parse the invoice and make sure they're signing what they expect, rather than
+ /// blindly signing the hash.
+ ///
+ /// The `hrp_bytes` are ASCII bytes, while the `invoice_data` is base32.
+ ///
+ /// The secret key used to sign the invoice is dependent on the [`Recipient`].
+ ///
+ /// Errors if the [`Recipient`] variant is not supported by the implementation.
+ fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()>;
+
+ /// Sign a gossip message.
+ ///
+ /// Note that if this fails, LDK may panic and the message will not be broadcast to the network
+ /// or a possible channel counterparty. If LDK panics, the error should be resolved to allow the
+ /// message to be broadcast, as otherwise it may prevent one from receiving funds over the
+ /// corresponding channel.
+ fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()>;
+}
+
+/// A trait that can return signer instances for individual channels.
+pub trait SignerProvider {
+ /// A type which implements [`WriteableEcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`].
+ type Signer : WriteableEcdsaChannelSigner;
+
+ /// Generates a unique `channel_keys_id` that can be used to obtain a [`Self::Signer`] through
+ /// [`SignerProvider::derive_channel_signer`]. The `user_channel_id` is provided to allow
+ /// implementations of [`SignerProvider`] to maintain a mapping between itself and the generated
+ /// `channel_keys_id`.
+ ///
+ /// This method must return a different value each time it is called.
+ fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32];
+
+ /// Derives the private key material backing a `Signer`.
+ ///
+ /// To derive a new `Signer`, a fresh `channel_keys_id` should be obtained through
+ /// [`SignerProvider::generate_channel_keys_id`]. Otherwise, an existing `Signer` can be
+ /// re-derived from its `channel_keys_id`, which can be obtained through its trait method
+ /// [`ChannelSigner::channel_keys_id`].
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer;
+
+ /// Reads a [`Signer`] for this [`SignerProvider`] from the given input stream.
+ /// This is only called during deserialization of other objects which contain
+ /// [`WriteableEcdsaChannelSigner`]-implementing objects (i.e., [`ChannelMonitor`]s and [`ChannelManager`]s).
+ /// The bytes are exactly those which `<Self::Signer as Writeable>::write()` writes, and
+ /// contain no versioning scheme. You may wish to include your own version prefix and ensure
+ /// you've read all of the provided bytes to ensure no corruption occurred.
+ ///
+ /// This method is slowly being phased out -- it will only be called when reading objects
+ /// written by LDK versions prior to 0.0.113.
+ ///
+ /// [`Signer`]: Self::Signer
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+ fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError>;
+
+ /// Get a script pubkey which we send funds to when claiming on-chain contestable outputs.
+ ///
+ /// If this function returns an error, this will result in a channel failing to open.
+ ///
+ /// This method should return a different value each time it is called, to avoid linking
+ /// on-chain funds across channels as controlled to the same user.
+ fn get_destination_script(&self) -> Result<Script, ()>;
+
+ /// Get a script pubkey which we will send funds to when closing a channel.
+ ///
+ /// If this function returns an error, this will result in a channel failing to open or close.
+ /// In the event of a failure when the counterparty is initiating a close, this can result in a
+ /// channel force close.
+ ///
+ /// This method should return a different value each time it is called, to avoid linking
+ /// on-chain funds across channels as controlled to the same user.
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()>;
+}
+
+/// A simple implementation of [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
+///
+/// This implementation performs no policy checks and is insufficient by itself as
+/// a secure external signer.
+pub struct InMemorySigner {
+ /// Holder secret key in the 2-of-2 multisig script of a channel. This key also backs the
+ /// holder's anchor output in a commitment transaction, if one is present.
+ pub funding_key: SecretKey,
+ /// Holder secret key for blinded revocation pubkey.
+ pub revocation_base_key: SecretKey,
+ /// Holder secret key used for our balance in counterparty-broadcasted commitment transactions.
+ pub payment_key: SecretKey,
+ /// Holder secret key used in an HTLC transaction.
+ pub delayed_payment_base_key: SecretKey,
+ /// Holder HTLC secret key used in commitment transaction HTLC outputs.
+ pub htlc_base_key: SecretKey,
+ /// Commitment seed.
+ pub commitment_seed: [u8; 32],
+ /// Holder public keys and basepoints.
+ pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
+ /// Counterparty public keys and counterparty/holder `selected_contest_delay`, populated on channel acceptance.
+ channel_parameters: Option<ChannelTransactionParameters>,
+ /// The total value of this channel.
+ channel_value_satoshis: u64,
+ /// Key derivation parameters.
+ channel_keys_id: [u8; 32],
+ /// Seed from which all randomness produced is derived from.
+ rand_bytes_unique_start: [u8; 32],
+ /// Tracks the number of times we've produced randomness to ensure we don't return the same
+ /// bytes twice.
+ rand_bytes_index: AtomicCounter,
+}
+
+impl Clone for InMemorySigner {
+ fn clone(&self) -> Self {
+ Self {
+ funding_key: self.funding_key.clone(),
+ revocation_base_key: self.revocation_base_key.clone(),
+ payment_key: self.payment_key.clone(),
+ delayed_payment_base_key: self.delayed_payment_base_key.clone(),
+ htlc_base_key: self.htlc_base_key.clone(),
+ commitment_seed: self.commitment_seed.clone(),
+ holder_channel_pubkeys: self.holder_channel_pubkeys.clone(),
+ channel_parameters: self.channel_parameters.clone(),
+ channel_value_satoshis: self.channel_value_satoshis,
+ channel_keys_id: self.channel_keys_id,
+ rand_bytes_unique_start: self.get_secure_random_bytes(),
+ rand_bytes_index: AtomicCounter::new(),
+ }
+ }
+}
+
+impl InMemorySigner {
+ /// Creates a new [`InMemorySigner`].
+ pub fn new<C: Signing>(
+ secp_ctx: &Secp256k1<C>,
+ funding_key: SecretKey,
+ revocation_base_key: SecretKey,
+ payment_key: SecretKey,
+ delayed_payment_base_key: SecretKey,
+ htlc_base_key: SecretKey,
+ commitment_seed: [u8; 32],
+ channel_value_satoshis: u64,
+ channel_keys_id: [u8; 32],
+ rand_bytes_unique_start: [u8; 32],
+ ) -> InMemorySigner {
+ let holder_channel_pubkeys =
+ InMemorySigner::make_holder_keys(secp_ctx, &funding_key, &revocation_base_key,
+ &payment_key, &delayed_payment_base_key,
+ &htlc_base_key);
+ InMemorySigner {
+ funding_key,
+ revocation_base_key,
+ payment_key,
+ delayed_payment_base_key,
+ htlc_base_key,
+ commitment_seed,
+ channel_value_satoshis,
+ holder_channel_pubkeys,
+ channel_parameters: None,
+ channel_keys_id,
+ rand_bytes_unique_start,
+ rand_bytes_index: AtomicCounter::new(),
+ }
+ }
+
+ fn make_holder_keys<C: Signing>(secp_ctx: &Secp256k1<C>,
+ funding_key: &SecretKey,
+ revocation_base_key: &SecretKey,
+ payment_key: &SecretKey,
+ delayed_payment_base_key: &SecretKey,
+ htlc_base_key: &SecretKey) -> ChannelPublicKeys {
+ let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s);
+ ChannelPublicKeys {
+ funding_pubkey: from_secret(&funding_key),
+ revocation_basepoint: from_secret(&revocation_base_key),
+ payment_point: from_secret(&payment_key),
+ delayed_payment_basepoint: from_secret(&delayed_payment_base_key),
+ htlc_basepoint: from_secret(&htlc_base_key),
+ }
+ }
+
+ /// Returns the counterparty's pubkeys.
+ ///
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
+ pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().pubkeys }
+ /// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable
+ /// transactions, i.e., the amount of time that we have to wait to recover our funds if we
+ /// broadcast a transaction.
+ ///
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
+ pub fn counterparty_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().selected_contest_delay }
+ /// Returns the `contest_delay` value specified by us and applied on transactions broadcastable
+ /// by our counterparty, i.e., the amount of time that they have to wait to recover their funds
+ /// if they broadcast a transaction.
+ ///
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
+ pub fn holder_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().holder_selected_contest_delay }
+ /// Returns whether the holder is the initiator.
+ ///
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
+ pub fn is_outbound(&self) -> bool { self.get_channel_parameters().is_outbound_from_holder }
+ /// Funding outpoint
+ ///
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
+ pub fn funding_outpoint(&self) -> &OutPoint { self.get_channel_parameters().funding_outpoint.as_ref().unwrap() }
+ /// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or
+ /// building transactions.
+ ///
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
+ pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
+ self.channel_parameters.as_ref().unwrap()
+ }
+ /// Returns whether anchors should be used.
+ ///
+ /// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
+ pub fn opt_anchors(&self) -> bool {
+ self.get_channel_parameters().opt_anchors.is_some()
+ }
+ /// Sign the single input of `spend_tx` at index `input_idx`, which spends the output described
+ /// by `descriptor`, returning the witness stack for the input.
+ ///
+ /// Returns an error if the input at `input_idx` does not exist, has a non-empty `script_sig`,
+ /// is not spending the outpoint described by [`descriptor.outpoint`],
+ /// or if an output descriptor `script_pubkey` does not match the one we can spend.
+ ///
+ /// [`descriptor.outpoint`]: StaticPaymentOutputDescriptor::outpoint
+ pub fn sign_counterparty_payment_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
+ // TODO: We really should be taking the SigHashCache as a parameter here instead of
+ // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
+ // so that we can check them. This requires upstream rust-bitcoin changes (as well as
+ // bindings updates to support SigHashCache objects).
+ if spend_tx.input.len() <= input_idx { return Err(()); }
+ if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
+ if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
+
+ let remotepubkey = self.pubkeys().payment_point;
+ let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Testnet).script_pubkey();
+ let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
+ let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
+ let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
+
+ if payment_script != descriptor.output.script_pubkey { return Err(()); }
+
+ let mut witness = Vec::with_capacity(2);
+ witness.push(remotesig.serialize_der().to_vec());
+ witness[0].push(EcdsaSighashType::All as u8);
+ witness.push(remotepubkey.serialize().to_vec());
+ Ok(witness)
+ }
+
+ /// Sign the single input of `spend_tx` at index `input_idx` which spends the output
+ /// described by `descriptor`, returning the witness stack for the input.
+ ///
+ /// Returns an error if the input at `input_idx` does not exist, has a non-empty `script_sig`,
+ /// is not spending the outpoint described by [`descriptor.outpoint`], does not have a
+ /// sequence set to [`descriptor.to_self_delay`], or if an output descriptor
+ /// `script_pubkey` does not match the one we can spend.
+ ///
+ /// [`descriptor.outpoint`]: DelayedPaymentOutputDescriptor::outpoint
+ /// [`descriptor.to_self_delay`]: DelayedPaymentOutputDescriptor::to_self_delay
+ pub fn sign_dynamic_p2wsh_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
+ // TODO: We really should be taking the SigHashCache as a parameter here instead of
+ // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
+ // so that we can check them. This requires upstream rust-bitcoin changes (as well as
+ // bindings updates to support SigHashCache objects).
+ if spend_tx.input.len() <= input_idx { return Err(()); }
+ if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
+ if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
+ if spend_tx.input[input_idx].sequence.0 != descriptor.to_self_delay as u32 { return Err(()); }
+
+ let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key);
+ let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
+ let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
+ let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
+ let local_delayedsig = sign_with_aux_rand(secp_ctx, &sighash, &delayed_payment_key, &self);
+ let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
+
+ if descriptor.output.script_pubkey != payment_script { return Err(()); }
+
+ let mut witness = Vec::with_capacity(3);
+ witness.push(local_delayedsig.serialize_der().to_vec());
+ witness[0].push(EcdsaSighashType::All as u8);
+ witness.push(vec!()); //MINIMALIF
+ witness.push(witness_script.clone().into_bytes());
+ Ok(witness)
+ }
+}
+
+impl EntropySource for InMemorySigner {
+ fn get_secure_random_bytes(&self) -> [u8; 32] {
+ let index = self.rand_bytes_index.get_increment();
+ let mut nonce = [0u8; 16];
+ nonce[..8].copy_from_slice(&index.to_be_bytes());
+ ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
+ }
+}
+
+impl ChannelSigner for InMemorySigner {
+ fn get_per_commitment_point(&self, idx: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicKey {
+ let commitment_secret = SecretKey::from_slice(&chan_utils::build_commitment_secret(&self.commitment_seed, idx)).unwrap();
+ PublicKey::from_secret_key(secp_ctx, &commitment_secret)
+ }
+
+ fn release_commitment_secret(&self, idx: u64) -> [u8; 32] {
+ chan_utils::build_commitment_secret(&self.commitment_seed, idx)
+ }
+
+ fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
+ Ok(())
+ }
+
+ fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
+
+ fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
+
+ fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) {
+ assert!(self.channel_parameters.is_none() || self.channel_parameters.as_ref().unwrap() == channel_parameters);
+ if self.channel_parameters.is_some() {
+ // The channel parameters were already set and they match, return early.
+ return;
+ }
+ assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated");
+ self.channel_parameters = Some(channel_parameters.clone());
+ }
+}
+
+impl EcdsaChannelSigner for InMemorySigner {
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
+ let trusted_tx = commitment_tx.trust();
+ let keys = trusted_tx.keys();
+
+ let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
+ let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
+
+ let built_tx = trusted_tx.built_transaction();
+ let commitment_sig = built_tx.sign_counterparty_commitment(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
+ let commitment_txid = built_tx.txid;
+
+ let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
+ for htlc in commitment_tx.htlcs() {
+ let channel_parameters = self.get_channel_parameters();
+ let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), self.holder_selected_contest_delay(), htlc, self.opt_anchors(), channel_parameters.opt_non_zero_fee_anchors.is_some(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
+ let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys);
+ let htlc_sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
+ let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]);
+ let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key);
+ htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key));
+ }
+
+ Ok((commitment_sig, htlc_sigs))
+ }
+
+ fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> {
+ Ok(())
+ }
+
+ fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
+ let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
+ let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
+ let trusted_tx = commitment_tx.trust();
+ let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
+ let channel_parameters = self.get_channel_parameters();
+ let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
+ Ok((sig, htlc_sigs))
+ }
+
+ #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
+ fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
+ let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
+ let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
+ let trusted_tx = commitment_tx.trust();
+ let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
+ let channel_parameters = self.get_channel_parameters();
+ let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
+ Ok((sig, htlc_sigs))
+ }
+
+ fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+ let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
+ let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
+ let witness_script = {
+ let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint);
+ chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey)
+ };
+ let mut sighash_parts = sighash::SighashCache::new(justice_tx);
+ let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
+ return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
+ }
+
+ fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+ let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key);
+ let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
+ let witness_script = {
+ let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
+ let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
+ chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
+ };
+ let mut sighash_parts = sighash::SighashCache::new(justice_tx);
+ let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
+ return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self))
+ }
+
+ #[cfg(anchors)]
+ fn sign_holder_htlc_transaction(
+ &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor,
+ secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()> {
+ let per_commitment_point = self.get_per_commitment_point(
+ htlc_descriptor.per_commitment_number, &secp_ctx
+ );
+ let witness_script = htlc_descriptor.witness_script(&per_commitment_point, secp_ctx);
+ let sighash = &sighash::SighashCache::new(&*htlc_tx).segwit_signature_hash(
+ input, &witness_script, htlc_descriptor.htlc.amount_msat / 1000, EcdsaSighashType::All
+ ).map_err(|_| ())?;
+ let our_htlc_private_key = chan_utils::derive_private_key(
+ &secp_ctx, &per_commitment_point, &self.htlc_base_key
+ );
+ Ok(sign_with_aux_rand(&secp_ctx, &hash_to_message!(sighash), &our_htlc_private_key, &self))
+ }
+
+ fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+ let htlc_key = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key);
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
+ let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
+ let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
+ let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
+ let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
+ let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
+ Ok(sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self))
+ }
+
+ fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+ let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
+ let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
+ Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
+ }
+
+ fn sign_holder_anchor_input(
+ &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
+ ) -> Result<Signature, ()> {
+ let witness_script = chan_utils::get_anchor_redeemscript(&self.holder_channel_pubkeys.funding_pubkey);
+ let sighash = sighash::SighashCache::new(&*anchor_tx).segwit_signature_hash(
+ input, &witness_script, ANCHOR_OUTPUT_VALUE_SATOSHI, EcdsaSighashType::All,
+ ).unwrap();
+ Ok(sign_with_aux_rand(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key, &self))
+ }
+
+ fn sign_channel_announcement_with_funding_key(
+ &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
+ ) -> Result<Signature, ()> {
+ let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
+ Ok(secp_ctx.sign_ecdsa(&msghash, &self.funding_key))
+ }
+}
+
+const SERIALIZATION_VERSION: u8 = 1;
+
+const MIN_SERIALIZATION_VERSION: u8 = 1;
+
+impl WriteableEcdsaChannelSigner for InMemorySigner {}
+
+impl Writeable for InMemorySigner {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
+ write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
+
+ self.funding_key.write(writer)?;
+ self.revocation_base_key.write(writer)?;
+ self.payment_key.write(writer)?;
+ self.delayed_payment_base_key.write(writer)?;
+ self.htlc_base_key.write(writer)?;
+ self.commitment_seed.write(writer)?;
+ self.channel_parameters.write(writer)?;
+ self.channel_value_satoshis.write(writer)?;
+ self.channel_keys_id.write(writer)?;
+
+ write_tlv_fields!(writer, {});
+
+ Ok(())
+ }
+}
+
+impl<ES: Deref> ReadableArgs<ES> for InMemorySigner where ES::Target: EntropySource {
+ fn read<R: io::Read>(reader: &mut R, entropy_source: ES) -> Result<Self, DecodeError> {
+ let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
+
+ let funding_key = Readable::read(reader)?;
+ let revocation_base_key = Readable::read(reader)?;
+ let payment_key = Readable::read(reader)?;
+ let delayed_payment_base_key = Readable::read(reader)?;
+ let htlc_base_key = Readable::read(reader)?;
+ let commitment_seed = Readable::read(reader)?;
+ let counterparty_channel_data = Readable::read(reader)?;
+ let channel_value_satoshis = Readable::read(reader)?;
+ let secp_ctx = Secp256k1::signing_only();
+ let holder_channel_pubkeys =
+ InMemorySigner::make_holder_keys(&secp_ctx, &funding_key, &revocation_base_key,
+ &payment_key, &delayed_payment_base_key, &htlc_base_key);
+ let keys_id = Readable::read(reader)?;
+
+ read_tlv_fields!(reader, {});
+
+ Ok(InMemorySigner {
+ funding_key,
+ revocation_base_key,
+ payment_key,
+ delayed_payment_base_key,
+ htlc_base_key,
+ commitment_seed,
+ channel_value_satoshis,
+ holder_channel_pubkeys,
+ channel_parameters: counterparty_channel_data,
+ channel_keys_id: keys_id,
+ rand_bytes_unique_start: entropy_source.get_secure_random_bytes(),
+ rand_bytes_index: AtomicCounter::new(),
+ })
+ }
+}
+
+/// Simple implementation of [`EntropySource`], [`NodeSigner`], and [`SignerProvider`] that takes a
+/// 32-byte seed for use as a BIP 32 extended key and derives keys from that.
+///
+/// Your `node_id` is seed/0'.
+/// Unilateral closes may use seed/1'.
+/// Cooperative closes may use seed/2'.
+/// The two close keys may be needed to claim on-chain funds!
+///
+/// This struct cannot be used for nodes that wish to support receiving phantom payments;
+/// [`PhantomKeysManager`] must be used instead.
+///
+/// Note that switching between this struct and [`PhantomKeysManager`] will invalidate any
+/// previously issued invoices and attempts to pay previous invoices will fail.
+pub struct KeysManager {
+ secp_ctx: Secp256k1<secp256k1::All>,
+ node_secret: SecretKey,
+ node_id: PublicKey,
+ inbound_payment_key: KeyMaterial,
+ destination_script: Script,
+ shutdown_pubkey: PublicKey,
+ channel_master_key: ExtendedPrivKey,
+ channel_child_index: AtomicUsize,
+
+ rand_bytes_unique_start: [u8; 32],
+ rand_bytes_index: AtomicCounter,
+
+ seed: [u8; 32],
+ starting_time_secs: u64,
+ starting_time_nanos: u32,
+}
+
+impl KeysManager {
+ /// Constructs a [`KeysManager`] from a 32-byte seed. If the seed is in some way biased (e.g.,
+ /// your CSRNG is busted) this may panic (but more importantly, you will possibly lose funds).
+ /// `starting_time` isn't strictly required to actually be a time, but it must absolutely,
+ /// without a doubt, be unique to this instance. ie if you start multiple times with the same
+ /// `seed`, `starting_time` must be unique to each run. Thus, the easiest way to achieve this
+ /// is to simply use the current time (with very high precision).
+ ///
+ /// The `seed` MUST be backed up safely prior to use so that the keys can be re-created, however,
+ /// obviously, `starting_time` should be unique every time you reload the library - it is only
+ /// used to generate new ephemeral key data (which will be stored by the individual channel if
+ /// necessary).
+ ///
+ /// Note that the seed is required to recover certain on-chain funds independent of
+ /// [`ChannelMonitor`] data, though a current copy of [`ChannelMonitor`] data is also required
+ /// for any channel, and some on-chain during-closing funds.
+ ///
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+ pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self {
+ let secp_ctx = Secp256k1::new();
+ // Note that when we aren't serializing the key, network doesn't matter
+ match ExtendedPrivKey::new_master(Network::Testnet, seed) {
+ Ok(master_key) => {
+ let node_secret = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0).unwrap()).expect("Your RNG is busted").private_key;
+ let node_id = PublicKey::from_secret_key(&secp_ctx, &node_secret);
+ let destination_script = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(1).unwrap()) {
+ Ok(destination_key) => {
+ let wpubkey_hash = WPubkeyHash::hash(&ExtendedPubKey::from_priv(&secp_ctx, &destination_key).to_pub().to_bytes());
+ Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0)
+ .push_slice(&wpubkey_hash.into_inner())
+ .into_script()
+ },
+ Err(_) => panic!("Your RNG is busted"),
+ };
+ let shutdown_pubkey = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(2).unwrap()) {
+ Ok(shutdown_key) => ExtendedPubKey::from_priv(&secp_ctx, &shutdown_key).public_key,
+ Err(_) => panic!("Your RNG is busted"),
+ };
+ let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap()).expect("Your RNG is busted");
+ let inbound_payment_key: SecretKey = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted").private_key;
+ let mut inbound_pmt_key_bytes = [0; 32];
+ inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]);
+
+ let mut rand_bytes_engine = Sha256::engine();
+ rand_bytes_engine.input(&starting_time_secs.to_be_bytes());
+ rand_bytes_engine.input(&starting_time_nanos.to_be_bytes());
+ rand_bytes_engine.input(seed);
+ rand_bytes_engine.input(b"LDK PRNG Seed");
+ let rand_bytes_unique_start = Sha256::from_engine(rand_bytes_engine).into_inner();
+
+ let mut res = KeysManager {
+ secp_ctx,
+ node_secret,
+ node_id,
+ inbound_payment_key: KeyMaterial(inbound_pmt_key_bytes),
+
+ destination_script,
+ shutdown_pubkey,
+
+ channel_master_key,
+ channel_child_index: AtomicUsize::new(0),
+
+ rand_bytes_unique_start,
+ rand_bytes_index: AtomicCounter::new(),
+
+ seed: *seed,
+ starting_time_secs,
+ starting_time_nanos,
+ };
+ let secp_seed = res.get_secure_random_bytes();
+ res.secp_ctx.seeded_randomize(&secp_seed);
+ res
+ },
+ Err(_) => panic!("Your rng is busted"),
+ }
+ }
+
+ /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
+ pub fn get_node_secret_key(&self) -> SecretKey {
+ self.node_secret
+ }
+
+ /// Derive an old [`WriteableEcdsaChannelSigner`] containing per-channel secrets based on a key derivation parameters.
+ pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
+ let chan_id = u64::from_be_bytes(params[0..8].try_into().unwrap());
+ let mut unique_start = Sha256::engine();
+ unique_start.input(params);
+ unique_start.input(&self.seed);
+
+ // We only seriously intend to rely on the channel_master_key for true secure
+ // entropy, everything else just ensures uniqueness. We rely on the unique_start (ie
+ // starting_time provided in the constructor) to be unique.
+ let child_privkey = self.channel_master_key.ckd_priv(&self.secp_ctx,
+ ChildNumber::from_hardened_idx((chan_id as u32) % (1 << 31)).expect("key space exhausted")
+ ).expect("Your RNG is busted");
+ unique_start.input(&child_privkey.private_key[..]);
+
+ let seed = Sha256::from_engine(unique_start).into_inner();
+
+ let commitment_seed = {
+ let mut sha = Sha256::engine();
+ sha.input(&seed);
+ sha.input(&b"commitment seed"[..]);
+ Sha256::from_engine(sha).into_inner()
+ };
+ macro_rules! key_step {
+ ($info: expr, $prev_key: expr) => {{
+ let mut sha = Sha256::engine();
+ sha.input(&seed);
+ sha.input(&$prev_key[..]);
+ sha.input(&$info[..]);
+ SecretKey::from_slice(&Sha256::from_engine(sha).into_inner()).expect("SHA-256 is busted")
+ }}
+ }
+ let funding_key = key_step!(b"funding key", commitment_seed);
+ let revocation_base_key = key_step!(b"revocation base key", funding_key);
+ let payment_key = key_step!(b"payment key", revocation_base_key);
+ let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key);
+ let htlc_base_key = key_step!(b"HTLC base key", delayed_payment_base_key);
+ let prng_seed = self.get_secure_random_bytes();
+
+ InMemorySigner::new(
+ &self.secp_ctx,
+ funding_key,
+ revocation_base_key,
+ payment_key,
+ delayed_payment_base_key,
+ htlc_base_key,
+ commitment_seed,
+ channel_value_satoshis,
+ params.clone(),
+ prng_seed,
+ )
+ }
+
+ /// Signs the given [`PartiallySignedTransaction`] which spends the given [`SpendableOutputDescriptor`]s.
+ /// The resulting inputs will be finalized and the PSBT will be ready for broadcast if there
+ /// are no other inputs that need signing.
+ ///
+ /// Returns `Err(())` if the PSBT is missing a descriptor or if we fail to sign.
+ ///
+ /// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
+ /// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
+ pub fn sign_spendable_outputs_psbt<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], psbt: &mut PartiallySignedTransaction, secp_ctx: &Secp256k1<C>) -> Result<(), ()> {
+ let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None;
+ for outp in descriptors {
+ match outp {
+ SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
+ let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == descriptor.outpoint.into_bitcoin_outpoint()).ok_or(())?;
+ if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
+ keys_cache = Some((
+ self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
+ descriptor.channel_keys_id));
+ }
+ let witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?);
+ psbt.inputs[input_idx].final_script_witness = Some(witness);
+ },
+ SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
+ let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == descriptor.outpoint.into_bitcoin_outpoint()).ok_or(())?;
+ if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
+ keys_cache = Some((
+ self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
+ descriptor.channel_keys_id));
+ }
+ let witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?);
+ psbt.inputs[input_idx].final_script_witness = Some(witness);
+ },
+ SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
+ let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == outpoint.into_bitcoin_outpoint()).ok_or(())?;
+ let derivation_idx = if output.script_pubkey == self.destination_script {
+ 1
+ } else {
+ 2
+ };
+ let secret = {
+ // Note that when we aren't serializing the key, network doesn't matter
+ match ExtendedPrivKey::new_master(Network::Testnet, &self.seed) {
+ Ok(master_key) => {
+ match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(derivation_idx).expect("key space exhausted")) {
+ Ok(key) => key,
+ Err(_) => panic!("Your RNG is busted"),
+ }
+ }
+ Err(_) => panic!("Your rng is busted"),
+ }
+ };
+ let pubkey = ExtendedPubKey::from_priv(&secp_ctx, &secret).to_pub();
+ if derivation_idx == 2 {
+ assert_eq!(pubkey.inner, self.shutdown_pubkey);
+ }
+ let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
+ let payment_script = bitcoin::Address::p2wpkh(&pubkey, Network::Testnet).expect("uncompressed key found").script_pubkey();
+
+ if payment_script != output.script_pubkey { return Err(()); };
+
+ let sighash = hash_to_message!(&sighash::SighashCache::new(&psbt.unsigned_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
+ let sig = sign_with_aux_rand(secp_ctx, &sighash, &secret.private_key, &self);
+ let mut sig_ser = sig.serialize_der().to_vec();
+ sig_ser.push(EcdsaSighashType::All as u8);
+ let witness = Witness::from_vec(vec![sig_ser, pubkey.inner.serialize().to_vec()]);
+ psbt.inputs[input_idx].final_script_witness = Some(witness);
+ },
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
+ /// output to the given change destination (if sufficient change value remains). The
+ /// transaction will have a feerate, at least, of the given value.
+ ///
+ /// The `locktime` argument is used to set the transaction's locktime. If `None`, the
+ /// transaction will have a locktime of 0. It it recommended to set this to the current block
+ /// height to avoid fee sniping, unless you have some specific reason to use a different
+ /// locktime.
+ ///
+ /// Returns `Err(())` if the output value is greater than the input value minus required fee,
+ /// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
+ /// does not match the one we can spend.
+ ///
+ /// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
+ ///
+ /// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
+ /// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
+ pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, locktime: Option<PackedLockTime>, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
+ let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, locktime)?;
+ self.sign_spendable_outputs_psbt(descriptors, &mut psbt, secp_ctx)?;
+
+ let spend_tx = psbt.extract_tx();
+
+ debug_assert!(expected_max_weight >= spend_tx.weight());
+ // Note that witnesses with a signature vary somewhat in size, so allow
+ // `expected_max_weight` to overshoot by up to 3 bytes per input.
+ debug_assert!(expected_max_weight <= spend_tx.weight() + descriptors.len() * 3);
+
+ Ok(spend_tx)
+ }
+}
+
+impl EntropySource for KeysManager {
+ fn get_secure_random_bytes(&self) -> [u8; 32] {
+ let index = self.rand_bytes_index.get_increment();
+ let mut nonce = [0u8; 16];
+ nonce[..8].copy_from_slice(&index.to_be_bytes());
+ ChaCha20::get_single_block(&self.rand_bytes_unique_start, &nonce)
+ }
+}
+
+impl NodeSigner for KeysManager {
+ fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
+ match recipient {
+ Recipient::Node => Ok(self.node_id.clone()),
+ Recipient::PhantomNode => Err(())
+ }
+ }
+
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
+ let mut node_secret = match recipient {
+ Recipient::Node => Ok(self.node_secret.clone()),
+ Recipient::PhantomNode => Err(())
+ }?;
+ if let Some(tweak) = tweak {
+ node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
+ }
+ Ok(SharedSecret::new(other_key, &node_secret))
+ }
+
+ fn get_inbound_payment_key_material(&self) -> KeyMaterial {
+ self.inbound_payment_key.clone()
+ }
+
+ fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+ let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
+ 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)), secret))
+ }
+
+ fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
+ let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
+ Ok(self.secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
+ }
+}
+
+impl SignerProvider for KeysManager {
+ type Signer = InMemorySigner;
+
+ fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
+ let child_idx = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
+ // `child_idx` is the only thing guaranteed to make each channel unique without a restart
+ // (though `user_channel_id` should help, depending on user behavior). If it manages to
+ // roll over, we may generate duplicate keys for two different channels, which could result
+ // in loss of funds. Because we only support 32-bit+ systems, assert that our `AtomicUsize`
+ // doesn't reach `u32::MAX`.
+ assert!(child_idx < core::u32::MAX as usize, "2^32 channels opened without restart");
+ let mut id = [0; 32];
+ id[0..4].copy_from_slice(&(child_idx as u32).to_be_bytes());
+ id[4..8].copy_from_slice(&self.starting_time_nanos.to_be_bytes());
+ id[8..16].copy_from_slice(&self.starting_time_secs.to_be_bytes());
+ id[16..32].copy_from_slice(&user_channel_id.to_be_bytes());
+ id
+ }
+
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
+ self.derive_channel_keys(channel_value_satoshis, &channel_keys_id)
+ }
+
+ fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
+ InMemorySigner::read(&mut io::Cursor::new(reader), self)
+ }
+
+ fn get_destination_script(&self) -> Result<Script, ()> {
+ Ok(self.destination_script.clone())
+ }
+
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
+ Ok(ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone()))
+ }
+}
+
+/// Similar to [`KeysManager`], but allows the node using this struct to receive phantom node
+/// payments.
+///
+/// A phantom node payment is a payment made to a phantom invoice, which is an invoice that can be
+/// paid to one of multiple nodes. This works because we encode the invoice route hints such that
+/// LDK will recognize an incoming payment as destined for a phantom node, and collect the payment
+/// itself without ever needing to forward to this fake node.
+///
+/// Phantom node payments are useful for load balancing between multiple LDK nodes. They also
+/// provide some fault tolerance, because payers will automatically retry paying other provided
+/// nodes in the case that one node goes down.
+///
+/// Note that multi-path payments are not supported in phantom invoices for security reasons.
+// In the hypothetical case that we did support MPP phantom payments, there would be no way for
+// nodes to know when the full payment has been received (and the preimage can be released) without
+// significantly compromising on our safety guarantees. I.e., if we expose the ability for the user
+// to tell LDK when the preimage can be released, we open ourselves to attacks where the preimage
+// is released too early.
+//
+/// Switching between this struct and [`KeysManager`] will invalidate any previously issued
+/// invoices and attempts to pay previous invoices will fail.
+pub struct PhantomKeysManager {
+ inner: KeysManager,
+ inbound_payment_key: KeyMaterial,
+ phantom_secret: SecretKey,
+ phantom_node_id: PublicKey,
+}
+
+impl EntropySource for PhantomKeysManager {
+ fn get_secure_random_bytes(&self) -> [u8; 32] {
+ self.inner.get_secure_random_bytes()
+ }
+}
+
+impl NodeSigner for PhantomKeysManager {
+ fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
+ match recipient {
+ Recipient::Node => self.inner.get_node_id(Recipient::Node),
+ Recipient::PhantomNode => Ok(self.phantom_node_id.clone()),
+ }
+ }
+
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
+ let mut node_secret = match recipient {
+ Recipient::Node => self.inner.node_secret.clone(),
+ Recipient::PhantomNode => self.phantom_secret.clone(),
+ };
+ if let Some(tweak) = tweak {
+ node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
+ }
+ Ok(SharedSecret::new(other_key, &node_secret))
+ }
+
+ fn get_inbound_payment_key_material(&self) -> KeyMaterial {
+ self.inbound_payment_key.clone()
+ }
+
+ fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+ let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
+ 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)), secret))
+ }
+
+ fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
+ self.inner.sign_gossip_message(msg)
+ }
+}
+
+impl SignerProvider for PhantomKeysManager {
+ type Signer = InMemorySigner;
+
+ fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
+ self.inner.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id)
+ }
+
+ fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer {
+ self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id)
+ }
+
+ fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
+ self.inner.read_chan_signer(reader)
+ }
+
+ fn get_destination_script(&self) -> Result<Script, ()> {
+ self.inner.get_destination_script()
+ }
+
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
+ self.inner.get_shutdown_scriptpubkey()
+ }
+}
+
+impl PhantomKeysManager {
+ /// Constructs a [`PhantomKeysManager`] given a 32-byte seed and an additional `cross_node_seed`
+ /// that is shared across all nodes that intend to participate in [phantom node payments]
+ /// together.
+ ///
+ /// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and
+ /// `starting_time_nanos`.
+ ///
+ /// `cross_node_seed` must be the same across all phantom payment-receiving nodes and also the
+ /// same across restarts, or else inbound payments may fail.
+ ///
+ /// [phantom node payments]: PhantomKeysManager
+ pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, cross_node_seed: &[u8; 32]) -> Self {
+ let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos);
+ let (inbound_key, phantom_key) = hkdf_extract_expand_twice(b"LDK Inbound and Phantom Payment Key Expansion", cross_node_seed);
+ let phantom_secret = SecretKey::from_slice(&phantom_key).unwrap();
+ let phantom_node_id = PublicKey::from_secret_key(&inner.secp_ctx, &phantom_secret);
+ Self {
+ inner,
+ inbound_payment_key: KeyMaterial(inbound_key),
+ phantom_secret,
+ phantom_node_id,
+ }
+ }
+
+ /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method.
+ pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, locktime: Option<PackedLockTime>, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
+ self.inner.spend_spendable_outputs(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, locktime, secp_ctx)
+ }
+
+ /// See [`KeysManager::derive_channel_keys`] for documentation on this method.
+ pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
+ self.inner.derive_channel_keys(channel_value_satoshis, params)
+ }
+
+ /// Gets the "node_id" secret key used to sign gossip announcements, decode onion data, etc.
+ pub fn get_node_secret_key(&self) -> SecretKey {
+ self.inner.get_node_secret_key()
+ }
+
+ /// Gets the "node_id" secret key of the phantom node used to sign invoices, decode the
+ /// last-hop onion data, etc.
+ pub fn get_phantom_node_secret_key(&self) -> SecretKey {
+ self.phantom_secret
+ }
+}
+
+// Ensure that EcdsaChannelSigner can have a vtable
+#[test]
+pub fn dyn_sign() {
+ let _signer: Box<dyn EcdsaChannelSigner>;
+}
+
+#[cfg(all(test, feature = "_bench_unstable", not(feature = "no-std")))]
+mod benches {
+ use std::sync::{Arc, mpsc};
+ use std::sync::mpsc::TryRecvError;
+ use std::thread;
+ use std::time::Duration;
+ use bitcoin::blockdata::constants::genesis_block;
+ use bitcoin::Network;
+ use crate::sign::{EntropySource, KeysManager};
+
+ use test::Bencher;
+
+ #[bench]
+ fn bench_get_secure_random_bytes(bench: &mut Bencher) {
+ let seed = [0u8; 32];
+ let now = Duration::from_secs(genesis_block(Network::Testnet).header.time as u64);
+ let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_micros()));
+
+ let mut handles = Vec::new();
+ let mut stops = Vec::new();
+ for _ in 1..5 {
+ let keys_manager_clone = Arc::clone(&keys_manager);
+ let (stop_sender, stop_receiver) = mpsc::channel();
+ let handle = thread::spawn(move || {
+ loop {
+ keys_manager_clone.get_secure_random_bytes();
+ match stop_receiver.try_recv() {
+ Ok(_) | Err(TryRecvError::Disconnected) => {
+ println!("Terminating.");
+ break;
+ }
+ Err(TryRecvError::Empty) => {}
+ }
+ }
+ });
+ handles.push(handle);
+ stops.push(stop_sender);
+ }
+
+ bench.iter(|| {
+ for _ in 1..100 {
+ keys_manager.get_secure_random_bytes();
+ }
+ });
+
+ for stop in stops {
+ let _ = stop.send(());
+ }
+ for handle in handles {
+ handle.join().unwrap();
+ }
+ }
+
+}
///
/// Default value: true.
///
- /// [`SignerProvider::get_shutdown_scriptpubkey`]: crate::chain::keysinterface::SignerProvider::get_shutdown_scriptpubkey
+ /// [`SignerProvider::get_shutdown_scriptpubkey`]: crate::sign::SignerProvider::get_shutdown_scriptpubkey
pub commit_upfront_shutdown_pubkey: bool,
/// The Proportion of the channel value to configure as counterparty's channel reserve,
/// i.e., `their_channel_reserve_satoshis` for both outbound and inbound channels.
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, ecdsa::Signature, Signing};
-use crate::chain::keysinterface::EntropySource;
+use crate::sign::EntropySource;
use core::ops::Deref;
use crate::ln::channel::{ANCHOR_OUTPUT_VALUE_SATOSHI, MIN_CHAN_DUST_LIMIT_SATOSHIS};
use crate::ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction};
use crate::ln::{chan_utils, msgs, PaymentPreimage};
-use crate::chain::keysinterface::{WriteableEcdsaChannelSigner, InMemorySigner, ChannelSigner, EcdsaChannelSigner};
+use crate::sign::{WriteableEcdsaChannelSigner, InMemorySigner, ChannelSigner, EcdsaChannelSigner};
use crate::prelude::*;
use core::cmp;
/// Using a SegWit v0 script should resolve this issue. If you cannot, you won't be able to open
/// a channel or cooperatively close one with this peer (and will have to force-close instead).
///
- /// [`SignerProvider::get_shutdown_scriptpubkey`]: crate::chain::keysinterface::SignerProvider::get_shutdown_scriptpubkey
+ /// [`SignerProvider::get_shutdown_scriptpubkey`]: crate::sign::SignerProvider::get_shutdown_scriptpubkey
/// [`InitFeatures`]: crate::ln::features::InitFeatures
IncompatibleShutdownScript {
/// The incompatible shutdown script.
// licenses.
use crate::chain::transaction::OutPoint;
-use crate::chain::keysinterface::SpendableOutputDescriptor;
+use crate::sign::SpendableOutputDescriptor;
use bitcoin::hash_types::Txid;
use bitcoin::blockdata::transaction::Transaction;
use crate::chain;
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use crate::chain::chainmonitor::{Persist, MonitorUpdateId};
-use crate::chain::keysinterface::{EntropySource, NodeSigner, WriteableEcdsaChannelSigner, SignerProvider};
+use crate::sign::{EntropySource, NodeSigner, WriteableEcdsaChannelSigner, SignerProvider};
use crate::chain::transaction::OutPoint;
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate};
use crate::ln::channelmanager::ChannelManager;
pub(crate) mod fake_scid {
use bitcoin::hash_types::BlockHash;
use bitcoin::hashes::hex::FromHex;
- use crate::chain::keysinterface::EntropySource;
+ use crate::sign::EntropySource;
use crate::util::chacha20::ChaCha20;
use crate::util::scid_utils;
use bitcoin::secp256k1::ecdsa;
use bitcoin::secp256k1::schnorr;
use bitcoin::blockdata::constants::ChainHash;
-use bitcoin::blockdata::script::Script;
+use bitcoin::blockdata::script::{self, Script};
use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
-use bitcoin::consensus;
+use bitcoin::{consensus, Witness};
use bitcoin::consensus::Encodable;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::{Txid, BlockHash};
impl_writeable_primitive!(u64, 8);
impl_writeable_primitive!(u32, 4);
impl_writeable_primitive!(u16, 2);
+impl_writeable_primitive!(i64, 8);
+impl_writeable_primitive!(i32, 4);
+impl_writeable_primitive!(i16, 2);
+impl_writeable_primitive!(i8, 1);
impl Writeable for u8 {
#[inline]
fn from(v: &'a Vec<T>) -> Self { Self(v) }
}
+impl Writeable for WithoutLength<&Script> {
+ #[inline]
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ writer.write_all(self.0.as_bytes())
+ }
+}
+
+impl Readable for WithoutLength<Script> {
+ #[inline]
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let v: WithoutLength<Vec<u8>> = Readable::read(r)?;
+ Ok(WithoutLength(script::Builder::from(v.0).into_script()))
+ }
+}
+
#[derive(Debug)]
pub(crate) struct Iterable<'a, I: Iterator<Item = &'a T> + Clone, T: 'a>(pub I);
impl_writeable_for_vec!(&crate::routing::router::BlindedTail);
impl_readable_for_vec!(crate::routing::router::BlindedTail);
+impl Writeable for Vec<Witness> {
+ #[inline]
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ (self.len() as u16).write(w)?;
+ for witness in self {
+ (witness.serialized_len() as u16).write(w)?;
+ witness.write(w)?;
+ }
+ Ok(())
+ }
+}
+
+impl Readable for Vec<Witness> {
+ #[inline]
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let num_witnesses = <u16 as Readable>::read(r)? as usize;
+ let mut witnesses = Vec::with_capacity(num_witnesses);
+ for _ in 0..num_witnesses {
+ // Even though the length of each witness can be inferred in its consensus-encoded form,
+ // the spec includes a length prefix so that implementations don't have to deserialize
+ // each initially. We do that here anyway as in general we'll need to be able to make
+ // assertions on some properties of the witnesses when receiving a message providing a list
+ // of witnesses. We'll just do a sanity check for the lengths and error if there is a mismatch.
+ let witness_len = <u16 as Readable>::read(r)? as usize;
+ let witness = <Witness as Readable>::read(r)?;
+ if witness.serialized_len() != witness_len {
+ return Err(DecodeError::BadLengthDescriptor);
+ }
+ witnesses.push(witness);
+ }
+ Ok(witnesses)
+ }
+}
+
impl Writeable for Script {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.len() as u16).write(w)?;
}
impl_consensus_ser!(Transaction);
impl_consensus_ser!(TxOut);
+impl_consensus_ser!(Witness);
impl<T: Readable> Readable for Mutex<T> {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
}
}
+/// A wrapper for a `Transaction` which can only be constructed with [`TransactionU16LenLimited::new`]
+/// if the `Transaction`'s consensus-serialized length is <= u16::MAX.
+///
+/// Use [`TransactionU16LenLimited::into_transaction`] to convert into the contained `Transaction`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TransactionU16LenLimited(Transaction);
+
+impl TransactionU16LenLimited {
+ /// Constructs a new `TransactionU16LenLimited` from a `Transaction` only if it's consensus-
+ /// serialized length is <= u16::MAX.
+ pub fn new(transaction: Transaction) -> Result<Self, ()> {
+ if transaction.serialized_length() > (u16::MAX as usize) {
+ Err(())
+ } else {
+ Ok(Self(transaction))
+ }
+ }
+
+ /// Consumes this `TransactionU16LenLimited` and returns its contained `Transaction`.
+ pub fn into_transaction(self) -> Transaction {
+ self.0
+ }
+}
+
+impl Writeable for TransactionU16LenLimited {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ (self.0.serialized_length() as u16).write(w)?;
+ self.0.write(w)
+ }
+}
+
+impl Readable for TransactionU16LenLimited {
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let len = <u16 as Readable>::read(r)?;
+ let mut tx_reader = FixedLengthReader::new(r, len as u64);
+ let tx: Transaction = Readable::read(&mut tx_reader)?;
+ if tx_reader.bytes_remain() {
+ Err(DecodeError::BadLengthDescriptor)
+ } else {
+ Ok(Self(tx))
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
impl $crate::util::ser::Writeable for $st {
fn write<W: $crate::util::ser::Writer>(&self, w: &mut W) -> Result<(), $crate::io::Error> {
$( self.$field.write(w)?; )*
- $crate::encode_tlv_stream!(w, {$(($type, self.$tlvfield, $fieldty)),*});
+ $crate::encode_tlv_stream!(w, {$(($type, self.$tlvfield.as_ref(), $fieldty)),*});
Ok(())
}
}
($field: ident, optional_vec) => {
let mut $field = Some(Vec::new());
};
+ ($field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {
+ $crate::_init_tlv_field_var!($field, option);
+ };
($field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {
$crate::_init_tlv_field_var!($field, option);
};
use crate::chain::channelmonitor;
use crate::chain::channelmonitor::MonitorEvent;
use crate::chain::transaction::OutPoint;
-use crate::chain::keysinterface;
+use crate::sign;
use crate::events;
use crate::ln::channelmanager;
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use core::mem;
use bitcoin::bech32::u5;
-use crate::chain::keysinterface::{InMemorySigner, Recipient, EntropySource, NodeSigner, SignerProvider};
+use crate::sign::{InMemorySigner, Recipient, EntropySource, NodeSigner, SignerProvider};
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};
// Since the path is reversed, the last element in our iteration is the first
// hop.
if idx == path.hops.len() - 1 {
- scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(payer), &NodeId::from_pubkey(&hop.pubkey), usage);
+ scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(payer), &NodeId::from_pubkey(&hop.pubkey), usage, &());
} else {
let curr_hop_path_idx = path.hops.len() - 1 - idx;
- scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(&path.hops[curr_hop_path_idx - 1].pubkey), &NodeId::from_pubkey(&hop.pubkey), usage);
+ scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(&path.hops[curr_hop_path_idx - 1].pubkey), &NodeId::from_pubkey(&hop.pubkey), usage, &());
}
}
}
let scorer = self.scorer.lock().unwrap();
find_route(
payer, params, &self.network_graph, first_hops, &logger,
- &ScorerAccountingForInFlightHtlcs::new(scorer, &inflight_htlcs),
+ &ScorerAccountingForInFlightHtlcs::new(scorer, &inflight_htlcs), &(),
&[42; 32]
)
}
))
}
- fn get_destination_script(&self) -> Script { unreachable!(); }
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!(); }
+ fn get_destination_script(&self) -> Result<Script, ()> { Err(()) }
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> { Err(()) }
}
pub struct TestChainMonitor<'a> {
self.update_rets.lock().unwrap().push_back(next_ret);
}
}
-impl<Signer: keysinterface::WriteableEcdsaChannelSigner> chainmonitor::Persist<Signer> for TestPersister {
+impl<Signer: sign::WriteableEcdsaChannelSigner> chainmonitor::Persist<Signer> for TestPersister {
fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<Signer>, _id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
if let Some(update_ret) = self.update_rets.lock().unwrap().pop_front() {
return update_ret
fn provided_init_features(&self, _their_init_features: &PublicKey) -> InitFeatures {
channelmanager::provided_init_features(&UserConfig::default())
}
+
+ fn handle_open_channel_v2(&self, _their_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
+ self.received_msg(wire::Message::OpenChannelV2(msg.clone()));
+ }
+
+ fn handle_accept_channel_v2(&self, _their_node_id: &PublicKey, msg: &msgs::AcceptChannelV2) {
+ self.received_msg(wire::Message::AcceptChannelV2(msg.clone()));
+ }
+
+ fn handle_tx_add_input(&self, _their_node_id: &PublicKey, msg: &msgs::TxAddInput) {
+ self.received_msg(wire::Message::TxAddInput(msg.clone()));
+ }
+
+ fn handle_tx_add_output(&self, _their_node_id: &PublicKey, msg: &msgs::TxAddOutput) {
+ self.received_msg(wire::Message::TxAddOutput(msg.clone()));
+ }
+
+ fn handle_tx_remove_input(&self, _their_node_id: &PublicKey, msg: &msgs::TxRemoveInput) {
+ self.received_msg(wire::Message::TxRemoveInput(msg.clone()));
+ }
+
+ fn handle_tx_remove_output(&self, _their_node_id: &PublicKey, msg: &msgs::TxRemoveOutput) {
+ self.received_msg(wire::Message::TxRemoveOutput(msg.clone()));
+ }
+
+ fn handle_tx_complete(&self, _their_node_id: &PublicKey, msg: &msgs::TxComplete) {
+ self.received_msg(wire::Message::TxComplete(msg.clone()));
+ }
+
+ fn handle_tx_signatures(&self, _their_node_id: &PublicKey, msg: &msgs::TxSignatures) {
+ self.received_msg(wire::Message::TxSignatures(msg.clone()));
+ }
+
+ fn handle_tx_init_rbf(&self, _their_node_id: &PublicKey, msg: &msgs::TxInitRbf) {
+ self.received_msg(wire::Message::TxInitRbf(msg.clone()));
+ }
+
+ fn handle_tx_ack_rbf(&self, _their_node_id: &PublicKey, msg: &msgs::TxAckRbf) {
+ self.received_msg(wire::Message::TxAckRbf(msg.clone()));
+ }
+
+ fn handle_tx_abort(&self, _their_node_id: &PublicKey, msg: &msgs::TxAbort) {
+ self.received_msg(wire::Message::TxAbort(msg.clone()));
+ }
}
impl events::MessageSendEventsProvider for TestChannelMessageHandler {
}
impl NodeSigner for TestNodeSigner {
- fn get_inbound_payment_key_material(&self) -> crate::chain::keysinterface::KeyMaterial {
+ fn get_inbound_payment_key_material(&self) -> crate::sign::KeyMaterial {
unreachable!()
}
}
pub struct TestKeysInterface {
- pub backing: keysinterface::PhantomKeysManager,
+ pub backing: sign::PhantomKeysManager,
pub override_random_bytes: Mutex<Option<[u8; 32]>>,
pub disable_revocation_policy_check: bool,
enforcement_states: Mutex<HashMap<[u8;32], Arc<Mutex<EnforcementState>>>>,
self.backing.ecdh(recipient, other_key, tweak)
}
- fn get_inbound_payment_key_material(&self) -> keysinterface::KeyMaterial {
+ fn get_inbound_payment_key_material(&self) -> sign::KeyMaterial {
self.backing.get_inbound_payment_key_material()
}
))
}
- fn get_destination_script(&self) -> Script { self.backing.get_destination_script() }
+ fn get_destination_script(&self) -> Result<Script, ()> { self.backing.get_destination_script() }
- fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
+ fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
match &mut *self.expectations.lock().unwrap() {
None => self.backing.get_shutdown_scriptpubkey(),
Some(expectations) => match expectations.pop_front() {
None => panic!("Unexpected get_shutdown_scriptpubkey"),
- Some(expectation) => expectation.returns,
+ Some(expectation) => Ok(expectation.returns),
},
}
}
pub fn new(seed: &[u8; 32], network: Network) -> Self {
let now = Duration::from_secs(genesis_block(network).header.time as u64);
Self {
- backing: keysinterface::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed),
+ backing: sign::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed),
override_random_bytes: Mutex::new(None),
disable_revocation_policy_check: false,
enforcement_states: Mutex::new(HashMap::new()),
}
}
- /// Sets an expectation that [`keysinterface::SignerProvider::get_shutdown_scriptpubkey`] is
+ /// Sets an expectation that [`sign::SignerProvider::get_shutdown_scriptpubkey`] is
/// called.
pub fn expect(&self, expectation: OnGetShutdownScriptpubkey) -> &Self {
self.expectations.lock().unwrap()
}
}
-/// An expectation that [`keysinterface::SignerProvider::get_shutdown_scriptpubkey`] was called and
+/// An expectation that [`sign::SignerProvider::get_shutdown_scriptpubkey`] was called and
/// returns a [`ShutdownScript`].
pub struct OnGetShutdownScriptpubkey {
/// A shutdown script used to close a channel.
}
impl Score for TestScorer {
+ type ScoreParams = ();
fn channel_penalty_msat(
- &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage
+ &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage, _score_params: &Self::ScoreParams
) -> u64 {
if let Some(scorer_expectations) = self.scorer_expectations.borrow_mut().as_mut() {
match scorer_expectations.pop_front() {
pub(crate) fn notify(&self) {
let mut lock = self.notify_pending.lock().unwrap();
if let Some(future_state) = &lock.1 {
- if future_state.lock().unwrap().complete() {
+ if complete_future(future_state) {
lock.1 = None;
return;
}
} else {
let state = Arc::new(Mutex::new(FutureState {
callbacks: Vec::new(),
+ callbacks_with_state: Vec::new(),
complete: lock.0,
callbacks_made: false,
}));
// first bool - set to false if we're just calling a Waker, and true if we're calling an actual
// user-provided function.
callbacks: Vec<(bool, Box<dyn FutureCallback>)>,
+ callbacks_with_state: Vec<(bool, Box<dyn Fn(&Arc<Mutex<FutureState>>) -> () + Send>)>,
complete: bool,
callbacks_made: bool,
}
-impl FutureState {
- fn complete(&mut self) -> bool {
- for (counts_as_call, callback) in self.callbacks.drain(..) {
- callback.call();
- self.callbacks_made |= counts_as_call;
- }
- self.complete = true;
- self.callbacks_made
+fn complete_future(this: &Arc<Mutex<FutureState>>) -> bool {
+ let mut state_lock = this.lock().unwrap();
+ let state = &mut *state_lock;
+ for (counts_as_call, callback) in state.callbacks.drain(..) {
+ callback.call();
+ state.callbacks_made |= counts_as_call;
+ }
+ for (counts_as_call, callback) in state.callbacks_with_state.drain(..) {
+ (callback)(this);
+ state.callbacks_made |= counts_as_call;
}
+ state.complete = true;
+ state.callbacks_made
}
/// A simple future which can complete once, and calls some callback(s) when it does so.
for notifier_mtx in self.notifiers.iter() {
let cv_ref = Arc::clone(&cv);
let notified_fut_ref = Arc::clone(¬ified_fut_mtx);
- let notifier_ref = Arc::clone(¬ifier_mtx);
let mut notifier = notifier_mtx.lock().unwrap();
if notifier.complete {
- *notified_fut_mtx.lock().unwrap() = Some(notifier_ref);
+ *notified_fut_mtx.lock().unwrap() = Some(Arc::clone(¬ifier_mtx));
break;
}
- notifier.callbacks.push((false, Box::new(move || {
- *notified_fut_ref.lock().unwrap() = Some(Arc::clone(¬ifier_ref));
+ notifier.callbacks_with_state.push((false, Box::new(move |notifier_ref| {
+ *notified_fut_ref.lock().unwrap() = Some(Arc::clone(notifier_ref));
cv_ref.notify_all();
})));
}
}
}
+ #[cfg(feature = "std")]
+ #[test]
+ fn test_state_drops() {
+ // Previously, there was a leak if a `Notifier` was `drop`ed without ever being notified
+ // but after having been slept-on. This tests for that leak.
+ use crate::sync::Arc;
+ use std::thread;
+
+ let notifier_a = Arc::new(Notifier::new());
+ let notifier_b = Arc::new(Notifier::new());
+
+ let thread_notifier_a = Arc::clone(¬ifier_a);
+
+ let future_a = notifier_a.get_future();
+ let future_state_a = Arc::downgrade(&future_a.state);
+
+ let future_b = notifier_b.get_future();
+ let future_state_b = Arc::downgrade(&future_b.state);
+
+ let join_handle = thread::spawn(move || {
+ // Let the other thread get to the wait point, then notify it.
+ std::thread::sleep(Duration::from_millis(50));
+ thread_notifier_a.notify();
+ });
+
+ // Wait on the other thread to finish its sleep, note that the leak only happened if we
+ // actually have to sleep here, not if we immediately return.
+ Sleeper::from_two_futures(future_a, future_b).wait();
+
+ join_handle.join().unwrap();
+
+ // then drop the notifiers and make sure the future states are gone.
+ mem::drop(notifier_a);
+ mem::drop(notifier_b);
+
+ assert!(future_state_a.upgrade().is_none() && future_state_b.upgrade().is_none());
+ }
+
#[test]
fn test_future_callbacks() {
let future = Future {
state: Arc::new(Mutex::new(FutureState {
callbacks: Vec::new(),
+ callbacks_with_state: Vec::new(),
complete: false,
callbacks_made: false,
}))
future.register_callback(Box::new(move || assert!(!callback_ref.fetch_or(true, Ordering::SeqCst))));
assert!(!callback.load(Ordering::SeqCst));
- future.state.lock().unwrap().complete();
+ complete_future(&future.state);
assert!(callback.load(Ordering::SeqCst));
- future.state.lock().unwrap().complete();
+ complete_future(&future.state);
}
#[test]
let future = Future {
state: Arc::new(Mutex::new(FutureState {
callbacks: Vec::new(),
+ callbacks_with_state: Vec::new(),
complete: false,
callbacks_made: false,
}))
};
- future.state.lock().unwrap().complete();
+ complete_future(&future.state);
let callback = Arc::new(AtomicBool::new(false));
let callback_ref = Arc::clone(&callback);
let mut future = Future {
state: Arc::new(Mutex::new(FutureState {
callbacks: Vec::new(),
+ callbacks_with_state: Vec::new(),
complete: false,
callbacks_made: false,
}))
assert_eq!(Pin::new(&mut second_future).poll(&mut Context::from_waker(&second_waker)), Poll::Pending);
assert!(!second_woken.load(Ordering::SeqCst));
- future.state.lock().unwrap().complete();
+ complete_future(&future.state);
assert!(woken.load(Ordering::SeqCst));
assert!(second_woken.load(Ordering::SeqCst));
assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Ready(()));
+++ /dev/null
-## Backwards Compatibility
-
-- Providing `ChannelMonitorUpdate`s generated by LDK 0.0.115 to a
-`ChannelMonitor` on 0.0.114 or before may panic.
+++ /dev/null
-## API Updates
-
-- `Event::PaymentPathFailed::retry` will always be `None` if we initiate a payment on 0.0.115
- then downgrade to an earlier version (#2063)
+++ /dev/null
-## Backwards Compatibility
-
-* Routes manually constructed with `Path::blinded_tail` present will not be readable by prior versions of LDK
-* `PaymentParameters` manually constructed with `Hints::Blinded` will not be readable by prior versions of LDK
--- /dev/null
+## Bug Fixes
+
+* Fixed sending large onion messages, which previously would result in an HMAC error on the second
+ hop (#2277).
--- /dev/null
+## Backwards Compatibility
+
+* `PaymentParameters` written with blinded path info using 0.0.115 will not be readable in 0.0.116
+++ /dev/null
-## Backwards Compatibility
- * Payments sent with the legacy `*_with_route` methods on LDK 0.0.115+ will no
- longer be retryable via the LDK 0.0.114- `retry_payment` method (#XXXX).